diff options
| author | Tiezhu Yang <yangtiezhu@loongson.cn> | 2026-10-27 22:51:35 +0800 |
|---|---|---|
| committer | Huacai Chen <chenhuacai@loongson.cn> | 2026-10-27 22:51:35 +0800 |
| commit | 5e0b00fd000a53a5428fed6c96be74019bd0ad17 (patch) | |
| tree | 7e8ad086a40355d342e04d2c2351c8fa1013292a /tools/lib/python | |
| parent | f5c0f8427469e703465c719c4304ce91282a32eb (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
