From: "Masami Hiramatsu (Google)" <[email protected]> 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) <[email protected]> Signed-off-by: Jinchao Wang <[email protected]> --- 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
