From: Peter Zijlstra peterz@infradead.org
mainline inclusion from mainline-v6.3-rc6 commit b168098912926236bbeebaf7795eb7aab76d2b45 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAL27E
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
---------------------------
Thomas reported that offlining CPUs spends a lot of time in synchronize_rcu() as called from perf_pmu_migrate_context() even though he's not actually using uncore events.
Turns out, the thing is unconditionally waiting for RCU, even if there's no actual events to migrate.
Fixes: 0cda4c023132 ("perf: Introduce perf_pmu_migrate_context()") Reported-by: Thomas Gleixner tglx@linutronix.de Signed-off-by: Peter Zijlstra (Intel) peterz@infradead.org Tested-by: Thomas Gleixner tglx@linutronix.de Reviewed-by: Thomas Gleixner tglx@linutronix.de Reviewed-by: Paul E. McKenney paulmck@kernel.org Link: https://lkml.kernel.org/r/20230403090858.GT4253@hirez.programming.kicks-ass.... Conflicts: kernel/events/core.c [Due to not merge previous commit bd27568117664b8b3e259721393df420ed51f57b] Signed-off-by: Liu Chuang liuchuang40@huawei.com --- kernel/events/core.c | 68 +++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 33 deletions(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c index b1fabcaeeffd..46b0c7fc2270 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -12424,42 +12424,44 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) list_add(&event->migrate_entry, &events); }
- /* - * Wait for the events to quiesce before re-instating them. - */ - synchronize_rcu(); + if (!list_empty(&events)) { + /* + * Wait for the events to quiesce before re-instating them. + */ + synchronize_rcu();
- /* - * Re-instate events in 2 passes. - * - * Skip over group leaders and only install siblings on this first - * pass, siblings will not get enabled without a leader, however a - * leader will enable its siblings, even if those are still on the old - * context. - */ - list_for_each_entry_safe(event, tmp, &events, migrate_entry) { - if (event->group_leader == event) - continue; + /* + * Re-instate events in 2 passes. + * + * Skip over group leaders and only install siblings on this first + * pass, siblings will not get enabled without a leader, however a + * leader will enable its siblings, even if those are still on the old + * context. + */ + list_for_each_entry_safe(event, tmp, &events, migrate_entry) { + if (event->group_leader == event) + continue;
- list_del(&event->migrate_entry); - if (event->state >= PERF_EVENT_STATE_OFF) - event->state = PERF_EVENT_STATE_INACTIVE; - account_event_cpu(event, dst_cpu); - perf_install_in_context(dst_ctx, event, dst_cpu); - get_ctx(dst_ctx); - } + list_del(&event->migrate_entry); + if (event->state >= PERF_EVENT_STATE_OFF) + event->state = PERF_EVENT_STATE_INACTIVE; + account_event_cpu(event, dst_cpu); + perf_install_in_context(dst_ctx, event, dst_cpu); + get_ctx(dst_ctx); + }
- /* - * Once all the siblings are setup properly, install the group leaders - * to make it go. - */ - list_for_each_entry_safe(event, tmp, &events, migrate_entry) { - list_del(&event->migrate_entry); - if (event->state >= PERF_EVENT_STATE_OFF) - event->state = PERF_EVENT_STATE_INACTIVE; - account_event_cpu(event, dst_cpu); - perf_install_in_context(dst_ctx, event, dst_cpu); - get_ctx(dst_ctx); + /* + * Once all the siblings are setup properly, install the group leaders + * to make it go. + */ + list_for_each_entry_safe(event, tmp, &events, migrate_entry) { + list_del(&event->migrate_entry); + if (event->state >= PERF_EVENT_STATE_OFF) + event->state = PERF_EVENT_STATE_INACTIVE; + account_event_cpu(event, dst_cpu); + perf_install_in_context(dst_ctx, event, dst_cpu); + get_ctx(dst_ctx); + } } mutex_unlock(&dst_ctx->mutex); mutex_unlock(&src_ctx->mutex);