[PATCH v1 4/6] LoongArch: Only send SIGTRAP signal if necessary in do_watch()
Tiezhu Yang <[email protected]>
| Newsgroups | dev.linux.lists.loongarch,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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'.
Fixes: 424421a7f34c ("LoongArch: ptrace: Add hardware single step support")
Cc: [email protected]
Signed-off-by: Tiezhu Yang <[email protected]>
---
arch/loongarch/include/asm/hw_breakpoint.h | 4 ++--
arch/loongarch/kernel/hw_breakpoint.c | 18 ++++++++++++++++--
arch/loongarch/kernel/traps.c | 8 +++++---
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/arch/loongarch/include/asm/hw_breakpoint.h b/arch/loongarch/include/asm/hw_breakpoint.h
index 5faa97a87a9e..d202052df8a1 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 7f69cf361a5e..3683a52b2368 100644
--- a/arch/loongarch/kernel/hw_breakpoint.c
+++ b/arch/loongarch/kernel/hw_breakpoint.c
@@ -482,10 +482,11 @@ NOKPROBE_SYMBOL(update_bp_registers);
/*
* Debug exception handlers.
*/
-void breakpoint_handler(struct pt_regs *regs)
+bool breakpoint_handler(struct pt_regs *regs)
{
int i;
struct perf_event *bp, **slots;
+ bool need_sigtrap = false;
slots = this_cpu_ptr(bp_on_reg);
@@ -494,18 +495,25 @@ 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;
struct perf_event *wp, **slots;
+ bool need_sigtrap = false;
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 c4d7e55fb3ea..de0c9b03d551 100644
--- a/arch/loongarch/kernel/traps.c
+++ b/arch/loongarch/kernel/traps.c
@@ -811,6 +811,7 @@ asmlinkage void noinstr do_bp(struct pt_regs *regs)
asmlinkage void noinstr do_watch(struct pt_regs *regs)
{
irqentry_state_t state = irqentry_enter(regs);
+ bool perf_sigtrap = false;
#ifndef CONFIG_HAVE_HW_BREAKPOINT
pr_warn("Hardware watch point handler not implemented!\n");
@@ -851,11 +852,12 @@ asmlinkage void noinstr do_watch(struct pt_regs *regs)
}
}
} else {
- breakpoint_handler(regs);
- watchpoint_handler(regs);
+ perf_sigtrap |= breakpoint_handler(regs);
+ perf_sigtrap |= watchpoint_handler(regs);
}
- force_sig(SIGTRAP);
+ if (current->ptrace || perf_sigtrap)
+ force_sig(SIGTRAP);
out:
#endif
irqentry_exit(regs, state);
--
2.42.0