Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9886167d authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar
Browse files

perf: Fix perf_pmu_migrate_context



While auditing the list_entry usage due to a trinity bug I found that
perf_pmu_migrate_context violates the rules for
perf_event::event_entry.

The problem is that perf_event::event_entry is a RCU list element, and
hence we must wait for a full RCU grace period before re-using the
element after deletion.

Therefore the usage in perf_pmu_migrate_context() which re-uses the
entry immediately is broken. For now introduce another list_head into
perf_event for this specific usage.

This doesn't actually fix the trinity report because that never goes
through this code.

Signed-off-by: default avatarPeter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/n/tip-mkj72lxagw1z8fvjm648iznw@git.kernel.org


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent cac66535
Loading
Loading
Loading
Loading
+23 −1
Original line number Original line Diff line number Diff line
@@ -294,9 +294,31 @@ struct ring_buffer;
 */
 */
struct perf_event {
struct perf_event {
#ifdef CONFIG_PERF_EVENTS
#ifdef CONFIG_PERF_EVENTS
	struct list_head		group_entry;
	/*
	 * entry onto perf_event_context::event_list;
	 *   modifications require ctx->lock
	 *   RCU safe iterations.
	 */
	struct list_head		event_entry;
	struct list_head		event_entry;

	/*
	 * XXX: group_entry and sibling_list should be mutually exclusive;
	 * either you're a sibling on a group, or you're the group leader.
	 * Rework the code to always use the same list element.
	 *
	 * Locked for modification by both ctx->mutex and ctx->lock; holding
	 * either sufficies for read.
	 */
	struct list_head		group_entry;
	struct list_head		sibling_list;
	struct list_head		sibling_list;

	/*
	 * We need storage to track the entries in perf_pmu_migrate_context; we
	 * cannot use the event_entry because of RCU and we want to keep the
	 * group in tact which avoids us using the other two entries.
	 */
	struct list_head		migrate_entry;

	struct hlist_node		hlist_entry;
	struct hlist_node		hlist_entry;
	int				nr_siblings;
	int				nr_siblings;
	int				group_flags;
	int				group_flags;
+3 −3
Original line number Original line Diff line number Diff line
@@ -7234,15 +7234,15 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu)
		perf_remove_from_context(event);
		perf_remove_from_context(event);
		unaccount_event_cpu(event, src_cpu);
		unaccount_event_cpu(event, src_cpu);
		put_ctx(src_ctx);
		put_ctx(src_ctx);
		list_add(&event->event_entry, &events);
		list_add(&event->migrate_entry, &events);
	}
	}
	mutex_unlock(&src_ctx->mutex);
	mutex_unlock(&src_ctx->mutex);


	synchronize_rcu();
	synchronize_rcu();


	mutex_lock(&dst_ctx->mutex);
	mutex_lock(&dst_ctx->mutex);
	list_for_each_entry_safe(event, tmp, &events, event_entry) {
	list_for_each_entry_safe(event, tmp, &events, migrate_entry) {
		list_del(&event->event_entry);
		list_del(&event->migrate_entry);
		if (event->state >= PERF_EVENT_STATE_OFF)
		if (event->state >= PERF_EVENT_STATE_OFF)
			event->state = PERF_EVENT_STATE_INACTIVE;
			event->state = PERF_EVENT_STATE_INACTIVE;
		account_event_cpu(event, dst_cpu);
		account_event_cpu(event, dst_cpu);