Skip to content

Commit 889c58b

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
perf/core: Fix cpuctx refcounting
Audit of the refcounting turned up that perf_pmu_migrate_context() fails to migrate the ctx refcount. Fixes: bd27568 ("perf: Rewrite core context handling") Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Link: https://lkml.kernel.org/r/20230612093539.085862001@infradead.org Cc: <stable@vger.kernel.org>
1 parent b85ea95 commit 889c58b

File tree

2 files changed

+25
-5
lines changed

2 files changed

+25
-5
lines changed

include/linux/perf_event.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -843,11 +843,11 @@ struct perf_event {
843843
};
844844

845845
/*
846-
* ,-----------------------[1:n]----------------------.
847-
* V V
848-
* perf_event_context <-[1:n]-> perf_event_pmu_context <--- perf_event
849-
* ^ ^ | |
850-
* `--------[1:n]---------' `-[n:1]-> pmu <-[1:n]-'
846+
* ,-----------------------[1:n]------------------------.
847+
* V V
848+
* perf_event_context <-[1:n]-> perf_event_pmu_context <-[1:n]- perf_event
849+
* | |
850+
* `--[n:1]-> pmu <-[1:n]--'
851851
*
852852
*
853853
* struct perf_event_pmu_context lifetime is refcount based and RCU freed
@@ -865,6 +865,9 @@ struct perf_event {
865865
* ctx->mutex pinning the configuration. Since we hold a reference on
866866
* group_leader (through the filedesc) it can't go away, therefore it's
867867
* associated pmu_ctx must exist and cannot change due to ctx->mutex.
868+
*
869+
* perf_event holds a refcount on perf_event_context
870+
* perf_event holds a refcount on perf_event_pmu_context
868871
*/
869872
struct perf_event_pmu_context {
870873
struct pmu *pmu;

kernel/events/core.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4828,6 +4828,11 @@ find_get_pmu_context(struct pmu *pmu, struct perf_event_context *ctx,
48284828
void *task_ctx_data = NULL;
48294829

48304830
if (!ctx->task) {
4831+
/*
4832+
* perf_pmu_migrate_context() / __perf_pmu_install_event()
4833+
* relies on the fact that find_get_pmu_context() cannot fail
4834+
* for CPU contexts.
4835+
*/
48314836
struct perf_cpu_pmu_context *cpc;
48324837

48334838
cpc = per_cpu_ptr(pmu->cpu_pmu_context, event->cpu);
@@ -12889,6 +12894,9 @@ static void __perf_pmu_install_event(struct pmu *pmu,
1288912894
int cpu, struct perf_event *event)
1289012895
{
1289112896
struct perf_event_pmu_context *epc;
12897+
struct perf_event_context *old_ctx = event->ctx;
12898+
12899+
get_ctx(ctx); /* normally find_get_context() */
1289212900

1289312901
event->cpu = cpu;
1289412902
epc = find_get_pmu_context(pmu, ctx, event);
@@ -12897,6 +12905,11 @@ static void __perf_pmu_install_event(struct pmu *pmu,
1289712905
if (event->state >= PERF_EVENT_STATE_OFF)
1289812906
event->state = PERF_EVENT_STATE_INACTIVE;
1289912907
perf_install_in_context(ctx, event, cpu);
12908+
12909+
/*
12910+
* Now that event->ctx is updated and visible, put the old ctx.
12911+
*/
12912+
put_ctx(old_ctx);
1290012913
}
1290112914

1290212915
static void __perf_pmu_install(struct perf_event_context *ctx,
@@ -12935,6 +12948,10 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu)
1293512948
struct perf_event_context *src_ctx, *dst_ctx;
1293612949
LIST_HEAD(events);
1293712950

12951+
/*
12952+
* Since per-cpu context is persistent, no need to grab an extra
12953+
* reference.
12954+
*/
1293812955
src_ctx = &per_cpu_ptr(&perf_cpu_context, src_cpu)->ctx;
1293912956
dst_ctx = &per_cpu_ptr(&perf_cpu_context, dst_cpu)->ctx;
1294012957

0 commit comments

Comments
 (0)