Skip to content

Commit

Permalink
perf: Fix perf_pmu_migrate_context
Browse files Browse the repository at this point in the history
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 <[email protected]>
Link: http://lkml.kernel.org/n/[email protected]
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
Peter Zijlstra authored and Ingo Molnar committed Oct 4, 2013
1 parent cac6653 commit 9886167
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 4 deletions.
24 changes: 23 additions & 1 deletion include/linux/perf_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions kernel/events/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 9886167

Please sign in to comment.