perf/core: Fix cpuctx refcounting
Audit of the refcounting turned up that perf_pmu_migrate_context()
fails to migrate the ctx refcount.
Fixes: bd27568117
("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>
This commit is contained in:
parent
b85ea95d08
commit
889c58b315
@ -843,11 +843,11 @@ struct perf_event {
|
||||
};
|
||||
|
||||
/*
|
||||
* ,-----------------------[1:n]----------------------.
|
||||
* V V
|
||||
* perf_event_context <-[1:n]-> perf_event_pmu_context <--- perf_event
|
||||
* ^ ^ | |
|
||||
* `--------[1:n]---------' `-[n:1]-> pmu <-[1:n]-'
|
||||
* ,-----------------------[1:n]------------------------.
|
||||
* V V
|
||||
* perf_event_context <-[1:n]-> perf_event_pmu_context <-[1:n]- perf_event
|
||||
* | |
|
||||
* `--[n:1]-> pmu <-[1:n]--'
|
||||
*
|
||||
*
|
||||
* struct perf_event_pmu_context lifetime is refcount based and RCU freed
|
||||
@ -865,6 +865,9 @@ struct perf_event {
|
||||
* ctx->mutex pinning the configuration. Since we hold a reference on
|
||||
* group_leader (through the filedesc) it can't go away, therefore it's
|
||||
* associated pmu_ctx must exist and cannot change due to ctx->mutex.
|
||||
*
|
||||
* perf_event holds a refcount on perf_event_context
|
||||
* perf_event holds a refcount on perf_event_pmu_context
|
||||
*/
|
||||
struct perf_event_pmu_context {
|
||||
struct pmu *pmu;
|
||||
|
@ -4828,6 +4828,11 @@ find_get_pmu_context(struct pmu *pmu, struct perf_event_context *ctx,
|
||||
void *task_ctx_data = NULL;
|
||||
|
||||
if (!ctx->task) {
|
||||
/*
|
||||
* perf_pmu_migrate_context() / __perf_pmu_install_event()
|
||||
* relies on the fact that find_get_pmu_context() cannot fail
|
||||
* for CPU contexts.
|
||||
*/
|
||||
struct perf_cpu_pmu_context *cpc;
|
||||
|
||||
cpc = per_cpu_ptr(pmu->cpu_pmu_context, event->cpu);
|
||||
@ -12889,6 +12894,9 @@ static void __perf_pmu_install_event(struct pmu *pmu,
|
||||
int cpu, struct perf_event *event)
|
||||
{
|
||||
struct perf_event_pmu_context *epc;
|
||||
struct perf_event_context *old_ctx = event->ctx;
|
||||
|
||||
get_ctx(ctx); /* normally find_get_context() */
|
||||
|
||||
event->cpu = cpu;
|
||||
epc = find_get_pmu_context(pmu, ctx, event);
|
||||
@ -12897,6 +12905,11 @@ static void __perf_pmu_install_event(struct pmu *pmu,
|
||||
if (event->state >= PERF_EVENT_STATE_OFF)
|
||||
event->state = PERF_EVENT_STATE_INACTIVE;
|
||||
perf_install_in_context(ctx, event, cpu);
|
||||
|
||||
/*
|
||||
* Now that event->ctx is updated and visible, put the old ctx.
|
||||
*/
|
||||
put_ctx(old_ctx);
|
||||
}
|
||||
|
||||
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)
|
||||
struct perf_event_context *src_ctx, *dst_ctx;
|
||||
LIST_HEAD(events);
|
||||
|
||||
/*
|
||||
* Since per-cpu context is persistent, no need to grab an extra
|
||||
* reference.
|
||||
*/
|
||||
src_ctx = &per_cpu_ptr(&perf_cpu_context, src_cpu)->ctx;
|
||||
dst_ctx = &per_cpu_ptr(&perf_cpu_context, dst_cpu)->ctx;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user