[PATCH v9 06/10] x86/hw_breakpoint: Add arch_reinstall_hw_breakpoint
From: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
Date: 2026-07-17 14:20:47
Also in:
linux-doc, linux-perf-users, lkml
Subsystem:
the rest, x86 architecture (32-bit and 64-bit) · Maintainers:
Linus Torvalds, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen
From: Jinchao Wang <redacted>
The new arch_reinstall_hw_breakpoint() function can be used in an
atomic context, unlike the more expensive free and re-allocation path.
This allows callers to efficiently re-establish an existing breakpoint.
Signed-off-by: Jinchao Wang <redacted>
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v9:
- Update commit message.
- Temporarily disable the active slot in setup_hwbp() before updating
the address register to avoid spurious debug exceptions.
---
arch/x86/include/asm/hw_breakpoint.h | 2 ++
arch/x86/kernel/hw_breakpoint.c | 34 ++++++++++++++++++++++++++++------
2 files changed, 30 insertions(+), 6 deletions(-)
diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h
index aa6adac6c3a2..c22cc4e87fc5 100644
--- a/arch/x86/include/asm/hw_breakpoint.h
+++ b/arch/x86/include/asm/hw_breakpoint.h@@ -21,6 +21,7 @@ struct arch_hw_breakpoint { enum bp_slot_action { BP_SLOT_ACTION_INSTALL, + BP_SLOT_ACTION_REINSTALL, BP_SLOT_ACTION_UNINSTALL, };
@@ -65,6 +66,7 @@ extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, int arch_install_hw_breakpoint(struct perf_event *bp); +int arch_reinstall_hw_breakpoint(struct perf_event *bp); 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 c323c2aab2af..0df3ff556f47 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c@@ -100,6 +100,10 @@ static int manage_bp_slot(struct perf_event *bp, enum bp_slot_action action) old_bp = NULL; new_bp = bp; break; + case BP_SLOT_ACTION_REINSTALL: + old_bp = bp; + new_bp = bp; + break; case BP_SLOT_ACTION_UNINSTALL: old_bp = bp; new_bp = NULL;
@@ -129,23 +133,36 @@ static int manage_bp_slot(struct perf_event *bp, enum bp_slot_action action) static void setup_hwbp(struct arch_hw_breakpoint *info, int slot, bool enable) { unsigned long dr7; - - set_debugreg(info->address, slot); - __this_cpu_write(cpu_debugreg[slot], info->address); + bool enabled; dr7 = this_cpu_read(cpu_dr7); + enabled = dr7 & ((DR_LOCAL_ENABLE | DR_GLOBAL_ENABLE) << (slot * DR_ENABLE_SIZE)); dr7 &= ~(__encode_dr7(slot, 0xc, 0x3) | (DR_LOCAL_ENABLE << (slot * DR_ENABLE_SIZE))); - if (enable) - dr7 |= encode_dr7(slot, info->len, info->type); + + /* + * If the slot is currently enabled, disable it first before updating + * the address register to prevent spurious debug exceptions. + */ + if (enable && enabled) { + barrier(); + set_debugreg(dr7, 7); + barrier(); + this_cpu_write(cpu_dr7, dr7); + } + + set_debugreg(info->address, slot); + __this_cpu_write(cpu_debugreg[slot], info->address); /* * Enabling: * 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. */ - if (enable) + if (enable) { + dr7 |= encode_dr7(slot, info->len, info->type); this_cpu_write(cpu_dr7, dr7); + } barrier();
@@ -189,6 +206,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp) return arch_manage_bp(bp, BP_SLOT_ACTION_INSTALL); } +int arch_reinstall_hw_breakpoint(struct perf_event *bp) +{ + return arch_manage_bp(bp, BP_SLOT_ACTION_REINSTALL); +} + void arch_uninstall_hw_breakpoint(struct perf_event *bp) { arch_manage_bp(bp, BP_SLOT_ACTION_UNINSTALL);