diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-18 12:31:07 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-18 12:31:07 -0700 |
| commit | 8915457146a11d20a6c0786396376afda65eec40 (patch) | |
| tree | 75a8c5625e800896bb4d977dede53590e60cce95 /kernel | |
| parent | a0acd94e3819fcd8346ae3c16df987e0fabb3128 (diff) | |
| parent | 917d558b151cad5b05991e5eaee22efab33525ca (diff) | |
| download | linux-next-8915457146a11d20a6c0786396376afda65eec40.tar.gz linux-next-8915457146a11d20a6c0786396376afda65eec40.zip | |
Merge tag 'perf-core-2026-08-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull performance events updates from Ingo Molnar:
"uprobes updates:
- Fix a category of bugs with optimized uprobes that can clobber the
redzone area with call instruction storing return address on stack
where user code may keep temporary data without adjusting RSP.
Fix this by moving the optimized uprobes on top of 10-bytes NOP
instruction, so we can squeeze another instruction to escape the
redzone area before doing the call (Jiri Olsa, Andrii Nakryiko)
- Switch uretprobes_srcu to SRCU-fast-updown, to improve performance
(Puranjay Mohan)
Intel CPU PMU driver updates:
- Optimize ACR handling in match_prev_assignment() (Dapeng Mi)
- Fix various PMU driver bugs and data leaks (Dapeng Mi)
- Fix Intel PT stop/start with no update (Adrian Hunter)
Intel uncore PMU driver updates:
- Fix various uncore PMU setup robustness bugs (Zide Chen)
AMD uncore PMU driver updates:
- Add group validation (Sandipan Das)
.. and misc fixes and updates by Dapeng Mi, Randy Dunlap and Zide Chen"
* tag 'perf-core-2026-08-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (42 commits)
perf/x86: Optimize ACR handling in match_prev_assignment()
perf/x86/intel: Fix intel_cap handling on hybrid PMUs
perf/x86: Remove stale fixed counter helper and fix hybrid PMU access
perf/x86/intel: Unwind cpuc state if PEBS buffer setup fails
perf/x86: Guard intel_pmu_cpu_dead() against invalid hybrid PMU casts
perf/x86: Free hybrid state on PMU init failure
perf/x86: Unregister PMI handler on PMU init failure
perf/x86/intel/pt: Fix stop/start with no update
perf/x86/intel/pt: Use bitwise access for PERF_HES_STOPPED
perf/x86/intel/pt: Factor out pt_config_enable()
uprobes: Switch uretprobes_srcu to SRCU-fast-updown
srcu: Add lock guard for srcu_fast_updown flavor
perf/x86/intel/pt: Drop kernel-doc for deleted struct members
perf/x86/amd/uncore: Add group validation
selftests/bpf: Add tests for forked/cloned optimized uprobes
selftests/bpf: Add tests for uprobe nop10 red zone clobbering
selftests/bpf: Add reattach tests for uprobe syscall
selftests/bpf: Change uprobe/usdt trigger bench code to use nop10
selftests/bpf: Change uprobe syscall tests to use nop10
selftests/bpf: Emit nop,nop10 instructions combo for x86_64 arch
...
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/events/core.c | 41 | ||||
| -rw-r--r-- | kernel/events/uprobes.c | 39 | ||||
| -rw-r--r-- | kernel/fork.c | 1 |
3 files changed, 50 insertions, 31 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index 1a73ba0747df..2eee83cdb43d 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7800,10 +7800,20 @@ unsigned long perf_misc_flags(struct perf_event *event, unsigned long perf_instruction_pointer(struct perf_event *event, struct pt_regs *regs) { - if (should_sample_guest(event)) - return perf_guest_get_ip(); + /* + * Hardware skid can lead to a scenario where a PMI is + * delivered after the CPU has already entered kernel mode. + * In that case, user-space sampling must not expose kernel + * register state. + */ + if (should_sample_guest(event)) { + return event->attr.exclude_kernel && + !(perf_guest_state() & PERF_GUEST_USER) ? + 0 : perf_guest_get_ip(); + } - return perf_arch_instruction_pointer(regs); + return event->attr.exclude_kernel && !user_mode(regs) ? + 0 : perf_arch_instruction_pointer(regs); } static void @@ -7837,10 +7847,22 @@ static void perf_sample_regs_user(struct perf_regs *regs_user, } static void perf_sample_regs_intr(struct perf_regs *regs_intr, - struct pt_regs *regs) + struct pt_regs *regs, + bool exclude_kernel) { - regs_intr->regs = regs; - regs_intr->abi = perf_reg_abi(current); + /* + * Hardware skid can lead to a scenario where a PMI is + * delivered after the CPU has already entered kernel mode. + * In that case, user-space sampling must not expose kernel + * register state. + */ + if (exclude_kernel && !user_mode(regs)) { + regs_intr->abi = PERF_SAMPLE_REGS_ABI_NONE; + regs_intr->regs = NULL; + } else { + regs_intr->regs = regs; + regs_intr->abi = perf_reg_abi(current); + } } @@ -8731,7 +8753,8 @@ void perf_prepare_sample(struct perf_sample_data *data, /* regs dump ABI info */ int size = sizeof(u64); - perf_sample_regs_intr(&data->regs_intr, regs); + perf_sample_regs_intr(&data->regs_intr, regs, + event->attr.exclude_kernel); if (data->regs_intr.regs) { u64 mask = event->attr.sample_regs_intr; @@ -13918,7 +13941,9 @@ SYSCALL_DEFINE5(perf_event_open, if (err) return err; - if (!attr.exclude_kernel) { + if (!attr.exclude_kernel || + ((attr.sample_type & PERF_SAMPLE_CALLCHAIN) && + !attr.exclude_callchain_kernel)) { err = perf_allow_kernel(); if (err) return err; diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 6300b216012c..a18529ab2b87 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -54,7 +54,7 @@ static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ]; DEFINE_STATIC_PERCPU_RWSEM(dup_mmap_sem); /* Covers return_instance's uprobe lifetime. */ -DEFINE_STATIC_SRCU(uretprobes_srcu); +DEFINE_STATIC_SRCU_FAST_UPDOWN(uretprobes_srcu); /* Have a copy of original instruction */ #define UPROBE_COPY_INSN 0 @@ -707,12 +707,13 @@ static void put_uprobe(struct uprobe *uprobe) } /* Initialize hprobe as SRCU-protected "leased" uprobe */ -static void hprobe_init_leased(struct hprobe *hprobe, struct uprobe *uprobe, int srcu_idx) +static void hprobe_init_leased(struct hprobe *hprobe, struct uprobe *uprobe, + struct srcu_ctr __percpu *srcu_scp) { WARN_ON(!uprobe); hprobe->state = HPROBE_LEASED; hprobe->uprobe = uprobe; - hprobe->srcu_idx = srcu_idx; + hprobe->srcu_scp = srcu_scp; } /* Initialize hprobe as refcounted ("stable") uprobe (uprobe can be NULL). */ @@ -720,7 +721,7 @@ static void hprobe_init_stable(struct hprobe *hprobe, struct uprobe *uprobe) { hprobe->state = uprobe ? HPROBE_STABLE : HPROBE_GONE; hprobe->uprobe = uprobe; - hprobe->srcu_idx = -1; + hprobe->srcu_scp = NULL; } /* @@ -757,7 +758,7 @@ static void hprobe_finalize(struct hprobe *hprobe, enum hprobe_state hstate) { switch (hstate) { case HPROBE_LEASED: - __srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx); + srcu_up_read_fast(&uretprobes_srcu, hprobe->srcu_scp); break; case HPROBE_STABLE: put_uprobe(hprobe->uprobe); @@ -829,7 +830,7 @@ static struct uprobe *hprobe_expire(struct hprobe *hprobe, bool get) */ if (try_cmpxchg(&hprobe->state, &hstate, uprobe ? HPROBE_STABLE : HPROBE_GONE)) { /* We won the race, we are the ones to unlock SRCU */ - __srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx); + srcu_up_read_fast(&uretprobes_srcu, hprobe->srcu_scp); return get && uprobe ? get_uprobe(uprobe) : uprobe; } @@ -1806,14 +1807,6 @@ static struct xol_area *get_xol_area(void) return area; } -void __weak arch_uprobe_clear_state(struct mm_struct *mm) -{ -} - -void __weak arch_uprobe_init_state(struct mm_struct *mm) -{ -} - /* * uprobe_clear_state - Free the area allocated for slots. */ @@ -1825,8 +1818,6 @@ void uprobe_clear_state(struct mm_struct *mm) delayed_uprobe_remove(NULL, mm); mutex_unlock(&delayed_uprobe_lock); - arch_uprobe_clear_state(mm); - if (!area) return; @@ -2045,7 +2036,7 @@ static void ri_timer(struct timer_list *timer) struct return_instance *ri; /* SRCU protects uprobe from reuse for the cmpxchg() inside hprobe_expire(). */ - guard(srcu)(&uretprobes_srcu); + guard(srcu_fast_updown)(&uretprobes_srcu); /* RCU protects return_instance from freeing. */ guard(rcu)(); @@ -2142,7 +2133,7 @@ static int dup_utask(struct task_struct *t, struct uprobe_task *o_utask) t->utask = n_utask; /* protect uprobes from freeing, we'll need try_get_uprobe() them */ - guard(srcu)(&uretprobes_srcu); + guard(srcu_fast_updown)(&uretprobes_srcu); p = &n_utask->return_instances; for (o = o_utask->return_instances; o; o = o->next) { @@ -2254,8 +2245,8 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs, { struct uprobe_task *utask = current->utask; unsigned long orig_ret_vaddr, trampoline_vaddr; + struct srcu_ctr __percpu *srcu_scp; bool chained; - int srcu_idx; if (!get_xol_area()) goto free; @@ -2293,8 +2284,12 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs, orig_ret_vaddr = utask->return_instances->orig_ret_vaddr; } - /* __srcu_read_lock() because SRCU lock survives switch to user space */ - srcu_idx = __srcu_read_lock(&uretprobes_srcu); + /* + * Use srcu_down_read_fast() because the SRCU lock survives a switch to + * user space and can be unlocked from a different context by ri_timer() + * or dup_utask(). + */ + srcu_scp = srcu_down_read_fast(&uretprobes_srcu); ri->func = instruction_pointer(regs); ri->stack = user_stack_pointer(regs); @@ -2303,7 +2298,7 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs, utask->depth++; - hprobe_init_leased(&ri->hprobe, uprobe, srcu_idx); + hprobe_init_leased(&ri->hprobe, uprobe, srcu_scp); ri->next = utask->return_instances; rcu_assign_pointer(utask->return_instances, ri); diff --git a/kernel/fork.c b/kernel/fork.c index 94e021eabf1d..175c73bbe2bf 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1069,7 +1069,6 @@ static void mm_init_uprobes_state(struct mm_struct *mm) { #ifdef CONFIG_UPROBES mm->uprobes_state.xol_area = NULL; - arch_uprobe_init_state(mm); #endif } |
