From 1f8271add4e680f93584c283a1adbd57d8e5d223 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 7 May 2025 06:39:57 +0000 Subject: [PATCH] Revert "perf/core: Simplify the perf_event_alloc() error path" This reverts commit 7ef5aa081f989ecfecc1df02068a80aebbd3ec31 which is commit c70ca298036c58a88686ff388d3d367e9d21acf0 upstream. It breaks the Android kernel abi and can be brought back in the future in an abi-safe way if it is really needed. Bug: 161946584 Change-Id: I85d3cc6be7e72829ccaf6e813a68995e1477f332 Signed-off-by: Greg Kroah-Hartman --- include/linux/perf_event.h | 16 ++--- kernel/events/core.c | 140 ++++++++++++++++++------------------- 2 files changed, 77 insertions(+), 79 deletions(-) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index c7a28d1c7bc7..13a426cc54db 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -673,15 +673,13 @@ struct swevent_hlist { struct rcu_head rcu_head; }; -#define PERF_ATTACH_CONTEXT 0x0001 -#define PERF_ATTACH_GROUP 0x0002 -#define PERF_ATTACH_TASK 0x0004 -#define PERF_ATTACH_TASK_DATA 0x0008 -#define PERF_ATTACH_ITRACE 0x0010 -#define PERF_ATTACH_SCHED_CB 0x0020 -#define PERF_ATTACH_CHILD 0x0040 -#define PERF_ATTACH_EXCLUSIVE 0x0080 -#define PERF_ATTACH_CALLCHAIN 0x0100 +#define PERF_ATTACH_CONTEXT 0x01 +#define PERF_ATTACH_GROUP 0x02 +#define PERF_ATTACH_TASK 0x04 +#define PERF_ATTACH_TASK_DATA 0x08 +#define PERF_ATTACH_ITRACE 0x10 +#define PERF_ATTACH_SCHED_CB 0x20 +#define PERF_ATTACH_CHILD 0x40 struct bpf_prog; struct perf_cgroup; diff --git a/kernel/events/core.c b/kernel/events/core.c index b6ffd995a51e..d5f0a18a7066 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5263,8 +5263,6 @@ static int exclusive_event_init(struct perf_event *event) return -EBUSY; } - event->attach_state |= PERF_ATTACH_EXCLUSIVE; - return 0; } @@ -5272,13 +5270,14 @@ static void exclusive_event_destroy(struct perf_event *event) { struct pmu *pmu = event->pmu; + if (!is_exclusive_pmu(pmu)) + return; + /* see comment in exclusive_event_init() */ if (event->attach_state & PERF_ATTACH_TASK) atomic_dec(&pmu->exclusive_cnt); else atomic_inc(&pmu->exclusive_cnt); - - event->attach_state &= ~PERF_ATTACH_EXCLUSIVE; } static bool exclusive_event_match(struct perf_event *e1, struct perf_event *e2) @@ -5337,54 +5336,6 @@ static void perf_pending_task_sync(struct perf_event *event) rcuwait_wait_event(&event->pending_work_wait, !event->pending_work, TASK_UNINTERRUPTIBLE); } -/* vs perf_event_alloc() error */ -static void __free_event(struct perf_event *event) -{ - if (event->attach_state & PERF_ATTACH_CALLCHAIN) - put_callchain_buffers(); - - kfree(event->addr_filter_ranges); - - if (event->attach_state & PERF_ATTACH_EXCLUSIVE) - exclusive_event_destroy(event); - - if (is_cgroup_event(event)) - perf_detach_cgroup(event); - - if (event->destroy) - event->destroy(event); - - /* - * Must be after ->destroy(), due to uprobe_perf_close() using - * hw.target. - */ - if (event->hw.target) - put_task_struct(event->hw.target); - - if (event->pmu_ctx) { - /* - * put_pmu_ctx() needs an event->ctx reference, because of - * epc->ctx. - */ - WARN_ON_ONCE(!event->ctx); - WARN_ON_ONCE(event->pmu_ctx->ctx != event->ctx); - put_pmu_ctx(event->pmu_ctx); - } - - /* - * perf_event_free_task() relies on put_ctx() being 'last', in - * particular all task references must be cleaned up. - */ - if (event->ctx) - put_ctx(event->ctx); - - if (event->pmu) - module_put(event->pmu->module); - - call_rcu(&event->rcu_head, free_event_rcu); -} - -/* vs perf_event_alloc() success */ static void _free_event(struct perf_event *event) { irq_work_sync(&event->pending_irq); @@ -5407,10 +5358,42 @@ static void _free_event(struct perf_event *event) mutex_unlock(&event->mmap_mutex); } + if (is_cgroup_event(event)) + perf_detach_cgroup(event); + + if (!event->parent) { + if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) + put_callchain_buffers(); + } + perf_event_free_bpf_prog(event); perf_addr_filters_splice(event, NULL); + kfree(event->addr_filter_ranges); - __free_event(event); + if (event->destroy) + event->destroy(event); + + /* + * Must be after ->destroy(), due to uprobe_perf_close() using + * hw.target. + */ + if (event->hw.target) + put_task_struct(event->hw.target); + + if (event->pmu_ctx) + put_pmu_ctx(event->pmu_ctx); + + /* + * perf_event_free_task() relies on put_ctx() being 'last', in particular + * all task references must be cleaned up. + */ + if (event->ctx) + put_ctx(event->ctx); + + exclusive_event_destroy(event); + module_put(event->pmu->module); + + call_rcu(&event->rcu_head, free_event_rcu); } /* @@ -12017,10 +12000,8 @@ static int perf_try_init_event(struct pmu *pmu, struct perf_event *event) event->destroy(event); } - if (ret) { - event->pmu = NULL; + if (ret) module_put(pmu->module); - } return ret; } @@ -12348,7 +12329,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, * See perf_output_read(). */ if (has_inherit_and_sample_read(attr) && !(attr->sample_type & PERF_SAMPLE_TID)) - goto err; + goto err_ns; if (!has_branch_stack(event)) event->attr.branch_sample_type = 0; @@ -12356,7 +12337,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, pmu = perf_init_event(event); if (IS_ERR(pmu)) { err = PTR_ERR(pmu); - goto err; + goto err_ns; } /* @@ -12366,25 +12347,25 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, */ if (pmu->task_ctx_nr == perf_invalid_context && (task || cgroup_fd != -1)) { err = -EINVAL; - goto err; + goto err_pmu; } if (event->attr.aux_output && (!(pmu->capabilities & PERF_PMU_CAP_AUX_OUTPUT) || event->attr.aux_pause || event->attr.aux_resume)) { err = -EOPNOTSUPP; - goto err; + goto err_pmu; } if (event->attr.aux_pause && event->attr.aux_resume) { err = -EINVAL; - goto err; + goto err_pmu; } if (event->attr.aux_start_paused) { if (!(pmu->capabilities & PERF_PMU_CAP_AUX_PAUSE)) { err = -EOPNOTSUPP; - goto err; + goto err_pmu; } event->hw.aux_paused = 1; } @@ -12392,12 +12373,12 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, if (cgroup_fd != -1) { err = perf_cgroup_connect(cgroup_fd, event, attr, group_leader); if (err) - goto err; + goto err_pmu; } err = exclusive_event_init(event); if (err) - goto err; + goto err_pmu; if (has_addr_filter(event)) { event->addr_filter_ranges = kcalloc(pmu->nr_addr_filters, @@ -12405,7 +12386,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, GFP_KERNEL); if (!event->addr_filter_ranges) { err = -ENOMEM; - goto err; + goto err_per_task; } /* @@ -12430,22 +12411,41 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) { err = get_callchain_buffers(attr->sample_max_stack); if (err) - goto err; - event->attach_state |= PERF_ATTACH_CALLCHAIN; + goto err_addr_filters; } } err = security_perf_event_alloc(event); if (err) - goto err; + goto err_callchain_buffer; /* symmetric to unaccount_event() in _free_event() */ account_event(event); return event; -err: - __free_event(event); +err_callchain_buffer: + if (!event->parent) { + if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) + put_callchain_buffers(); + } +err_addr_filters: + kfree(event->addr_filter_ranges); + +err_per_task: + exclusive_event_destroy(event); + +err_pmu: + if (is_cgroup_event(event)) + perf_detach_cgroup(event); + if (event->destroy) + event->destroy(event); + module_put(pmu->module); +err_ns: + if (event->hw.target) + put_task_struct(event->hw.target); + call_rcu(&event->rcu_head, free_event_rcu); + return ERR_PTR(err); }