summaryrefslogtreecommitdiff
path: root/tools/lib/python
diff options
context:
space:
mode:
authorTiezhu Yang <yangtiezhu@loongson.cn>2026-10-27 22:51:35 +0800
committerHuacai Chen <chenhuacai@loongson.cn>2026-10-27 22:51:35 +0800
commit5e0b00fd000a53a5428fed6c96be74019bd0ad17 (patch)
tree7e8ad086a40355d342e04d2c2351c8fa1013292a /tools/lib/python
parentf5c0f8427469e703465c719c4304ce91282a32eb (diff)
LoongArch: Fix one-shot limitation for perf hardware breakpoints
In breakpoint_handler() and watchpoint_handler(), the code disables the hardware slot by executing update_bp_registers(regs, 0, ...). This design forces the active hardware breakpoint configuration to be wiped out upon its first hit, turning standard perf hardware breakpoints into "one-shot" events. Furthermore, while the ptrace single-step path in do_watch() executes a hardware single-step skip mechanism to advance the PC, the standard perf path lacks any mechanism to bypass the original triggering instruction. To maintain the long-term persistence of hardware breakpoints for perf usage, eliminate the disruptive calls to update_bp_registers() within the handler loops to keep the breakpoint configuration enabled. Concurrently, execute a single, unified register write outside the loop to atomize the state transition and explicitly enforce the hardware skip mechanism once per exception return, ensuring the processor safely steps forward without lockups. Meanwhile, in order to prevent status contamination during single-core context switches or cross-core thread migrations, the hardware register skip state of the previous thread is saved first. Then, the skip state is restored and its software skip flag is also cleared only if the next thread has the skip flag; otherwise, 0 is written to clear the FWPS and MWPS registers, which explicitly clears the skip bit according to the architectural specification. Additionally, explicitly clear hbp_break_skip and hbp_watch_skip inside ptrace_hw_copy_thread() which is called by copy_thread() during the task creation. This guarantees that a newly cloned child task starts with a clean slate. Here is a user-space reproducer to demonstrate the issue. (1) Test program (test_perf_continuous.c): #include <stdio.h> #include <unistd.h> #include <sys/syscall.h> #include <sys/ioctl.h> #include <linux/perf_event.h> #include <linux/hw_breakpoint.h> static int var = 0; int main(void) { size_t count = 0; struct perf_event_attr attr = { .type = PERF_TYPE_BREAKPOINT, .size = sizeof(attr), .bp_type = HW_BREAKPOINT_W, .bp_addr = (unsigned long)&var, .bp_len = HW_BREAKPOINT_LEN_1, .exclude_kernel = 1, }; int fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0); ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); asm volatile("st.b %1, %0" : "=m"(var) : "r"(11) : "memory"); asm volatile("st.b %1, %0" : "=m"(var) : "r"(22) : "memory"); asm volatile("st.b %1, %0" : "=m"(var) : "r"(33) : "memory"); ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); read(fd, &count, sizeof(size_t)); printf("Watchpoint counts: expected = 3, actual = %zu\n", count); close(fd); return 0; } (2) Test steps: $ gcc test_perf_continuous.c -o test_perf_continuous $ ./test_perf_continuous (3) Test results: Without this patch: Watchpoint counts: expected = 3, actual = 1 With this patch: Watchpoint counts: expected = 3, actual = 3 Cc: stable@vger.kernel.org Fixes: 3eb2a8b23598 ("LoongArch: Fix multiple hardware watchpoint issues") Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
Diffstat (limited to 'tools/lib/python')
0 files changed, 0 insertions, 0 deletions