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: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/n/tip-mkj72lxagw1z8fvjm648iznw@git.kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
cac6653529
commit
9886167d20
|
@ -294,9 +294,31 @@ struct ring_buffer;
|
|||
*/
|
||||
struct perf_event {
|
||||
#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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
int nr_siblings;
|
||||
int group_flags;
|
||||
|
|
|
@ -7234,15 +7234,15 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu)
|
|||
perf_remove_from_context(event);
|
||||
unaccount_event_cpu(event, src_cpu);
|
||||
put_ctx(src_ctx);
|
||||
list_add(&event->event_entry, &events);
|
||||
list_add(&event->migrate_entry, &events);
|
||||
}
|
||||
mutex_unlock(&src_ctx->mutex);
|
||||
|
||||
synchronize_rcu();
|
||||
|
||||
mutex_lock(&dst_ctx->mutex);
|
||||
list_for_each_entry_safe(event, tmp, &events, event_entry) {
|
||||
list_del(&event->event_entry);
|
||||
list_for_each_entry_safe(event, tmp, &events, migrate_entry) {
|
||||
list_del(&event->migrate_entry);
|
||||
if (event->state >= PERF_EVENT_STATE_OFF)
|
||||
event->state = PERF_EVENT_STATE_INACTIVE;
|
||||
account_event_cpu(event, dst_cpu);
|
||||
|
|
Loading…
Reference in New Issue