* [PATCH v4] ARM, ARM64, LONGARCH, XTENSA: Delay HW BP notification to task_work()
@ 2026-10-12 9:04 Sebastian Andrzej Siewior
2026-10-12 9:18 ` sashiko-bot
2026-10-12 10:15 ` Huacai Chen
0 siblings, 2 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-10-12 9:04 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, linux-rt-devel, linux-perf-users,
loongarch
Cc: Mark Rutland, Ian Rogers, Peter Zijlstra, Catalin Marinas,
Oleg Nesterov, WANG Xuerui, Will Deacon, Huacai Chen,
Russell King, Alexander Shishkin, Ingo Molnar, Waiman Long,
Clark Williams, Luis Claudio R. Goncalves, James Clark,
Steven Rostedt, Arnaldo Carvalho de Melo, Namhyung Kim,
Chris Zankel, Max Filippov, Adrian Hunter, Jiri Olsa
Waiman, Luis, Ada reported that HW breakpoints on ARM64 trigger
"sleeping while atomic" warnings on PREEMPT_RT. The hardware event is
delivered with disabled interrupts and perf intrastrucure expects
disabled interrupts while the overflow callback is invoked.
The callback then sends a SIGTRAP signal for which it acquires
sighand_struct::siglock, a spinlock_t which becomes a sleeping lock and
must not be acquired in atomic context.
Delay the event callback until the return to userland.
Add perf_hwbp_notify(), a generic perf callback which delayes the
actual callback invocation to task_work_add() callback. This callback
invokes the architecture defined callback perf_arch_hwbp_send_sig().
This requires struct callback_head and the functions require
ARCH_NEED_PERF_HW_NOTIFY to be defined.
This was reported against ARM64. ARM, LongARCH and Xtensa follow the
same pattern are also converted. Xtensa is the only not supporting
PREEMPT_RT but now we have all architectures using the same pattern.
Fixes: d8fccd9ca5f90 ("arm64: Allow to enable PREEMPT_RT.")
Reported-by: Luis Claudio R. Goncalves <lgoncalv@redhat.com>
Reported-by: Waiman Long <longman@redhat.com>
Closes: https://lore.kernel.org/all/aho0eqjMESuHxECr@redhat.com/
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
Tested-by: Ada Couprie Diaz <ada.coupriediaz@arm.com> (arm64)
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
v3…v4: https://lore.kernel.org/all/20261001143516.Ew8C97WS@linutronix.de/
- Add fixes tag
- Rename the functions as suggested by Ada to use the pattern that perf
uses in other places:
- perf_arch_hwbp_notify() -> perf_hwbp_notify() (generic)
- arch_hwbp_send_sig() -> perf_arch_hwbp_send_sig() (arch specific)
- perf_arch_hwbp_send_sig() -> perf_hwbp_send_sig() (generic)
- Rename ARCH_NEED_PERF_HW_NOTIF -> ARCH_NEED_PERF_HW_NOTIFY
v2…v3: https://lore.kernel.org/all/20260814085118.OPEA_Ssn@linutronix.de/
- Add Xtensa for completion
- sashiko complains and wants TWA_SIGNAL instead TWA_RESUME. His
argument is that a syscall will trap via get_user() and loop forever
instead making progress. This is wrong IMHO. ARM64 will single step
over the watchpoint and continue execution. The only downside is that
userland will get notified after the syscall completed. So my theory.
Using TWA_SIGNAL is worse: Assume we have a watchpoint on UADDR and
are in a futex() syscall. The get_user() invocation will trigger the
exception, the debug handler will step over and queue a signal. The
futex code will notice this and return ERESTARTNOINTR. A signal will
be sent, the syscall restarts, traps onto UADDR again, the loop
continues. But this should be case now, too…
Now that I look into arch_build_bp_info() and do actual testing I must
say arm64 does not support mixed breakpoints. This means there is no
breakpoint in kernel on a userland address. \o/
v1…v2: https://lore.kernel.org/all/20260713144939.FuCj9yvZ@linutronix.de/
- sashiko complained that a memory breakpoint might trigger several
times before a signal is sent if the syscall touches the memory (via
get_user()) more than once before returning back. This would lead to
list corruption in task_work_add(). To handle this, there is now a
variable which is set via xchg before task_work_add() and cleared
after the signal has been sent.
arch/arm/include/asm/hw_breakpoint.h | 1 +
arch/arm/kernel/ptrace.c | 7 ++----
arch/arm64/include/asm/hw_breakpoint.h | 1 +
arch/arm64/kernel/ptrace.c | 6 ++---
arch/loongarch/include/asm/hw_breakpoint.h | 1 +
arch/loongarch/kernel/ptrace.c | 6 ++---
arch/xtensa/include/asm/hw_breakpoint.h | 1 +
arch/xtensa/kernel/ptrace.c | 7 ++----
include/linux/hw_breakpoint.h | 3 +++
include/linux/perf_event.h | 4 ++++
kernel/events/core.c | 26 ++++++++++++++++++++++
11 files changed, 45 insertions(+), 18 deletions(-)
diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h
index e7f9961c53b2d..fa7fb2b2a392a 100644
--- a/arch/arm/include/asm/hw_breakpoint.h
+++ b/arch/arm/include/asm/hw_breakpoint.h
@@ -7,6 +7,7 @@
struct task_struct;
#ifdef CONFIG_HAVE_HW_BREAKPOINT
+#define ARCH_NEED_PERF_HW_NOTIFY
struct arch_hw_breakpoint_ctrl {
u32 __reserved : 9,
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index ed7a2a87a6707..db4b28c6e15a4 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -347,9 +347,7 @@ static long ptrace_hbp_idx_to_num(int idx)
/*
* Handle hitting a HW-breakpoint.
*/
-static void ptrace_hbptriggered(struct perf_event *bp,
- struct perf_sample_data *data,
- struct pt_regs *regs)
+void perf_arch_hwbp_send_sig(struct perf_event *bp)
{
struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
long num;
@@ -424,8 +422,7 @@ static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type)
attr.bp_type = type;
attr.disabled = 1;
- return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL,
- tsk);
+ return register_user_hw_breakpoint(&attr, perf_hwbp_notify, NULL, tsk);
}
static int ptrace_gethbpregs(struct task_struct *tsk, long num,
diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
index bd81cf17744af..0bc75f42b0da6 100644
--- a/arch/arm64/include/asm/hw_breakpoint.h
+++ b/arch/arm64/include/asm/hw_breakpoint.h
@@ -124,6 +124,7 @@ extern void hw_breakpoint_pmu_read(struct perf_event *bp);
extern int hw_breakpoint_slots(int type);
#ifdef CONFIG_HAVE_HW_BREAKPOINT
+#define ARCH_NEED_PERF_HW_NOTIFY
extern void hw_breakpoint_thread_switch(struct task_struct *next);
extern void ptrace_hw_copy_thread(struct task_struct *task);
#else
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index f743cbec1c3ad..dc3793c2a7c3c 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -168,9 +168,7 @@ void ptrace_disable(struct task_struct *child)
/*
* Handle hitting a HW-breakpoint.
*/
-static void ptrace_hbptriggered(struct perf_event *bp,
- struct perf_sample_data *data,
- struct pt_regs *regs)
+void perf_arch_hwbp_send_sig(struct perf_event *bp)
{
struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
const char *desc = "Hardware breakpoint trap (ptrace)";
@@ -312,7 +310,7 @@ static struct perf_event *ptrace_hbp_create(unsigned int note_type,
attr.bp_type = type;
attr.disabled = 1;
- bp = register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, tsk);
+ bp = register_user_hw_breakpoint(&attr, perf_hwbp_notify, NULL, tsk);
if (IS_ERR(bp))
return bp;
diff --git a/arch/loongarch/include/asm/hw_breakpoint.h b/arch/loongarch/include/asm/hw_breakpoint.h
index 5faa97a87a9e2..c2d061ff3763a 100644
--- a/arch/loongarch/include/asm/hw_breakpoint.h
+++ b/arch/loongarch/include/asm/hw_breakpoint.h
@@ -120,6 +120,7 @@ void breakpoint_handler(struct pt_regs *regs);
void watchpoint_handler(struct pt_regs *regs);
#ifdef CONFIG_HAVE_HW_BREAKPOINT
+#define ARCH_NEED_PERF_HW_NOTIFY
extern void ptrace_hw_copy_thread(struct task_struct *task);
extern void hw_breakpoint_thread_switch(struct task_struct *next);
#else
diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c
index be38430f7e280..27e7e64591592 100644
--- a/arch/loongarch/kernel/ptrace.c
+++ b/arch/loongarch/kernel/ptrace.c
@@ -384,9 +384,7 @@ static int lbt_set(struct task_struct *target,
/*
* Handle hitting a HW-breakpoint.
*/
-static void ptrace_hbptriggered(struct perf_event *bp,
- struct perf_sample_data *data,
- struct pt_regs *regs)
+void perf_arch_hwbp_send_sig(struct perf_event *bp)
{
int i;
struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
@@ -479,7 +477,7 @@ static struct perf_event *ptrace_hbp_create(unsigned int note_type,
attr.bp_type = type;
attr.disabled = 1;
- bp = register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, tsk);
+ bp = register_user_hw_breakpoint(&attr, perf_hwbp_notify, NULL, tsk);
if (IS_ERR(bp))
return bp;
diff --git a/arch/xtensa/include/asm/hw_breakpoint.h b/arch/xtensa/include/asm/hw_breakpoint.h
index 9ec86f440a48e..44ea340c88742 100644
--- a/arch/xtensa/include/asm/hw_breakpoint.h
+++ b/arch/xtensa/include/asm/hw_breakpoint.h
@@ -12,6 +12,7 @@
#define __ASM_XTENSA_HW_BREAKPOINT_H
#ifdef CONFIG_HAVE_HW_BREAKPOINT
+#define ARCH_NEED_PERF_HW_NOTIFY
#include <linux/kdebug.h>
#include <linux/types.h>
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c
index 364e4fdabb00d..c9f583d650de2 100644
--- a/arch/xtensa/kernel/ptrace.c
+++ b/arch/xtensa/kernel/ptrace.c
@@ -361,9 +361,7 @@ static int ptrace_pokeusr(struct task_struct *child, long regno, long val)
}
#ifdef CONFIG_HAVE_HW_BREAKPOINT
-static void ptrace_hbptriggered(struct perf_event *bp,
- struct perf_sample_data *data,
- struct pt_regs *regs)
+void perf_arch_hwbp_send_sig(struct perf_event *bp)
{
int i;
struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
@@ -395,8 +393,7 @@ static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type)
attr.bp_type = type;
attr.disabled = 1;
- return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL,
- tsk);
+ return register_user_hw_breakpoint(&attr, perf_hwbp_notify, NULL, tsk);
}
/*
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
index db199d653dd1a..91e8d80220c54 100644
--- a/include/linux/hw_breakpoint.h
+++ b/include/linux/hw_breakpoint.h
@@ -85,6 +85,9 @@ extern int register_perf_hw_breakpoint(struct perf_event *bp);
extern void unregister_hw_breakpoint(struct perf_event *bp);
extern void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events);
extern bool hw_breakpoint_is_used(void);
+extern void perf_arch_hwbp_send_sig(struct perf_event *bp);
+extern void perf_hwbp_notify(struct perf_event *bp, struct perf_sample_data *data,
+ struct pt_regs *regs);
extern int dbg_reserve_bp_slot(struct perf_event *bp);
extern int dbg_release_bp_slot(struct perf_event *bp);
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 2d5e7687875b0..07f2c858d50d1 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -215,6 +215,10 @@ struct hw_perf_event {
/* Last sync'ed generation of filters */
unsigned long addr_filters_gen;
+#ifdef ARCH_NEED_PERF_HW_NOTIFY
+ struct callback_head arch_hw_notif;
+ int arch_hw_notif_busy;
+#endif
/*
* hw_perf_event::state flags; used to track the PERF_EF_* state.
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 7846d70be57f6..619eab68d1dfd 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -13334,6 +13334,28 @@ static void account_event(struct perf_event *event)
account_pmu_sb_event(event);
}
+#ifdef ARCH_NEED_PERF_HW_NOTIFY
+static void perf_hwbp_send_sig(struct callback_head *head)
+{
+ struct perf_event *bp;
+
+ bp = container_of(head, struct perf_event, hw.arch_hw_notif);
+ perf_arch_hwbp_send_sig(bp);
+ xchg_relaxed(&bp->hw.arch_hw_notif_busy, 0);
+ put_event(bp);
+}
+
+void perf_hwbp_notify(struct perf_event *bp, struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ if (WARN_ON_ONCE(!atomic_long_inc_not_zero(&bp->refcount)))
+ return;
+ if (xchg_relaxed(&bp->hw.arch_hw_notif_busy, 1) ||
+ WARN_ON_ONCE(task_work_add(current, &bp->hw.arch_hw_notif, TWA_RESUME)))
+ put_event(bp);
+}
+#endif
+
/*
* Allocate and initialize an event structure
*/
@@ -13439,6 +13461,10 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
}
if (overflow_handler) {
+#ifdef ARCH_NEED_PERF_HW_NOTIFY
+ if (overflow_handler == perf_hwbp_notify)
+ init_task_work(&event->hw.arch_hw_notif, perf_hwbp_send_sig);
+#endif
event->overflow_handler = overflow_handler;
event->overflow_handler_context = context;
} else if (is_write_backward(event)){
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH v4] ARM, ARM64, LONGARCH, XTENSA: Delay HW BP notification to task_work()
2026-10-12 9:04 [PATCH v4] ARM, ARM64, LONGARCH, XTENSA: Delay HW BP notification to task_work() Sebastian Andrzej Siewior
@ 2026-10-12 9:18 ` sashiko-bot
2026-10-12 9:37 ` Sebastian Andrzej Siewior
2026-10-12 10:15 ` Huacai Chen
1 sibling, 1 reply; 5+ messages in thread
From: sashiko-bot @ 2026-10-12 9:18 UTC (permalink / raw)
To: Sebastian Andrzej Siewior; +Cc: linux-kernel, linux-perf-users, linux-rt-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] perf: ODR violation and ABI mismatch in struct perf_event due to unexported macro
--
commit fc15e8519a12261f55ee613e302041c25e4218d0
Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
ARM, ARM64, LONGARCH, XTENSA: Delay HW BP notification to task_work()
This commit delays the hardware event delivery callback to task_work() to
fix sleeping while atomic warnings on PREEMPT_RT. It implements a generic
perf callback perf_hwbp_notify() and adds ARCH_NEED_PERF_HW_NOTIFY to
supported architectures.
Closes: https://lore.kernel.org/all/aho0eqjMESuHxECr@redhat.com/
> diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
> --- a/include/linux/perf_event.h
> +++ b/include/linux/perf_event.h
> @@ -215,6 +215,10 @@ struct hw_perf_event {
>
> /* Last sync'ed generation of filters */
> unsigned long addr_filters_gen;
> +#ifdef ARCH_NEED_PERF_HW_NOTIFY
> + struct callback_head arch_hw_notif;
> + int arch_hw_notif_busy;
> +#endif
[Severity: Critical]
Does this cause an ABI mismatch and One Definition Rule violation across
the kernel?
The ARCH_NEED_PERF_HW_NOTIFY macro is defined in architecture-specific
headers like arch/arm64/include/asm/hw_breakpoint.h, but it is used here
in the generic include/linux/perf_event.h header.
Because linux/perf_event.h does not universally include asm/hw_breakpoint.h,
translation units that include linux/perf_event.h without previously
including the architecture hardware breakpoint header will evaluate
struct hw_perf_event without these fields. Since struct hw_perf_event
is embedded in struct perf_event, this alters the size and member offsets
of struct perf_event across different subsystems.
Can this lead to memory corruption when perf_event objects are passed
between translation units with mismatched struct definitions? Should these
conditional fields be driven by a globally visible Kconfig symbol instead?
>
> /*
> * hw_perf_event::state flags; used to track the PERF_EF_* state.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261012090431.TF3iVDsk@linutronix.de?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH v4] ARM, ARM64, LONGARCH, XTENSA: Delay HW BP notification to task_work()
2026-10-12 9:18 ` sashiko-bot
@ 2026-10-12 9:37 ` Sebastian Andrzej Siewior
0 siblings, 0 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-10-12 9:37 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-kernel, linux-perf-users, linux-rt-devel
On 2026-10-12 09:18:30 [+0000], sashiko-bot@kernel.org wrote:
> > diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
> > --- a/include/linux/perf_event.h
> > +++ b/include/linux/perf_event.h
> > @@ -215,6 +215,10 @@ struct hw_perf_event {
> >
> > /* Last sync'ed generation of filters */
> > unsigned long addr_filters_gen;
> > +#ifdef ARCH_NEED_PERF_HW_NOTIFY
> > + struct callback_head arch_hw_notif;
> > + int arch_hw_notif_busy;
> > +#endif
>
> [Severity: Critical]
> Does this cause an ABI mismatch and One Definition Rule violation across
> the kernel?
So you learned a new thing.
> The ARCH_NEED_PERF_HW_NOTIFY macro is defined in architecture-specific
> headers like arch/arm64/include/asm/hw_breakpoint.h, but it is used here
> in the generic include/linux/perf_event.h header.
>
> Because linux/perf_event.h does not universally include asm/hw_breakpoint.h,
> translation units that include linux/perf_event.h without previously
> including the architecture hardware breakpoint header will evaluate
> struct hw_perf_event without these fields. Since struct hw_perf_event
> is embedded in struct perf_event, this alters the size and member offsets
> of struct perf_event across different subsystems.
>
> Can this lead to memory corruption when perf_event objects are passed
> between translation units with mismatched struct definitions? Should these
> conditional fields be driven by a globally visible Kconfig symbol instead?
This header file includes the mentioned header files defining
ARCH_NEED_PERF_HW_NOTIFY. The condition upon which the header file is
included does not change while the kernel is built. This could be moved
to Kconfig but I don't see the need for this.
Sebastian
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v4] ARM, ARM64, LONGARCH, XTENSA: Delay HW BP notification to task_work()
2026-10-12 9:04 [PATCH v4] ARM, ARM64, LONGARCH, XTENSA: Delay HW BP notification to task_work() Sebastian Andrzej Siewior
2026-10-12 9:18 ` sashiko-bot
@ 2026-10-12 10:15 ` Huacai Chen
2026-10-12 10:21 ` Sebastian Andrzej Siewior
1 sibling, 1 reply; 5+ messages in thread
From: Huacai Chen @ 2026-10-12 10:15 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: linux-arm-kernel, linux-kernel, linux-rt-devel, linux-perf-users,
loongarch, Mark Rutland, Ian Rogers, Peter Zijlstra,
Catalin Marinas, Oleg Nesterov, WANG Xuerui, Will Deacon,
Russell King, Alexander Shishkin, Ingo Molnar, Waiman Long,
Clark Williams, Luis Claudio R. Goncalves, James Clark,
Steven Rostedt, Arnaldo Carvalho de Melo, Namhyung Kim,
Chris Zankel, Max Filippov, Adrian Hunter, Jiri Olsa
Hi, Sebastian,
On Mon, Oct 12, 2026 at 5:04 PM Sebastian Andrzej Siewior
<bigeasy@linutronix.de> wrote:
>
> Waiman, Luis, Ada reported that HW breakpoints on ARM64 trigger
> "sleeping while atomic" warnings on PREEMPT_RT. The hardware event is
> delivered with disabled interrupts and perf intrastrucure expects
> disabled interrupts while the overflow callback is invoked.
>
> The callback then sends a SIGTRAP signal for which it acquires
> sighand_struct::siglock, a spinlock_t which becomes a sleeping lock and
> must not be acquired in atomic context.
>
> Delay the event callback until the return to userland.
> Add perf_hwbp_notify(), a generic perf callback which delayes the
> actual callback invocation to task_work_add() callback. This callback
> invokes the architecture defined callback perf_arch_hwbp_send_sig().
> This requires struct callback_head and the functions require
> ARCH_NEED_PERF_HW_NOTIFY to be defined.
>
> This was reported against ARM64. ARM, LongARCH and Xtensa follow the
> same pattern are also converted. Xtensa is the only not supporting
> PREEMPT_RT but now we have all architectures using the same pattern.
>
> Fixes: d8fccd9ca5f90 ("arm64: Allow to enable PREEMPT_RT.")
> Reported-by: Luis Claudio R. Goncalves <lgoncalv@redhat.com>
> Reported-by: Waiman Long <longman@redhat.com>
> Closes: https://lore.kernel.org/all/aho0eqjMESuHxECr@redhat.com/
> Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
> Tested-by: Ada Couprie Diaz <ada.coupriediaz@arm.com> (arm64)
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> ---
> v3…v4: https://lore.kernel.org/all/20261001143516.Ew8C97WS@linutronix.de/
> - Add fixes tag
> - Rename the functions as suggested by Ada to use the pattern that perf
> uses in other places:
> - perf_arch_hwbp_notify() -> perf_hwbp_notify() (generic)
> - arch_hwbp_send_sig() -> perf_arch_hwbp_send_sig() (arch specific)
> - perf_arch_hwbp_send_sig() -> perf_hwbp_send_sig() (generic)
> - Rename ARCH_NEED_PERF_HW_NOTIF -> ARCH_NEED_PERF_HW_NOTIFY
>
> v2…v3: https://lore.kernel.org/all/20260814085118.OPEA_Ssn@linutronix.de/
> - Add Xtensa for completion
> - sashiko complains and wants TWA_SIGNAL instead TWA_RESUME. His
> argument is that a syscall will trap via get_user() and loop forever
> instead making progress. This is wrong IMHO. ARM64 will single step
> over the watchpoint and continue execution. The only downside is that
> userland will get notified after the syscall completed. So my theory.
> Using TWA_SIGNAL is worse: Assume we have a watchpoint on UADDR and
> are in a futex() syscall. The get_user() invocation will trigger the
> exception, the debug handler will step over and queue a signal. The
> futex code will notice this and return ERESTARTNOINTR. A signal will
> be sent, the syscall restarts, traps onto UADDR again, the loop
> continues. But this should be case now, too…
> Now that I look into arch_build_bp_info() and do actual testing I must
> say arm64 does not support mixed breakpoints. This means there is no
> breakpoint in kernel on a userland address. \o/
>
> v1…v2: https://lore.kernel.org/all/20260713144939.FuCj9yvZ@linutronix.de/
> - sashiko complained that a memory breakpoint might trigger several
> times before a signal is sent if the syscall touches the memory (via
> get_user()) more than once before returning back. This would lead to
> list corruption in task_work_add(). To handle this, there is now a
> variable which is set via xchg before task_work_add() and cleared
> after the signal has been sent.
>
> arch/arm/include/asm/hw_breakpoint.h | 1 +
> arch/arm/kernel/ptrace.c | 7 ++----
> arch/arm64/include/asm/hw_breakpoint.h | 1 +
> arch/arm64/kernel/ptrace.c | 6 ++---
> arch/loongarch/include/asm/hw_breakpoint.h | 1 +
> arch/loongarch/kernel/ptrace.c | 6 ++---
> arch/xtensa/include/asm/hw_breakpoint.h | 1 +
> arch/xtensa/kernel/ptrace.c | 7 ++----
> include/linux/hw_breakpoint.h | 3 +++
> include/linux/perf_event.h | 4 ++++
> kernel/events/core.c | 26 ++++++++++++++++++++++
> 11 files changed, 45 insertions(+), 18 deletions(-)
>
> diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h
> index e7f9961c53b2d..fa7fb2b2a392a 100644
> --- a/arch/arm/include/asm/hw_breakpoint.h
> +++ b/arch/arm/include/asm/hw_breakpoint.h
> @@ -7,6 +7,7 @@
> struct task_struct;
>
> #ifdef CONFIG_HAVE_HW_BREAKPOINT
> +#define ARCH_NEED_PERF_HW_NOTIFY
>
> struct arch_hw_breakpoint_ctrl {
> u32 __reserved : 9,
> diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
> index ed7a2a87a6707..db4b28c6e15a4 100644
> --- a/arch/arm/kernel/ptrace.c
> +++ b/arch/arm/kernel/ptrace.c
> @@ -347,9 +347,7 @@ static long ptrace_hbp_idx_to_num(int idx)
> /*
> * Handle hitting a HW-breakpoint.
> */
> -static void ptrace_hbptriggered(struct perf_event *bp,
> - struct perf_sample_data *data,
> - struct pt_regs *regs)
> +void perf_arch_hwbp_send_sig(struct perf_event *bp)
> {
> struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
> long num;
> @@ -424,8 +422,7 @@ static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type)
> attr.bp_type = type;
> attr.disabled = 1;
>
> - return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL,
> - tsk);
> + return register_user_hw_breakpoint(&attr, perf_hwbp_notify, NULL, tsk);
> }
>
> static int ptrace_gethbpregs(struct task_struct *tsk, long num,
> diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
> index bd81cf17744af..0bc75f42b0da6 100644
> --- a/arch/arm64/include/asm/hw_breakpoint.h
> +++ b/arch/arm64/include/asm/hw_breakpoint.h
> @@ -124,6 +124,7 @@ extern void hw_breakpoint_pmu_read(struct perf_event *bp);
> extern int hw_breakpoint_slots(int type);
>
> #ifdef CONFIG_HAVE_HW_BREAKPOINT
> +#define ARCH_NEED_PERF_HW_NOTIFY
> extern void hw_breakpoint_thread_switch(struct task_struct *next);
> extern void ptrace_hw_copy_thread(struct task_struct *task);
> #else
> diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> index f743cbec1c3ad..dc3793c2a7c3c 100644
> --- a/arch/arm64/kernel/ptrace.c
> +++ b/arch/arm64/kernel/ptrace.c
> @@ -168,9 +168,7 @@ void ptrace_disable(struct task_struct *child)
> /*
> * Handle hitting a HW-breakpoint.
> */
> -static void ptrace_hbptriggered(struct perf_event *bp,
> - struct perf_sample_data *data,
> - struct pt_regs *regs)
> +void perf_arch_hwbp_send_sig(struct perf_event *bp)
> {
> struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
> const char *desc = "Hardware breakpoint trap (ptrace)";
> @@ -312,7 +310,7 @@ static struct perf_event *ptrace_hbp_create(unsigned int note_type,
> attr.bp_type = type;
> attr.disabled = 1;
>
> - bp = register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, tsk);
> + bp = register_user_hw_breakpoint(&attr, perf_hwbp_notify, NULL, tsk);
> if (IS_ERR(bp))
> return bp;
>
> diff --git a/arch/loongarch/include/asm/hw_breakpoint.h b/arch/loongarch/include/asm/hw_breakpoint.h
> index 5faa97a87a9e2..c2d061ff3763a 100644
> --- a/arch/loongarch/include/asm/hw_breakpoint.h
> +++ b/arch/loongarch/include/asm/hw_breakpoint.h
> @@ -120,6 +120,7 @@ void breakpoint_handler(struct pt_regs *regs);
> void watchpoint_handler(struct pt_regs *regs);
>
> #ifdef CONFIG_HAVE_HW_BREAKPOINT
> +#define ARCH_NEED_PERF_HW_NOTIFY
> extern void ptrace_hw_copy_thread(struct task_struct *task);
> extern void hw_breakpoint_thread_switch(struct task_struct *next);
> #else
> diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c
> index be38430f7e280..27e7e64591592 100644
> --- a/arch/loongarch/kernel/ptrace.c
> +++ b/arch/loongarch/kernel/ptrace.c
> @@ -384,9 +384,7 @@ static int lbt_set(struct task_struct *target,
> /*
> * Handle hitting a HW-breakpoint.
> */
> -static void ptrace_hbptriggered(struct perf_event *bp,
> - struct perf_sample_data *data,
> - struct pt_regs *regs)
> +void perf_arch_hwbp_send_sig(struct perf_event *bp)
> {
> int i;
> struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
> @@ -479,7 +477,7 @@ static struct perf_event *ptrace_hbp_create(unsigned int note_type,
> attr.bp_type = type;
> attr.disabled = 1;
>
> - bp = register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, tsk);
> + bp = register_user_hw_breakpoint(&attr, perf_hwbp_notify, NULL, tsk);
> if (IS_ERR(bp))
> return bp;
>
> diff --git a/arch/xtensa/include/asm/hw_breakpoint.h b/arch/xtensa/include/asm/hw_breakpoint.h
> index 9ec86f440a48e..44ea340c88742 100644
> --- a/arch/xtensa/include/asm/hw_breakpoint.h
> +++ b/arch/xtensa/include/asm/hw_breakpoint.h
> @@ -12,6 +12,7 @@
> #define __ASM_XTENSA_HW_BREAKPOINT_H
>
> #ifdef CONFIG_HAVE_HW_BREAKPOINT
> +#define ARCH_NEED_PERF_HW_NOTIFY
>
> #include <linux/kdebug.h>
> #include <linux/types.h>
> diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c
> index 364e4fdabb00d..c9f583d650de2 100644
> --- a/arch/xtensa/kernel/ptrace.c
> +++ b/arch/xtensa/kernel/ptrace.c
> @@ -361,9 +361,7 @@ static int ptrace_pokeusr(struct task_struct *child, long regno, long val)
> }
>
> #ifdef CONFIG_HAVE_HW_BREAKPOINT
> -static void ptrace_hbptriggered(struct perf_event *bp,
> - struct perf_sample_data *data,
> - struct pt_regs *regs)
> +void perf_arch_hwbp_send_sig(struct perf_event *bp)
> {
> int i;
> struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
> @@ -395,8 +393,7 @@ static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type)
> attr.bp_type = type;
> attr.disabled = 1;
>
> - return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL,
> - tsk);
> + return register_user_hw_breakpoint(&attr, perf_hwbp_notify, NULL, tsk);
> }
>
> /*
> diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
> index db199d653dd1a..91e8d80220c54 100644
> --- a/include/linux/hw_breakpoint.h
> +++ b/include/linux/hw_breakpoint.h
> @@ -85,6 +85,9 @@ extern int register_perf_hw_breakpoint(struct perf_event *bp);
> extern void unregister_hw_breakpoint(struct perf_event *bp);
> extern void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events);
> extern bool hw_breakpoint_is_used(void);
> +extern void perf_arch_hwbp_send_sig(struct perf_event *bp);
> +extern void perf_hwbp_notify(struct perf_event *bp, struct perf_sample_data *data,
> + struct pt_regs *regs);
Maybe this declaration can be put in one line because there is no 80
characters limit now.
Huacai
>
> extern int dbg_reserve_bp_slot(struct perf_event *bp);
> extern int dbg_release_bp_slot(struct perf_event *bp);
> diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
> index 2d5e7687875b0..07f2c858d50d1 100644
> --- a/include/linux/perf_event.h
> +++ b/include/linux/perf_event.h
> @@ -215,6 +215,10 @@ struct hw_perf_event {
>
> /* Last sync'ed generation of filters */
> unsigned long addr_filters_gen;
> +#ifdef ARCH_NEED_PERF_HW_NOTIFY
> + struct callback_head arch_hw_notif;
> + int arch_hw_notif_busy;
> +#endif
>
> /*
> * hw_perf_event::state flags; used to track the PERF_EF_* state.
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index 7846d70be57f6..619eab68d1dfd 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -13334,6 +13334,28 @@ static void account_event(struct perf_event *event)
> account_pmu_sb_event(event);
> }
>
> +#ifdef ARCH_NEED_PERF_HW_NOTIFY
> +static void perf_hwbp_send_sig(struct callback_head *head)
> +{
> + struct perf_event *bp;
> +
> + bp = container_of(head, struct perf_event, hw.arch_hw_notif);
> + perf_arch_hwbp_send_sig(bp);
> + xchg_relaxed(&bp->hw.arch_hw_notif_busy, 0);
> + put_event(bp);
> +}
> +
> +void perf_hwbp_notify(struct perf_event *bp, struct perf_sample_data *data,
> + struct pt_regs *regs)
> +{
> + if (WARN_ON_ONCE(!atomic_long_inc_not_zero(&bp->refcount)))
> + return;
> + if (xchg_relaxed(&bp->hw.arch_hw_notif_busy, 1) ||
> + WARN_ON_ONCE(task_work_add(current, &bp->hw.arch_hw_notif, TWA_RESUME)))
> + put_event(bp);
> +}
> +#endif
> +
> /*
> * Allocate and initialize an event structure
> */
> @@ -13439,6 +13461,10 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
> }
>
> if (overflow_handler) {
> +#ifdef ARCH_NEED_PERF_HW_NOTIFY
> + if (overflow_handler == perf_hwbp_notify)
> + init_task_work(&event->hw.arch_hw_notif, perf_hwbp_send_sig);
> +#endif
> event->overflow_handler = overflow_handler;
> event->overflow_handler_context = context;
> } else if (is_write_backward(event)){
> --
> 2.55.0
>
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH v4] ARM, ARM64, LONGARCH, XTENSA: Delay HW BP notification to task_work()
2026-10-12 10:15 ` Huacai Chen
@ 2026-10-12 10:21 ` Sebastian Andrzej Siewior
0 siblings, 0 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-10-12 10:21 UTC (permalink / raw)
To: Huacai Chen
Cc: linux-arm-kernel, linux-kernel, linux-rt-devel, linux-perf-users,
loongarch, Mark Rutland, Ian Rogers, Peter Zijlstra,
Catalin Marinas, Oleg Nesterov, WANG Xuerui, Will Deacon,
Russell King, Alexander Shishkin, Ingo Molnar, Waiman Long,
Clark Williams, Luis Claudio R. Goncalves, James Clark,
Steven Rostedt, Arnaldo Carvalho de Melo, Namhyung Kim,
Chris Zankel, Max Filippov, Adrian Hunter, Jiri Olsa
On 2026-10-12 18:15:51 [+0800], Huacai Chen wrote:
> Hi, Sebastian,
Hi Huacai,
> > diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
> > index db199d653dd1a..91e8d80220c54 100644
> > --- a/include/linux/hw_breakpoint.h
> > +++ b/include/linux/hw_breakpoint.h
> > @@ -85,6 +85,9 @@ extern int register_perf_hw_breakpoint(struct perf_event *bp);
> > extern void unregister_hw_breakpoint(struct perf_event *bp);
> > extern void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events);
> > extern bool hw_breakpoint_is_used(void);
> > +extern void perf_arch_hwbp_send_sig(struct perf_event *bp);
> > +extern void perf_hwbp_notify(struct perf_event *bp, struct perf_sample_data *data,
> > + struct pt_regs *regs);
> Maybe this declaration can be put in one line because there is no 80
> characters limit now.
We don't save much, do we? It looks more readable. Maybe if the perf
maintainer requests it. Looks like waste to repost it just for that.
Any comments on the looong bits?
> Huacai
Sebastian
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-12 10:21 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-12 9:04 [PATCH v4] ARM, ARM64, LONGARCH, XTENSA: Delay HW BP notification to task_work() Sebastian Andrzej Siewior
2026-10-12 9:18 ` sashiko-bot
2026-10-12 9:37 ` Sebastian Andrzej Siewior
2026-10-12 10:15 ` Huacai Chen
2026-10-12 10:21 ` Sebastian Andrzej Siewior
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®