Skip to content

Commit c5de60c

Browse files
namhyungPeter Zijlstra
authored andcommitted
perf/core: Fix cgroup event list management
The active cgroup events are managed in the per-cpu cgrp_cpuctx_list. This list is only accessed from current cpu and not protected by any locks. But from the commit ef54c1a ("perf: Rework perf_event_exit_event()"), it's possible to access (actually modify) the list from another cpu. In the perf_remove_from_context(), it can remove an event from the context without an IPI when the context is not active. This is not safe with cgroup events which can have some active events in the context even if ctx->is_active is 0 at the moment. The target cpu might be in the middle of list iteration at the same time. If the event is enabled when it's about to be closed, it might call perf_cgroup_event_disable() and list_del() with the cgrp_cpuctx_list on a different cpu. This resulted in a crash due to an invalid list pointer access during the cgroup list traversal on the cpu which the event belongs to. Let's fallback to IPI to access the cgrp_cpuctx_list from that cpu. Similarly, perf_install_in_context() should use IPI for the cgroup events too. Fixes: ef54c1a ("perf: Rework perf_event_exit_event()") Signed-off-by: Namhyung Kim <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 961c391 commit c5de60c

File tree

1 file changed

+9
-2
lines changed

1 file changed

+9
-2
lines changed

kernel/events/core.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2462,7 +2462,11 @@ static void perf_remove_from_context(struct perf_event *event, unsigned long fla
24622462
* event_function_call() user.
24632463
*/
24642464
raw_spin_lock_irq(&ctx->lock);
2465-
if (!ctx->is_active) {
2465+
/*
2466+
* Cgroup events are per-cpu events, and must IPI because of
2467+
* cgrp_cpuctx_list.
2468+
*/
2469+
if (!ctx->is_active && !is_cgroup_event(event)) {
24662470
__perf_remove_from_context(event, __get_cpu_context(ctx),
24672471
ctx, (void *)flags);
24682472
raw_spin_unlock_irq(&ctx->lock);
@@ -2895,11 +2899,14 @@ perf_install_in_context(struct perf_event_context *ctx,
28952899
* perf_event_attr::disabled events will not run and can be initialized
28962900
* without IPI. Except when this is the first event for the context, in
28972901
* that case we need the magic of the IPI to set ctx->is_active.
2902+
* Similarly, cgroup events for the context also needs the IPI to
2903+
* manipulate the cgrp_cpuctx_list.
28982904
*
28992905
* The IOC_ENABLE that is sure to follow the creation of a disabled
29002906
* event will issue the IPI and reprogram the hardware.
29012907
*/
2902-
if (__perf_effective_state(event) == PERF_EVENT_STATE_OFF && ctx->nr_events) {
2908+
if (__perf_effective_state(event) == PERF_EVENT_STATE_OFF &&
2909+
ctx->nr_events && !is_cgroup_event(event)) {
29032910
raw_spin_lock_irq(&ctx->lock);
29042911
if (ctx->task == TASK_TOMBSTONE) {
29052912
raw_spin_unlock_irq(&ctx->lock);

0 commit comments

Comments
 (0)