6.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Tiezhu Yang [ Upstream commit f087903018e35d38b8932e18b010ee2e8699d1a1 ] In do_watch(), the kernel unconditionally forces a SIGTRAP signal at the end of the handler via force_sig(SIGTRAP). This is essential for ptrace operations, but it severely disrupts standard perf_event usage. Under normal perf usage, it is not necessary to force a SIGTRAP signal on every hit. Forcing it unnecessarily aborts the target process if no user-space signal handler is registered, which violates performance monitoring behaviors and injects context switch overheads. However, a conditional check using only 'current->ptrace' would block legitimate user-requested signals when a perf_event is configured with 'attr.sigtrap = 1' for asynchronous user-space tracking. Fix this by refactoring breakpoint_handler() and watchpoint_handler() to return a boolean status indicating whether any triggered breakpoint explicitly requires a signal notification. Update do_watch() to enforce the SIGTRAP signal only when the process is actively being debugged via ptrace, or when the underlying perf_event infrastructure specifically demands it via 'attr.sigtrap'. Cc: stable@vger.kernel.org Fixes: 424421a7f34c ("LoongArch: ptrace: Add hardware single step support") Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen Signed-off-by: Sasha Levin --- arch/loongarch/include/asm/hw_breakpoint.h | 4 ++-- arch/loongarch/kernel/hw_breakpoint.c | 18 ++++++++++++++++-- arch/loongarch/kernel/traps.c | 9 ++++++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/arch/loongarch/include/asm/hw_breakpoint.h b/arch/loongarch/include/asm/hw_breakpoint.h index 5faa97a87a9e2..d202052df8a16 100644 --- a/arch/loongarch/include/asm/hw_breakpoint.h +++ b/arch/loongarch/include/asm/hw_breakpoint.h @@ -116,8 +116,8 @@ extern void arch_uninstall_hw_breakpoint(struct perf_event *bp); extern int hw_breakpoint_slots(int type); extern void hw_breakpoint_pmu_read(struct perf_event *bp); -void breakpoint_handler(struct pt_regs *regs); -void watchpoint_handler(struct pt_regs *regs); +bool breakpoint_handler(struct pt_regs *regs); +bool watchpoint_handler(struct pt_regs *regs); #ifdef CONFIG_HAVE_HW_BREAKPOINT extern void ptrace_hw_copy_thread(struct task_struct *task); diff --git a/arch/loongarch/kernel/hw_breakpoint.c b/arch/loongarch/kernel/hw_breakpoint.c index 7f69cf361a5e2..d009ed780cf02 100644 --- a/arch/loongarch/kernel/hw_breakpoint.c +++ b/arch/loongarch/kernel/hw_breakpoint.c @@ -482,9 +482,10 @@ NOKPROBE_SYMBOL(update_bp_registers); /* * Debug exception handlers. */ -void breakpoint_handler(struct pt_regs *regs) +bool breakpoint_handler(struct pt_regs *regs) { int i; + bool need_sigtrap = false; struct perf_event *bp, **slots; slots = this_cpu_ptr(bp_on_reg); @@ -494,17 +495,24 @@ void breakpoint_handler(struct pt_regs *regs) bp = slots[i]; if (bp == NULL) continue; + perf_bp_event(bp, regs); + if (bp->attr.sigtrap) + need_sigtrap = true; + csr_write32(0x1 << i, LOONGARCH_CSR_FWPS); update_bp_registers(regs, 0, 0); } } + + return need_sigtrap; } NOKPROBE_SYMBOL(breakpoint_handler); -void watchpoint_handler(struct pt_regs *regs) +bool watchpoint_handler(struct pt_regs *regs) { int i; + bool need_sigtrap = false; struct perf_event *wp, **slots; slots = this_cpu_ptr(wp_on_reg); @@ -514,11 +522,17 @@ void watchpoint_handler(struct pt_regs *regs) wp = slots[i]; if (wp == NULL) continue; + perf_bp_event(wp, regs); + if (wp->attr.sigtrap) + need_sigtrap = true; + csr_write32(0x1 << i, LOONGARCH_CSR_MWPS); update_bp_registers(regs, 0, 1); } } + + return need_sigtrap; } NOKPROBE_SYMBOL(watchpoint_handler); diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index 4c2e5a13f07e4..9ce12327d64ea 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -807,6 +807,8 @@ asmlinkage void noinstr do_watch(struct pt_regs *regs) #ifndef CONFIG_HAVE_HW_BREAKPOINT pr_warn("Hardware watch point handler not implemented!\n"); #else + bool need_sigtrap = !!current->ptrace; + if (kgdb_breakpoint_handler(regs)) goto out; @@ -843,11 +845,12 @@ asmlinkage void noinstr do_watch(struct pt_regs *regs) } } } else { - breakpoint_handler(regs); - watchpoint_handler(regs); + need_sigtrap |= breakpoint_handler(regs); + need_sigtrap |= watchpoint_handler(regs); } - force_sig(SIGTRAP); + if (need_sigtrap) + force_sig(SIGTRAP); out: #endif irqentry_exit(regs, state); -- 2.53.0