mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-28 05:12:49 +00:00
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
2 changed files with 25 additions and 5 deletions
|
@ -843,11 +843,11 @@ struct perf_event {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ,-----------------------[1:n]----------------------.
|
* ,-----------------------[1:n]------------------------.
|
||||||
* V V
|
* V V
|
||||||
* perf_event_context <-[1:n]-> perf_event_pmu_context <--- perf_event
|
* perf_event_context <-[1:n]-> perf_event_pmu_context <-[1:n]- perf_event
|
||||||
* ^ ^ | |
|
* | |
|
||||||
* `--------[1:n]---------' `-[n:1]-> pmu <-[1:n]-'
|
* `--[n:1]-> pmu <-[1:n]--'
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* struct perf_event_pmu_context lifetime is refcount based and RCU freed
|
* 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
|
* ctx->mutex pinning the configuration. Since we hold a reference on
|
||||||
* group_leader (through the filedesc) it can't go away, therefore it's
|
* 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.
|
* 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 perf_event_pmu_context {
|
||||||
struct pmu *pmu;
|
struct pmu *pmu;
|
||||||
|
|
|
@ -4828,6 +4828,11 @@ find_get_pmu_context(struct pmu *pmu, struct perf_event_context *ctx,
|
||||||
void *task_ctx_data = NULL;
|
void *task_ctx_data = NULL;
|
||||||
|
|
||||||
if (!ctx->task) {
|
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;
|
struct perf_cpu_pmu_context *cpc;
|
||||||
|
|
||||||
cpc = per_cpu_ptr(pmu->cpu_pmu_context, event->cpu);
|
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)
|
int cpu, struct perf_event *event)
|
||||||
{
|
{
|
||||||
struct perf_event_pmu_context *epc;
|
struct perf_event_pmu_context *epc;
|
||||||
|
struct perf_event_context *old_ctx = event->ctx;
|
||||||
|
|
||||||
|
get_ctx(ctx); /* normally find_get_context() */
|
||||||
|
|
||||||
event->cpu = cpu;
|
event->cpu = cpu;
|
||||||
epc = find_get_pmu_context(pmu, ctx, event);
|
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)
|
if (event->state >= PERF_EVENT_STATE_OFF)
|
||||||
event->state = PERF_EVENT_STATE_INACTIVE;
|
event->state = PERF_EVENT_STATE_INACTIVE;
|
||||||
perf_install_in_context(ctx, event, cpu);
|
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,
|
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;
|
struct perf_event_context *src_ctx, *dst_ctx;
|
||||||
LIST_HEAD(events);
|
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;
|
src_ctx = &per_cpu_ptr(&perf_cpu_context, src_cpu)->ctx;
|
||||||
dst_ctx = &per_cpu_ptr(&perf_cpu_context, dst_cpu)->ctx;
|
dst_ctx = &per_cpu_ptr(&perf_cpu_context, dst_cpu)->ctx;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue