diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-09-13 08:44:54 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-09-13 08:44:54 -0700 |
| commit | 85855f85de484c464adb39076ebcb090c7eeb3b5 (patch) | |
| tree | 5feecba8336077197f5553bb61c9d4f65c141d31 | |
| parent | feb66eea6b095e488755052e96f1e0d8b51e42c4 (diff) | |
| parent | a56c03a397e2cd0c4cf8da96dcd6214f7d0e7d8c (diff) | |
| download | linux-85855f85de484c464adb39076ebcb090c7eeb3b5.tar.gz linux-85855f85de484c464adb39076ebcb090c7eeb3b5.zip | |
Merge tag 'perf-urgent-2026-09-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf events fixes from Ingo Molnar
- Fix sched_cb_list corruption on PMU callbacks that
invoke list_del() during perf_event_overflow()
calls (Thomas Richter)
- Fix PEBS pt_regs->flags snapshot data that
regressed with the introduction of adaptive
PEBS v4 support (Dapeng Mi)
- Fix possible drain_pebs() re-entry bug when
intel_pmu_drain_pebs_buffer() is called from
process context (Dapeng Mi)
* tag 'perf-urgent-2026-09-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
perf/x86/intel: Prevent drain_pebs() reentry
perf/x86/intel: Correct pt_regs->flags update for PEBS path
perf/core: Allow list_del during perf_event_overflow()
| -rw-r--r-- | arch/x86/events/intel/core.c | 33 | ||||
| -rw-r--r-- | arch/x86/events/intel/ds.c | 18 | ||||
| -rw-r--r-- | arch/x86/events/perf_event.h | 3 | ||||
| -rw-r--r-- | kernel/events/core.c | 4 |
4 files changed, 45 insertions, 13 deletions
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index cc13164d948f..1ac2ca35db53 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -3125,6 +3125,27 @@ static void intel_pmu_del_event(struct perf_event *event) this_cpu_ptr(&cpu_hw_events)->n_late_setup--; } +int __intel_pmu_quiesce(void) +{ + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + int pmu_enabled = cpuc->enabled; + + cpuc->enabled = 0; + if (pmu_enabled) + intel_pmu_disable_all(); + + return pmu_enabled; +} + +void __intel_pmu_resume(int pmu_enabled) +{ + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + + cpuc->enabled = pmu_enabled; + if (pmu_enabled) + intel_pmu_enable_all(0); +} + static int icl_set_topdown_event_period(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -3316,16 +3337,13 @@ static void intel_pmu_read_event(struct perf_event *event) if (event->hw.flags & (PERF_X86_EVENT_AUTO_RELOAD | PERF_X86_EVENT_TOPDOWN) || is_pebs_counter_event_group(event)) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - bool pmu_enabled = cpuc->enabled; + int pmu_enabled; /* Only need to call update_topdown_event() once for group read. */ if (is_metric_event(event) && (cpuc->txn_flags & PERF_PMU_TXN_READ)) return; - cpuc->enabled = 0; - if (pmu_enabled) - intel_pmu_disable_all(); - + pmu_enabled = __intel_pmu_quiesce(); /* * If the PEBS counters snapshotting is enabled, * the topdown event is available in PEBS records. @@ -3334,10 +3352,7 @@ static void intel_pmu_read_event(struct perf_event *event) static_call(intel_pmu_update_topdown_event)(event, NULL); else intel_pmu_drain_pebs_buffer(); - - cpuc->enabled = pmu_enabled; - if (pmu_enabled) - intel_pmu_enable_all(0); + __intel_pmu_resume(pmu_enabled); return; } diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c index 8940f0292229..b98029b44052 100644 --- a/arch/x86/events/intel/ds.c +++ b/arch/x86/events/intel/ds.c @@ -1242,8 +1242,11 @@ unlock: void intel_pmu_drain_pebs_buffer(void) { + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_sample_data data; + WARN_ON_ONCE(cpuc->enabled); + static_call(x86_pmu_drain_pebs)(NULL, &data); } @@ -1864,8 +1867,11 @@ static void intel_pmu_pebs_via_pt_enable(struct perf_event *event) static inline void intel_pmu_drain_large_pebs(struct cpu_hw_events *cpuc) { if (cpuc->n_pebs == cpuc->n_large_pebs && - cpuc->n_pebs != cpuc->n_pebs_via_pt) + cpuc->n_pebs != cpuc->n_pebs_via_pt) { + int enabled = __intel_pmu_quiesce(); intel_pmu_drain_pebs_buffer(); + __intel_pmu_resume(enabled); + } } static void __intel_pmu_pebs_enable(struct perf_event *event) @@ -2432,7 +2438,7 @@ static inline void __setup_pebs_basic_group(struct perf_event *event, { /* The ip in basic is EventingIP */ set_linear_ip(regs, ip); - regs->flags = PERF_EFLAGS_EXACT; + regs->flags |= PERF_EFLAGS_EXACT; setup_pebs_time(event, data, tsc); if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) @@ -2444,9 +2450,17 @@ static inline void __setup_pebs_gpr_group(struct perf_event *event, struct pebs_gprs *gprs, u64 sample_type) { + /* + * Update flags with PEBS data. PERF_EFLAGS_EXACT must be set + * in previous basic group handling. + */ + regs->flags = gprs->flags | PERF_EFLAGS_EXACT; + if (event->attr.precise_ip < 2) { set_linear_ip(regs, gprs->ip); regs->flags &= ~PERF_EFLAGS_EXACT; + } else if (regs->flags & X86_VM_MASK) { + regs->flags ^= (PERF_EFLAGS_VM | X86_VM_MASK); } if (sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)) diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h index 71ed5b2acea2..4680cba91340 100644 --- a/arch/x86/events/perf_event.h +++ b/arch/x86/events/perf_event.h @@ -1638,6 +1638,9 @@ static __always_inline void __intel_pmu_lbr_disable(void) wrmsrq(MSR_IA32_DEBUGCTLMSR, debugctl); } +extern int __intel_pmu_quiesce(void); +extern void __intel_pmu_resume(int pmu_enabled); + int intel_pmu_save_and_restart(struct perf_event *event); struct event_constraint * diff --git a/kernel/events/core.c b/kernel/events/core.c index 33210aff3ee6..fe33fe15689d 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3925,13 +3925,13 @@ static void perf_pmu_sched_task(struct task_struct *prev, bool sched_in) { struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); - struct perf_cpu_pmu_context *cpc; + struct perf_cpu_pmu_context *cpc, *cpc2; /* cpuctx->task_ctx will be handled in perf_event_context_sched_in/out */ if (prev == next || cpuctx->task_ctx) return; - list_for_each_entry(cpc, this_cpu_ptr(&sched_cb_list), sched_cb_entry) + list_for_each_entry_safe(cpc, cpc2, this_cpu_ptr(&sched_cb_list), sched_cb_entry) __perf_pmu_sched_task(cpc, sched_in ? next : prev, sched_in); } |
