* [PATCH 0/3] hw_breakpoint: Fix and simplify local address updates for wprobe
[not found] <178476143000.26117.9810509272687580410.stgit@devnote2>
@ 2026-07-26 12:22 ` Jinchao Wang
2026-07-26 12:22 ` [PATCH 1/3] x86/hw_breakpoints: Make DR7 updates NMI safe Jinchao Wang
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Jinchao Wang @ 2026-07-26 12:22 UTC (permalink / raw)
To: Masami Hiramatsu, Peter Zijlstra, Thomas Gleixner, Steven Rostedt,
x86
Cc: Ingo Molnar, Borislav Petkov, Dave Hansen, H . Peter Anvin,
Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Ian Rogers, Mathieu Desnoyers, Tony Luck,
linux-kernel, linux-trace-kernel, linux-perf-users, linux-edac,
Jinchao Wang
Hi Masami,
While reviewing wprobe v10, I prepared three patches to fix and simplify
the HWBP support required by wprobe.
This series is based on linux-trace/probes/for-next, the same base used by
wprobe v10. It is intended to replace patches 5-7 in v10, so it should not
be applied on top of topic/wprobe-v2.
Patch 1 fixes the x86 HWBP slot and DR7 races when an operation is
interrupted by an NMI.
Patch 2 adds arch_modify_local_hw_breakpoint_addr(), which updates only
the watched address of an installed breakpoint on the local CPU.
Patch 3 adds the matching generic modify_local_hw_breakpoint_addr() API.
It keeps the original patch structure and Masami's authorship, while
removing attribute parsing, type/length updates and rollback.
With the address-only interface, the install/uninstall refactoring in the
old patch 5 is no longer needed.
I can take care of follow-up maintenance related to fast local HWBP
watched-address updates.
The patches passed checkpatch and the relevant x86_32 and x86_64 object
builds.
Jinchao Wang (2):
x86/hw_breakpoints: Make DR7 updates NMI safe
x86/hw_breakpoints: Add arch_modify_local_hw_breakpoint_addr() API
Masami Hiramatsu (Google) (1):
HWBP: Add modify_local_hw_breakpoint_addr() API
arch/Kconfig | 8 ++
arch/x86/Kconfig | 1 +
arch/x86/include/asm/debugreg.h | 36 ++++++---
arch/x86/include/asm/hw_breakpoint.h | 2 +
arch/x86/kernel/cpu/mce/core.c | 20 +++--
arch/x86/kernel/hw_breakpoint.c | 114 ++++++++++++++++-----------
arch/x86/kernel/nmi.c | 10 ++-
arch/x86/kernel/traps.c | 10 ++-
include/linux/hw_breakpoint.h | 6 ++
kernel/events/hw_breakpoint.c | 30 +++++++
10 files changed, 166 insertions(+), 71 deletions(-)
base-commit: 1a416ae446afa42d2d8500ce25bd61c564508721
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/3] x86/hw_breakpoints: Make DR7 updates NMI safe
2026-07-26 12:22 ` [PATCH 0/3] hw_breakpoint: Fix and simplify local address updates for wprobe Jinchao Wang
@ 2026-07-26 12:22 ` Jinchao Wang
2026-07-26 12:22 ` [PATCH 2/3] x86/hw_breakpoints: Add arch_modify_local_hw_breakpoint_addr() API Jinchao Wang
2026-07-26 12:22 ` [PATCH 3/3] HWBP: Add modify_local_hw_breakpoint_addr() API Jinchao Wang
2 siblings, 0 replies; 5+ messages in thread
From: Jinchao Wang @ 2026-07-26 12:22 UTC (permalink / raw)
To: Masami Hiramatsu, Peter Zijlstra, Thomas Gleixner, Steven Rostedt,
x86
Cc: Ingo Molnar, Borislav Petkov, Dave Hansen, H . Peter Anvin,
Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Ian Rogers, Mathieu Desnoyers, Tony Luck,
linux-kernel, linux-trace-kernel, linux-perf-users, linux-edac,
Jinchao Wang
Hardware breakpoint installation and removal run with IRQs disabled, but
an NMI can still enter the same code through KGDB. The interrupted
operation and the NMI can consequently claim the same slot or overwrite
each other's DR7 state.
Claim and release per-CPU slots with cmpxchg. Update cpu_dr7 with
single-instruction per-CPU operations, and preserve hardware-first
disable and hardware-last enable ordering. Add a per-CPU sequence number
so interrupted DR7 writers and restore paths detect an NMI update and
retry from the latest shadow state.
Signed-off-by: Jinchao Wang <wangjinchao600@gmail.com>
---
arch/x86/include/asm/debugreg.h | 36 ++++++++----
arch/x86/kernel/cpu/mce/core.c | 20 ++++---
arch/x86/kernel/hw_breakpoint.c | 98 ++++++++++++++++-----------------
arch/x86/kernel/nmi.c | 10 +++-
arch/x86/kernel/traps.c | 10 +++-
5 files changed, 101 insertions(+), 73 deletions(-)
diff --git a/arch/x86/include/asm/debugreg.h b/arch/x86/include/asm/debugreg.h
index a2c1f2d24b64..b1fe1c47978d 100644
--- a/arch/x86/include/asm/debugreg.h
+++ b/arch/x86/include/asm/debugreg.h
@@ -18,6 +18,7 @@
#define DR7_FIXED_1 0x00000400
DECLARE_PER_CPU(unsigned long, cpu_dr7);
+DECLARE_PER_CPU(unsigned int, cpu_dr7_seq);
#ifndef CONFIG_PARAVIRT_XXL
/*
@@ -125,18 +126,20 @@ static __always_inline bool hw_breakpoint_active(void)
extern void hw_breakpoint_restore(void);
-static __always_inline unsigned long local_db_save(void)
+static __always_inline void local_db_save(unsigned long *dr7,
+ unsigned int *dr7_seq)
{
- unsigned long dr7;
+ *dr7 = 0;
+ *dr7_seq = this_cpu_read(cpu_dr7_seq);
if (static_cpu_has(X86_FEATURE_HYPERVISOR) && !hw_breakpoint_active())
- return 0;
+ return;
- get_debugreg(dr7, 7);
+ get_debugreg(*dr7, 7);
/* Architecturally set bit */
- dr7 &= ~DR7_FIXED_1;
- if (dr7)
+ *dr7 &= ~DR7_FIXED_1;
+ if (*dr7)
set_debugreg(DR7_FIXED_1, 7);
/*
@@ -145,20 +148,33 @@ static __always_inline unsigned long local_db_save(void)
* be good.
*/
barrier();
-
- return dr7;
}
-static __always_inline void local_db_restore(unsigned long dr7)
+static __always_inline void local_db_restore(unsigned long dr7,
+ unsigned int dr7_seq)
{
+ unsigned int seq;
+
/*
* Ensure the compiler doesn't raise this statement into
* the critical section; enabling breakpoints early would
* not be good.
*/
barrier();
- if (dr7)
+
+ do {
+ seq = this_cpu_read(cpu_dr7_seq);
+ if (seq == dr7_seq) {
+ if (!dr7)
+ return;
+ } else {
+ dr7 = this_cpu_read(cpu_dr7);
+ if (!dr7)
+ dr7 = DR7_FIXED_1;
+ }
+
set_debugreg(dr7, 7);
+ } while (unlikely(seq != this_cpu_read(cpu_dr7_seq)));
}
#ifdef CONFIG_CPU_SUP_AMD
diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c
index 9bba1e2f03af..8dba9cd04bfa 100644
--- a/arch/x86/kernel/cpu/mce/core.c
+++ b/arch/x86/kernel/cpu/mce/core.c
@@ -2139,20 +2139,22 @@ static __always_inline void exc_machine_check_user(struct pt_regs *regs)
DEFINE_IDTENTRY_MCE(exc_machine_check)
{
unsigned long dr7;
+ unsigned int dr7_seq;
- dr7 = local_db_save();
+ local_db_save(&dr7, &dr7_seq);
exc_machine_check_kernel(regs);
- local_db_restore(dr7);
+ local_db_restore(dr7, dr7_seq);
}
/* The user mode variant. */
DEFINE_IDTENTRY_MCE_USER(exc_machine_check)
{
unsigned long dr7;
+ unsigned int dr7_seq;
- dr7 = local_db_save();
+ local_db_save(&dr7, &dr7_seq);
exc_machine_check_user(regs);
- local_db_restore(dr7);
+ local_db_restore(dr7, dr7_seq);
}
#ifdef CONFIG_X86_FRED
@@ -2170,13 +2172,14 @@ DEFINE_IDTENTRY_MCE_USER(exc_machine_check)
DEFINE_FREDENTRY_MCE(exc_machine_check)
{
unsigned long dr7;
+ unsigned int dr7_seq;
- dr7 = local_db_save();
+ local_db_save(&dr7, &dr7_seq);
if (user_mode(regs))
exc_machine_check_user(regs);
else
exc_machine_check_kernel(regs);
- local_db_restore(dr7);
+ local_db_restore(dr7, dr7_seq);
}
#endif
#else
@@ -2184,13 +2187,14 @@ DEFINE_FREDENTRY_MCE(exc_machine_check)
DEFINE_IDTENTRY_RAW(exc_machine_check)
{
unsigned long dr7;
+ unsigned int dr7_seq;
- dr7 = local_db_save();
+ local_db_save(&dr7, &dr7_seq);
if (user_mode(regs))
exc_machine_check_user(regs);
else
exc_machine_check_kernel(regs);
- local_db_restore(dr7);
+ local_db_restore(dr7, dr7_seq);
}
#endif
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index f846c15f21ca..9ef24b55737f 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -40,6 +40,9 @@
DEFINE_PER_CPU(unsigned long, cpu_dr7);
EXPORT_PER_CPU_SYMBOL(cpu_dr7);
+/* Sequence number of the per-CPU DR7 state. */
+DEFINE_PER_CPU(unsigned int, cpu_dr7_seq);
+
/* Per cpu debug address registers values */
static DEFINE_PER_CPU(unsigned long, cpu_debugreg[HBP_NUM]);
@@ -97,38 +100,30 @@ int decode_dr7(unsigned long dr7, int bpnum, unsigned *len, unsigned *type)
int arch_install_hw_breakpoint(struct perf_event *bp)
{
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
- unsigned long *dr7;
+ unsigned int seq;
int i;
lockdep_assert_irqs_disabled();
for (i = 0; i < HBP_NUM; i++) {
- struct perf_event **slot = this_cpu_ptr(&bp_per_reg[i]);
-
- if (!*slot) {
- *slot = bp;
+ if (!this_cpu_cmpxchg(bp_per_reg[i], NULL, bp))
break;
- }
}
if (WARN_ONCE(i == HBP_NUM, "Can't find any breakpoint slot"))
return -EBUSY;
- set_debugreg(info->address, i);
- __this_cpu_write(cpu_debugreg[i], info->address);
-
- dr7 = this_cpu_ptr(&cpu_dr7);
- *dr7 |= encode_dr7(i, info->len, info->type);
-
- /*
- * Ensure we first write cpu_dr7 before we set the DR7 register.
- * This ensures an NMI never see cpu_dr7 0 when DR7 is not.
- */
- barrier();
-
- set_debugreg(*dr7, 7);
- if (info->mask)
- amd_set_dr_addr_mask(info->mask, i);
+ do {
+ seq = this_cpu_inc_return(cpu_dr7_seq);
+ this_cpu_write(cpu_debugreg[i], info->address);
+ barrier();
+ set_debugreg(info->address, i);
+ if (info->mask)
+ amd_set_dr_addr_mask(info->mask, i);
+ this_cpu_or(cpu_dr7, encode_dr7(i, info->len, info->type));
+ barrier();
+ set_debugreg(this_cpu_read(cpu_dr7), 7);
+ } while (seq != this_cpu_read(cpu_dr7_seq));
return 0;
}
@@ -146,36 +141,33 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
{
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
unsigned long dr7;
+ unsigned int seq;
int i;
lockdep_assert_irqs_disabled();
for (i = 0; i < HBP_NUM; i++) {
- struct perf_event **slot = this_cpu_ptr(&bp_per_reg[i]);
-
- if (*slot == bp) {
- *slot = NULL;
+ if (this_cpu_read(bp_per_reg[i]) == bp)
break;
- }
}
if (WARN_ONCE(i == HBP_NUM, "Can't find any breakpoint slot"))
return;
- dr7 = this_cpu_read(cpu_dr7);
- dr7 &= ~__encode_dr7(i, info->len, info->type);
-
- set_debugreg(dr7, 7);
- if (info->mask)
- amd_set_dr_addr_mask(0, i);
-
- /*
- * Ensure the write to cpu_dr7 is after we've set the DR7 register.
- * This ensures an NMI never see cpu_dr7 0 when DR7 is not.
- */
- barrier();
-
- this_cpu_write(cpu_dr7, dr7);
+ do {
+ seq = this_cpu_inc_return(cpu_dr7_seq);
+ dr7 = this_cpu_read(cpu_dr7);
+ dr7 &= ~__encode_dr7(i, info->len, info->type);
+ set_debugreg(dr7, 7);
+ if (info->mask)
+ amd_set_dr_addr_mask(0, i);
+ barrier();
+ this_cpu_and(cpu_dr7,
+ ~__encode_dr7(i, info->len, info->type));
+ } while (seq != this_cpu_read(cpu_dr7_seq));
+
+ WARN_ONCE(this_cpu_cmpxchg(bp_per_reg[i], bp, NULL) != bp,
+ "Can't release breakpoint slot");
}
static int arch_bp_generic_len(int x86_len)
@@ -309,13 +301,14 @@ static inline bool within_cpu_entry(unsigned long addr, unsigned long end)
sizeof(struct tlb_state)))
return true;
- /*
- * When in guest (X86_FEATURE_HYPERVISOR), local_db_save()
- * will read per-cpu cpu_dr7 before clear dr7 register.
- */
+ /* local_db_save() reads this state before clearing DR7. */
if (within_area(addr, end, (unsigned long)&per_cpu(cpu_dr7, cpu),
sizeof(cpu_dr7)))
return true;
+ if (within_area(addr, end,
+ (unsigned long)&per_cpu(cpu_dr7_seq, cpu),
+ sizeof(cpu_dr7_seq)))
+ return true;
}
return false;
@@ -483,12 +476,17 @@ void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
void hw_breakpoint_restore(void)
{
- set_debugreg(__this_cpu_read(cpu_debugreg[0]), 0);
- set_debugreg(__this_cpu_read(cpu_debugreg[1]), 1);
- set_debugreg(__this_cpu_read(cpu_debugreg[2]), 2);
- set_debugreg(__this_cpu_read(cpu_debugreg[3]), 3);
- set_debugreg(DR6_RESERVED, 6);
- set_debugreg(__this_cpu_read(cpu_dr7), 7);
+ unsigned int seq;
+
+ do {
+ seq = this_cpu_inc_return(cpu_dr7_seq);
+ set_debugreg(this_cpu_read(cpu_debugreg[0]), 0);
+ set_debugreg(this_cpu_read(cpu_debugreg[1]), 1);
+ set_debugreg(this_cpu_read(cpu_debugreg[2]), 2);
+ set_debugreg(this_cpu_read(cpu_debugreg[3]), 3);
+ set_debugreg(DR6_RESERVED, 6);
+ set_debugreg(this_cpu_read(cpu_dr7), 7);
+ } while (seq != this_cpu_read(cpu_dr7_seq));
}
EXPORT_SYMBOL_FOR_KVM(hw_breakpoint_restore);
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index 3c9f60d6ca5a..f55a0cbd5927 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -532,10 +532,13 @@ enum nmi_states {
static DEFINE_PER_CPU(enum nmi_states, nmi_state);
static DEFINE_PER_CPU(unsigned long, nmi_cr2);
static DEFINE_PER_CPU(unsigned long, nmi_dr7);
+static DEFINE_PER_CPU(unsigned int, nmi_dr7_seq);
DEFINE_IDTENTRY_RAW(exc_nmi)
{
irqentry_state_t irq_state;
+ unsigned long dr7;
+ unsigned int dr7_seq;
struct nmi_stats *nsp = this_cpu_ptr(&nmi_stats);
/*
@@ -572,7 +575,9 @@ DEFINE_IDTENTRY_RAW(exc_nmi)
*/
sev_es_ist_enter(regs);
- this_cpu_write(nmi_dr7, local_db_save());
+ local_db_save(&dr7, &dr7_seq);
+ this_cpu_write(nmi_dr7, dr7);
+ this_cpu_write(nmi_dr7_seq, dr7_seq);
irq_state = irqentry_nmi_enter(regs);
@@ -594,7 +599,8 @@ DEFINE_IDTENTRY_RAW(exc_nmi)
irqentry_nmi_exit(regs, irq_state);
- local_db_restore(this_cpu_read(nmi_dr7));
+ local_db_restore(this_cpu_read(nmi_dr7),
+ this_cpu_read(nmi_dr7_seq));
sev_es_ist_exit();
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 30aa8369957e..018abe736285 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -1231,8 +1231,12 @@ static noinstr void exc_debug_kernel(struct pt_regs *regs, unsigned long dr6)
* it results in an endless recursion and stack overflow. Thus we stay
* with the IDT approach, i.e., save DR7 and disable #DB.
*/
- unsigned long dr7 = local_db_save();
- irqentry_state_t irq_state = irqentry_nmi_enter(regs);
+ unsigned long dr7;
+ unsigned int dr7_seq;
+ irqentry_state_t irq_state;
+
+ local_db_save(&dr7, &dr7_seq);
+ irq_state = irqentry_nmi_enter(regs);
instrumentation_begin();
/*
@@ -1289,7 +1293,7 @@ static noinstr void exc_debug_kernel(struct pt_regs *regs, unsigned long dr6)
instrumentation_end();
irqentry_nmi_exit(regs, irq_state);
- local_db_restore(dr7);
+ local_db_restore(dr7, dr7_seq);
}
static noinstr void exc_debug_user(struct pt_regs *regs, unsigned long dr6)
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/3] x86/hw_breakpoints: Add arch_modify_local_hw_breakpoint_addr() API
2026-07-26 12:22 ` [PATCH 0/3] hw_breakpoint: Fix and simplify local address updates for wprobe Jinchao Wang
2026-07-26 12:22 ` [PATCH 1/3] x86/hw_breakpoints: Make DR7 updates NMI safe Jinchao Wang
@ 2026-07-26 12:22 ` Jinchao Wang
2026-07-26 12:22 ` [PATCH 3/3] HWBP: Add modify_local_hw_breakpoint_addr() API Jinchao Wang
2 siblings, 0 replies; 5+ messages in thread
From: Jinchao Wang @ 2026-07-26 12:22 UTC (permalink / raw)
To: Masami Hiramatsu, Peter Zijlstra, Thomas Gleixner, Steven Rostedt,
x86
Cc: Ingo Molnar, Borislav Petkov, Dave Hansen, H . Peter Anvin,
Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Ian Rogers, Mathieu Desnoyers, Tony Luck,
linux-kernel, linux-trace-kernel, linux-perf-users, linux-edac,
Jinchao Wang
Wprobe needs to move an active per-CPU watchpoint without releasing and
reserving its hardware slot.
Add arch_modify_local_hw_breakpoint_addr() to find the installed local
slot and update only its address shadow and hardware debug address
register. Publish the shadow first so hw_breakpoint_restore() observes
the new address if an NMI interrupts the update.
The caller must provide an installed local event and a valid address.
Slot ownership, breakpoint type, length, mask and DR7 remain unchanged.
Signed-off-by: Jinchao Wang <wangjinchao600@gmail.com>
---
arch/x86/include/asm/hw_breakpoint.h | 2 ++
arch/x86/kernel/hw_breakpoint.c | 20 ++++++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h
index 0bc931cd0698..a1ecce536074 100644
--- a/arch/x86/include/asm/hw_breakpoint.h
+++ b/arch/x86/include/asm/hw_breakpoint.h
@@ -59,6 +59,8 @@ extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
int arch_install_hw_breakpoint(struct perf_event *bp);
+void arch_modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr);
void arch_uninstall_hw_breakpoint(struct perf_event *bp);
void hw_breakpoint_pmu_read(struct perf_event *bp);
void hw_breakpoint_pmu_unthrottle(struct perf_event *bp);
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index 9ef24b55737f..ff1b9b78f78c 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -128,6 +128,26 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
return 0;
}
+void arch_modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr)
+{
+ int i;
+
+ lockdep_assert_irqs_disabled();
+
+ for (i = 0; i < HBP_NUM; i++) {
+ if (this_cpu_read(bp_per_reg[i]) == bp)
+ break;
+ }
+
+ if (WARN_ONCE(i == HBP_NUM, "Can't find any breakpoint slot"))
+ return;
+
+ this_cpu_write(cpu_debugreg[i], addr);
+ barrier();
+ set_debugreg(addr, i);
+}
+
/*
* Uninstall the breakpoint contained in the given counter.
*
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/3] HWBP: Add modify_local_hw_breakpoint_addr() API
2026-07-26 12:22 ` [PATCH 0/3] hw_breakpoint: Fix and simplify local address updates for wprobe Jinchao Wang
2026-07-26 12:22 ` [PATCH 1/3] x86/hw_breakpoints: Make DR7 updates NMI safe Jinchao Wang
2026-07-26 12:22 ` [PATCH 2/3] x86/hw_breakpoints: Add arch_modify_local_hw_breakpoint_addr() API Jinchao Wang
@ 2026-07-26 12:22 ` Jinchao Wang
2026-07-26 12:27 ` Jinchao Wang
2 siblings, 1 reply; 5+ messages in thread
From: Jinchao Wang @ 2026-07-26 12:22 UTC (permalink / raw)
To: Masami Hiramatsu, Peter Zijlstra, Thomas Gleixner, Steven Rostedt,
x86
Cc: Ingo Molnar, Borislav Petkov, Dave Hansen, H . Peter Anvin,
Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Ian Rogers, Mathieu Desnoyers, Tony Luck,
linux-kernel, linux-trace-kernel, linux-perf-users, linux-edac,
Jinchao Wang
From: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
Add modify_local_hw_breakpoint_addr() to update only the watched
address of an installed hardware breakpoint on the local CPU without
releasing and reserving its hardware slot. This is available when the
architecture selects CONFIG_HAVE_MODIFY_LOCAL_HW_BREAKPOINT_ADDR.
The caller must provide an installed local event and a valid address,
and update other CPUs separately.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Jinchao Wang <wangjinchao600@gmail.com>
---
arch/Kconfig | 8 ++++++++
arch/x86/Kconfig | 1 +
include/linux/hw_breakpoint.h | 6 ++++++
kernel/events/hw_breakpoint.c | 30 ++++++++++++++++++++++++++++++
4 files changed, 45 insertions(+)
diff --git a/arch/Kconfig b/arch/Kconfig
index fa7507ac8e13..bea383408e32 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -457,6 +457,14 @@ config HAVE_MIXED_BREAKPOINTS_REGS
Select this option if your arch implements breakpoints under the
latter fashion.
+config HAVE_MODIFY_LOCAL_HW_BREAKPOINT_ADDR
+ bool
+ depends on HAVE_HW_BREAKPOINT
+ help
+ Select this if the architecture can modify the address of an
+ installed hardware breakpoint on the local CPU without releasing
+ and reserving its hardware slot.
+
config HAVE_USER_RETURN_NOTIFIER
bool
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index bdad90f210e4..09e599f2d0dd 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -246,6 +246,7 @@ config X86
select HAVE_FUNCTION_TRACER
select HAVE_GCC_PLUGINS
select HAVE_HW_BREAKPOINT
+ select HAVE_MODIFY_LOCAL_HW_BREAKPOINT_ADDR
select HAVE_IOREMAP_PROT
select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64
select HAVE_IRQ_TIME_ACCOUNTING
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
index db199d653dd1..bf65c7fffd99 100644
--- a/include/linux/hw_breakpoint.h
+++ b/include/linux/hw_breakpoint.h
@@ -81,6 +81,9 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr,
perf_overflow_handler_t triggered,
void *context);
+int modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr);
+
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);
@@ -124,6 +127,9 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr,
perf_overflow_handler_t triggered,
void *context) { return NULL; }
static inline int
+modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr) { return -EOPNOTSUPP; }
+static inline int
register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
static inline void unregister_hw_breakpoint(struct perf_event *bp) { }
static inline void
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c
index 789add0c185a..0b1fc01f1934 100644
--- a/kernel/events/hw_breakpoint.c
+++ b/kernel/events/hw_breakpoint.c
@@ -888,6 +888,36 @@ void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events)
}
EXPORT_SYMBOL_GPL(unregister_wide_hw_breakpoint);
+/**
+ * modify_local_hw_breakpoint_addr - update a local breakpoint address
+ * @bp: the hwbp perf event for this CPU
+ * @addr: the new address for @bp
+ *
+ * Update only the address of an installed breakpoint on the local CPU without
+ * releasing and reserving its hardware slot. The caller must update other CPUs.
+ * Return 0, or -EOPNOTSUPP if the architecture does not support this operation.
+ */
+#ifdef CONFIG_HAVE_MODIFY_LOCAL_HW_BREAKPOINT_ADDR
+int modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr)
+{
+ lockdep_assert_irqs_disabled();
+
+ counter_arch_bp(bp)->address = addr;
+ bp->attr.bp_addr = addr;
+ arch_modify_local_hw_breakpoint_addr(bp, addr);
+
+ return 0;
+}
+#else
+int modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+EXPORT_SYMBOL_GPL(modify_local_hw_breakpoint_addr);
+
/**
* hw_breakpoint_is_used - check if breakpoints are currently used
*
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/3] HWBP: Add modify_local_hw_breakpoint_addr() API
2026-07-26 12:22 ` [PATCH 3/3] HWBP: Add modify_local_hw_breakpoint_addr() API Jinchao Wang
@ 2026-07-26 12:27 ` Jinchao Wang
0 siblings, 0 replies; 5+ messages in thread
From: Jinchao Wang @ 2026-07-26 12:27 UTC (permalink / raw)
To: Masami Hiramatsu, Peter Zijlstra, Thomas Gleixner, Steven Rostedt,
x86
Cc: Ingo Molnar, Borislav Petkov, Dave Hansen, H . Peter Anvin,
Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Ian Rogers, Mathieu Desnoyers, Tony Luck,
linux-kernel, linux-trace-kernel, linux-perf-users, linux-edac,
Jinchao Wang
From: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
Add modify_local_hw_breakpoint_addr() to update only the watched
address of an installed hardware breakpoint on the local CPU without
releasing and reserving its hardware slot. This is available when the
architecture selects CONFIG_HAVE_MODIFY_LOCAL_HW_BREAKPOINT_ADDR.
The caller must provide an installed local event and a valid address,
and update other CPUs separately.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Jinchao Wang <wangjinchao600@gmail.com>
---
arch/Kconfig | 8 ++++++++
arch/x86/Kconfig | 1 +
include/linux/hw_breakpoint.h | 6 ++++++
kernel/events/hw_breakpoint.c | 30 ++++++++++++++++++++++++++++++
4 files changed, 45 insertions(+)
diff --git a/arch/Kconfig b/arch/Kconfig
index fa7507ac8e13..bea383408e32 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -457,6 +457,14 @@ config HAVE_MIXED_BREAKPOINTS_REGS
Select this option if your arch implements breakpoints under the
latter fashion.
+config HAVE_MODIFY_LOCAL_HW_BREAKPOINT_ADDR
+ bool
+ depends on HAVE_HW_BREAKPOINT
+ help
+ Select this if the architecture can modify the address of an
+ installed hardware breakpoint on the local CPU without releasing
+ and reserving its hardware slot.
+
config HAVE_USER_RETURN_NOTIFIER
bool
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index bdad90f210e4..09e599f2d0dd 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -246,6 +246,7 @@ config X86
select HAVE_FUNCTION_TRACER
select HAVE_GCC_PLUGINS
select HAVE_HW_BREAKPOINT
+ select HAVE_MODIFY_LOCAL_HW_BREAKPOINT_ADDR
select HAVE_IOREMAP_PROT
select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64
select HAVE_IRQ_TIME_ACCOUNTING
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
index db199d653dd1..bf65c7fffd99 100644
--- a/include/linux/hw_breakpoint.h
+++ b/include/linux/hw_breakpoint.h
@@ -81,6 +81,9 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr,
perf_overflow_handler_t triggered,
void *context);
+int modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr);
+
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);
@@ -124,6 +127,9 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr,
perf_overflow_handler_t triggered,
void *context) { return NULL; }
static inline int
+modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr) { return -EOPNOTSUPP; }
+static inline int
register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
static inline void unregister_hw_breakpoint(struct perf_event *bp) { }
static inline void
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c
index 789add0c185a..0b1fc01f1934 100644
--- a/kernel/events/hw_breakpoint.c
+++ b/kernel/events/hw_breakpoint.c
@@ -888,6 +888,36 @@ void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events)
}
EXPORT_SYMBOL_GPL(unregister_wide_hw_breakpoint);
+/**
+ * modify_local_hw_breakpoint_addr - update a local breakpoint address
+ * @bp: the hwbp perf event for this CPU
+ * @addr: the new address for @bp
+ *
+ * Update only the address of an installed breakpoint on the local CPU without
+ * releasing and reserving its hardware slot. The caller must update other CPUs.
+ * Return 0, or -EOPNOTSUPP if the architecture does not support this operation.
+ */
+#ifdef CONFIG_HAVE_MODIFY_LOCAL_HW_BREAKPOINT_ADDR
+int modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr)
+{
+ lockdep_assert_irqs_disabled();
+
+ counter_arch_bp(bp)->address = addr;
+ bp->attr.bp_addr = addr;
+ arch_modify_local_hw_breakpoint_addr(bp, addr);
+
+ return 0;
+}
+#else
+int modify_local_hw_breakpoint_addr(struct perf_event *bp,
+ unsigned long addr)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+EXPORT_SYMBOL_GPL(modify_local_hw_breakpoint_addr);
+
/**
* hw_breakpoint_is_used - check if breakpoints are currently used
*
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-26 12:28 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <178476143000.26117.9810509272687580410.stgit@devnote2>
2026-07-26 12:22 ` [PATCH 0/3] hw_breakpoint: Fix and simplify local address updates for wprobe Jinchao Wang
2026-07-26 12:22 ` [PATCH 1/3] x86/hw_breakpoints: Make DR7 updates NMI safe Jinchao Wang
2026-07-26 12:22 ` [PATCH 2/3] x86/hw_breakpoints: Add arch_modify_local_hw_breakpoint_addr() API Jinchao Wang
2026-07-26 12:22 ` [PATCH 3/3] HWBP: Add modify_local_hw_breakpoint_addr() API Jinchao Wang
2026-07-26 12:27 ` Jinchao Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox