From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F2EC537DE83; Wed, 15 Jul 2026 01:44:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079860; cv=none; b=agYuY7ZNdxRryegizghBvIEA/Jbn9CMy2zlC8nkPL+uxLPe40c04YCVJplWWL7HtWja9k8WtqFP6cL+MK/BYOaI1IYYf8/yizuBHR1zjjRDonPJ9pRZYQQ8d+qRLuYLkTRFLKq4xOJDhkj2jhudQBbZ2cDjQLc92KnYmYjFpBvs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079860; c=relaxed/simple; bh=MaR9KiSC78lZOfaQwjiO/9u9syCBCovGIHZ3oXpgwXk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=aYxJvJJmbeP+AEZXXpfi8ndhTyG4hpUlxnkG2S4NdxnJ0pazbJ091Eieo6KnQe3zIXdHC8z+vUQBwQo+GBeIpNtgbeVcr4GswkmVmIK4KaFBwDoSOtExIyZncrEMiTsSwBVgR5h610pj4jsKWxCYqihFeUnQwyTL52yel0RiG1I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ahFTwRme; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ahFTwRme" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E56961F000E9; Wed, 15 Jul 2026 01:44:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079856; bh=QPHVdr+TAWj3Xsv53QrIaD1N7WTqZcX6qeA+4IvDMUI=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=ahFTwRmerJI/VMQ27oMG44ZUfZPGKfpT75ZfU+jJFZfG2lUMpatWhciLRT29665Cr ho1x4jBuSLCEIqqlZnQiBywQ3AE1gB4nQ4N4Wfs4Ws2qC7LpAsfrq7LuQWQ3DmkmM7 HNc5hiiTbvrTqvq2az680r1seEh4Lo/MbPi7yxZd7HhrCNHgGL5nDieKMM9zbsrK4c Gw7dhiNJRpk2yePU7HWWw3FjiagU0qpzJfLGmUg5W0AHZfiMLN6FLzIhbZLoEIRQC7 9QqcY5DAODn92K7y/JBTbzZGWGTrUoyn5BHWvxUOXdA/WHrDMHK7LUGdavIc3G1SVz PcOPZHzE9aq5w== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 01/10] tracing: wprobe: Add watchpoint probe event based on hardware breakpoint Date: Wed, 15 Jul 2026 10:44:10 +0900 Message-ID: <178407985042.95826.15157571843735900142.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) Add a new probe event for the hardware breakpoint called wprobe-event. This wprobe allows user to trace (watch) the memory access at the specified memory address. The new syntax is; w[:[GROUP/]EVENT] [r|w|rw]@[ADDR|SYM][:SIZE] [FETCH_ARGs] User also can use $addr to fetch the accessed address. But no other variables are supported. To record updated value, use '+0($addr)'. For example, tracing updates of the jiffies; /sys/kernel/tracing # echo 'w:my_jiffies w@jiffies' >> dynamic_events /sys/kernel/tracing # cat dynamic_events w:wprobes/my_jiffies w@jiffies:4 /sys/kernel/tracing # echo 1 > events/wprobes/my_jiffies/enable /sys/kernel/tracing # head -n 20 trace | tail -n 5 # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | -0 [000] d.Z1. 206.547317: my_jiffies: (tick_do_upd= ate_jiffies64+0xbe/0x130) -0 [000] d.Z1. 206.548341: my_jiffies: (tick_do_upd= ate_jiffies64+0xbe/0x130) -0 [000] d.Z1. 206.549346: my_jiffies: (tick_do_upd= ate_jiffies64+0xbe/0x130) Link: https://lore.kernel.org/all/175859021100.374439.8723137923620348816.s= tgit@devnote2/ Signed-off-by: Masami Hiramatsu (Google) --- Changes in v7: - Include IS_ERR_PCPU fix. - use seq_print_ip_sym_offset(). - fix checkpatch warning on DEFINE_FREE() - Use bp->attr.bp_addr instead of tw->addr because it can be updated from= another CPU. --- Documentation/trace/index.rst | 1=20 Documentation/trace/wprobetrace.rst | 69 +++ include/linux/trace_events.h | 2=20 kernel/trace/Kconfig | 13 + kernel/trace/Makefile | 1=20 kernel/trace/trace.c | 9=20 kernel/trace/trace.h | 5=20 kernel/trace/trace_probe.c | 22 + kernel/trace/trace_probe.h | 8=20 kernel/trace/trace_wprobe.c | 692 +++++++++++++++++++++++++++++++= ++++ 10 files changed, 819 insertions(+), 3 deletions(-) create mode 100644 Documentation/trace/wprobetrace.rst create mode 100644 kernel/trace/trace_wprobe.c diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst index 5d9bf4694d5d..2f04f32001ed 100644 --- a/Documentation/trace/index.rst +++ b/Documentation/trace/index.rst @@ -36,6 +36,7 @@ the Linux kernel. kprobes kprobetrace fprobetrace + wprobetrace eprobetrace fprobe ring-buffer-design diff --git a/Documentation/trace/wprobetrace.rst b/Documentation/trace/wpro= betrace.rst new file mode 100644 index 000000000000..025b4c39b809 --- /dev/null +++ b/Documentation/trace/wprobetrace.rst @@ -0,0 +1,69 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Watchpoint probe (wprobe) Event Tracing +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +.. Author: Masami Hiramatsu + +Overview +-------- + +Wprobe event is a dynamic event based on the hardware breakpoint, which is +similar to other probe events, but it is for watching data access. It allo= ws +you to trace which code accesses a specified data. + +As same as other dynamic events, wprobe events are defined via +`dynamic_events` interface file on tracefs. + +Synopsis of wprobe-events +------------------------- +:: + + w:[GRP/][EVENT] SPEC [FETCHARGS] : Probe on data a= ccess + + GRP : Group name for wprobe. If omitted, use "wprobes" for it. + EVENT : Event name for wprobe. If omitted, an event name is + generated based on the address or symbol. + SPEC : Breakpoint specification. + [r|w|rw]@[:LENGTH] + + r|w|rw : Access type, r for read, w for write, and rw for both. + Default is rw if omitted. + ADDRESS : Address to trace (hexadecimal). + SYMBOL : Symbol name to trace. + LENGTH : Length of the data to trace in bytes. (1, 2, 4, or 8) + + FETCHARGS : Arguments. Each probe can have up to 128 args. + $addr : Fetch the accessing address. + @ADDR : Fetch memory at ADDR (ADDR should be in kernel) + @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbo= l) + +|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*1)(\= *2) + \IMM : Store an immediate value to the argument. + NAME=3DFETCHARG : Set NAME as the argument name of FETCHARG. + FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types + (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types + (x8/x16/x32/x64), "char", "string", "ustring", "symbol",= "symstr" + and bitfield are supported. + + (\*1) this is useful for fetching a field of data structures. + (\*2) "u" means user-space dereference. + +For the details of TYPE, see :ref:`kprobetrace documentation `. + +Usage examples +-------------- +Here is an example to add a wprobe event on a variable `jiffies`. +:: + + # echo 'w:my_jiffies w@jiffies' >> dynamic_events + # cat dynamic_events + w:wprobes/my_jiffies w@jiffies + # echo 1 > events/wprobes/enable + # cat trace | head + # TASK-PID CPU# ||||| TIMESTAMP FUNCTION + # | | | ||||| | | + -0 [000] d.Z1. 717.026259: my_jiffies: (tick_do_up= date_jiffies64+0xbe/0x130) + -0 [000] d.Z1. 717.026373: my_jiffies: (tick_do_up= date_jiffies64+0xbe/0x130) + +You can see the code which writes to `jiffies` is `tick_do_update_jiffies6= 4()`. diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 308c76b57d13..d1e5ab71d928 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -328,6 +328,7 @@ enum { TRACE_EVENT_FL_UPROBE_BIT, TRACE_EVENT_FL_EPROBE_BIT, TRACE_EVENT_FL_FPROBE_BIT, + TRACE_EVENT_FL_WPROBE_BIT, TRACE_EVENT_FL_CUSTOM_BIT, TRACE_EVENT_FL_TEST_STR_BIT, }; @@ -358,6 +359,7 @@ enum { TRACE_EVENT_FL_UPROBE =3D (1 << TRACE_EVENT_FL_UPROBE_BIT), TRACE_EVENT_FL_EPROBE =3D (1 << TRACE_EVENT_FL_EPROBE_BIT), TRACE_EVENT_FL_FPROBE =3D (1 << TRACE_EVENT_FL_FPROBE_BIT), + TRACE_EVENT_FL_WPROBE =3D (1 << TRACE_EVENT_FL_WPROBE_BIT), TRACE_EVENT_FL_CUSTOM =3D (1 << TRACE_EVENT_FL_CUSTOM_BIT), TRACE_EVENT_FL_TEST_STR =3D (1 << TRACE_EVENT_FL_TEST_STR_BIT), }; diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 0ab5916575a9..b58c2565024f 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -862,6 +862,19 @@ config EPROBE_EVENTS convert the type of an event field. For example, turn an address into a string. =20 +config WPROBE_EVENTS + bool "Enable wprobe-based dynamic events" + depends on TRACING + depends on HAVE_HW_BREAKPOINT + select PROBE_EVENTS + select DYNAMIC_EVENTS + help + This allows the user to add watchpoint tracing events based on + hardware breakpoints on the fly via the ftrace interface. + + Those events can be inserted wherever hardware breakpoints can be + set, and record accessed memory address and values. + config BPF_EVENTS depends on BPF_SYSCALL depends on (KPROBE_EVENTS || UPROBE_EVENTS) && PERF_EVENTS diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index f934ff586bd4..141c8323de20 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_FTRACE_RECORD_RECURSION) +=3D trace_recurs= ion_record.o obj-$(CONFIG_FPROBE) +=3D fprobe.o obj-$(CONFIG_RETHOOK) +=3D rethook.o obj-$(CONFIG_FPROBE_EVENTS) +=3D trace_fprobe.o +obj-$(CONFIG_WPROBE_EVENTS) +=3D trace_wprobe.o =20 obj-$(CONFIG_TRACEPOINT_BENCHMARK) +=3D trace_benchmark.o obj-$(CONFIG_RV) +=3D rv/ diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c9e182d40059..1bc27c0ad029 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4294,8 +4294,12 @@ static const char readme_msg[] =3D " uprobe_events\t\t- Create/append/remove/show the userspace dynamic eve= nts\n" "\t\t\t Write into this file to define/undefine new trace events.\n" #endif +#ifdef CONFIG_WPROBE_EVENTS + " wprobe_events\t\t- Create/append/remove/show the hardware breakpoint d= ynamic events\n" + "\t\t\t Write into this file to define/undefine new trace events.\n" +#endif #if defined(CONFIG_KPROBE_EVENTS) || defined(CONFIG_UPROBE_EVENTS) || \ - defined(CONFIG_FPROBE_EVENTS) + defined(CONFIG_FPROBE_EVENTS) || defined(CONFIG_WPROBE_EVENTS) "\t accepts: event-definitions (one definition per line)\n" #if defined(CONFIG_KPROBE_EVENTS) || defined(CONFIG_UPROBE_EVENTS) "\t Format: p[:[/][]] []\n" @@ -4305,6 +4309,9 @@ static const char readme_msg[] =3D "\t f[:[/][]] [%return] []\n" "\t t[:[/][]] []\n" #endif +#ifdef CONFIG_WPROBE_EVENTS + "\t w[:[/][]] [r|w|rw]@[:]\n" +#endif #ifdef CONFIG_HIST_TRIGGERS "\t s:[synthetic/] []\n" #endif diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 80fe152af1dd..2f07c5c4ffc8 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -179,6 +179,11 @@ struct fexit_trace_entry_head { unsigned long ret_ip; }; =20 +struct wprobe_trace_entry_head { + struct trace_entry ent; + unsigned long ip; +}; + #define TRACE_BUF_SIZE 1024 =20 struct trace_array; diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 18c212122344..2600d9699bd8 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -1344,6 +1344,24 @@ static int parse_probe_vars(char *orig_arg, const st= ruct fetch_type *t, return 0; } =20 + /* wprobe only support "$addr" and "$value" variable */ + if (ctx->flags & TPARG_FL_WPROBE) { + if (!strcmp(arg, "addr")) { + code->op =3D FETCH_OP_BADDR; + return 0; + } + if (!strcmp(arg, "value")) { + code->op =3D FETCH_OP_BADDR; + code++; + code->op =3D FETCH_OP_DEREF; + code->offset =3D 0; + *pcode =3D code; + return 0; + } + err =3D TP_ERR_BAD_VAR; + goto inval; + } + if (str_has_prefix(arg, "retval")) { if (!(ctx->flags & TPARG_FL_RETURN)) { err =3D TP_ERR_RETVAL_ON_PROBE; @@ -1491,8 +1509,9 @@ parse_probe_arg(char *arg, const struct fetch_type *t= ype, ret =3D parse_probe_vars(arg, type, pcode, end, ctx); break; =20 +#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API case '%': /* named register */ - if (ctx->flags & (TPARG_FL_TEVENT | TPARG_FL_FPROBE)) { + if (ctx->flags & (TPARG_FL_TEVENT | TPARG_FL_FPROBE | TPARG_FL_WPROBE)) { /* eprobe and fprobe do not handle registers */ trace_probe_log_err(ctx->offset, BAD_VAR); break; @@ -1505,6 +1524,7 @@ parse_probe_arg(char *arg, const struct fetch_type *t= ype, } else trace_probe_log_err(ctx->offset, BAD_REG_NAME); break; +#endif =20 case '@': /* memory, file-offset or symbol */ if (isdigit(arg[1])) { diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index e6268a8dc378..64c5fe9bdfc9 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -90,6 +90,7 @@ typedef int (*print_type_func_t)(struct trace_seq *, void= *, void *); FETCH_OP(STACK, param), /* Stack: .param =3D index */ \ FETCH_OP(STACKP, none), /* Stack pointer */ \ FETCH_OP(RETVAL, none), /* Return value */ \ + FETCH_OP(BADDR, none), /* Break address */ \ FETCH_OP(IMM, imm), /* Immediate: .immediate */ \ FETCH_OP(COMM, none), /* Current comm */ \ FETCH_OP(CURRENT, none), /* Current task_struct address */\ @@ -419,6 +420,7 @@ static inline int traceprobe_get_entry_data_size(struct= trace_probe *tp) #define TPARG_FL_USER BIT(4) #define TPARG_FL_FPROBE BIT(5) #define TPARG_FL_TPOINT BIT(6) +#define TPARG_FL_WPROBE BIT(7) #define TPARG_FL_LOC_MASK GENMASK(4, 0) =20 static inline bool tparg_is_function_entry(unsigned int flags) @@ -600,7 +602,11 @@ extern int traceprobe_define_arg_fields(struct trace_e= vent_call *event_call, C(TYPECAST_SYM_OFFSET, "@SYM+/-OFFSET with typecast needs parentheses"), \ C(TYPECAST_NOT_ALIGNED, "Typecast field option is not byte-aligned"), \ C(TYPECAST_BAD_ARROW, "Typecast field option does not support -> operator= "), \ - C(NOSUP_PERCPU, "Per-cpu variable access is only for kernel probes"), + C(NOSUP_PERCPU, "Per-cpu variable access is only for kernel probes"), \ + C(BAD_ACCESS_FMT, "Access memory address requires @"), \ + C(BAD_ACCESS_TYPE, "Bad memory access type"), \ + C(BAD_ACCESS_LEN, "This memory access length is not supported"), \ + C(BAD_ACCESS_ADDR, "Invalid access memory address"), =20 #undef C #define C(a, b) TP_ERR_##a diff --git a/kernel/trace/trace_wprobe.c b/kernel/trace/trace_wprobe.c new file mode 100644 index 000000000000..b52f3eac719f --- /dev/null +++ b/kernel/trace/trace_wprobe.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware-breakpoint-based tracing events + * + * Copyright (C) 2023, Masami Hiramatsu + */ +#define pr_fmt(fmt) "trace_wprobe: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "trace_dynevent.h" +#include "trace_probe.h" +#include "trace_probe_kernel.h" +#include "trace_probe_tmpl.h" + +#define WPROBE_EVENT_SYSTEM "wprobes" + +static int trace_wprobe_create(const char *raw_command); +static int trace_wprobe_show(struct seq_file *m, struct dyn_event *ev); +static int trace_wprobe_release(struct dyn_event *ev); +static bool trace_wprobe_is_busy(struct dyn_event *ev); +static bool trace_wprobe_match(const char *system, const char *event, + int argc, const char **argv, struct dyn_event *ev); + +static struct dyn_event_operations trace_wprobe_ops =3D { + .create =3D trace_wprobe_create, + .show =3D trace_wprobe_show, + .is_busy =3D trace_wprobe_is_busy, + .free =3D trace_wprobe_release, + .match =3D trace_wprobe_match, +}; + +struct trace_wprobe { + struct dyn_event devent; + struct perf_event * __percpu *bp_event; + unsigned long addr; + int len; + int type; + const char *symbol; + struct trace_probe tp; +}; + +static bool is_trace_wprobe(struct dyn_event *ev) +{ + return ev->ops =3D=3D &trace_wprobe_ops; +} + +static struct trace_wprobe *to_trace_wprobe(struct dyn_event *ev) +{ + return container_of(ev, struct trace_wprobe, devent); +} + +#define for_each_trace_wprobe(pos, dpos) \ + for_each_dyn_event(dpos) \ + if (is_trace_wprobe(dpos) && (pos =3D to_trace_wprobe(dpos))) + +static bool trace_wprobe_is_busy(struct dyn_event *ev) +{ + struct trace_wprobe *tw =3D to_trace_wprobe(ev); + + return trace_probe_is_enabled(&tw->tp); +} + +static bool trace_wprobe_match(const char *system, const char *event, + int argc, const char **argv, struct dyn_event *ev) +{ + struct trace_wprobe *tw =3D to_trace_wprobe(ev); + + if (event[0] !=3D '\0' && strcmp(trace_probe_name(&tw->tp), event)) + return false; + + if (system && strcmp(trace_probe_group_name(&tw->tp), system)) + return false; + + /* TODO: match arguments */ + return true; +} + +/* + * Note that we don't verify the fetch_insn code, since it does not come + * from user space. + */ +static int +process_fetch_insn(struct fetch_insn *code, void *rec, void *edata, + void *dest, void *base) +{ + void *baddr =3D rec; + unsigned long val; + int ret; + +retry: + /* 1st stage: get value from context */ + switch (code->op) { + case FETCH_OP_BADDR: + val =3D (unsigned long)baddr; + break; + case FETCH_NOP_SYMBOL: /* Ignore a place holder */ + code++; + goto retry; + default: + ret =3D process_common_fetch_insn(code, &val); + if (ret < 0) + return ret; + } + code++; + + return process_fetch_insn_bottom(code, val, dest, base); +} +NOKPROBE_SYMBOL(process_fetch_insn) + +static void wprobe_trace_handler(struct trace_wprobe *tw, + unsigned long addr, + struct pt_regs *regs, + struct trace_event_file *trace_file) +{ + struct wprobe_trace_entry_head *entry; + struct trace_event_call *call =3D trace_probe_event_call(&tw->tp); + struct trace_event_buffer fbuffer; + int dsize; + + if (WARN_ON_ONCE(call !=3D trace_file->event_call)) + return; + + if (trace_trigger_soft_disabled(trace_file)) + return; + + if (tw->addr !=3D addr) + return; + + dsize =3D __get_data_size(&tw->tp, (void *)addr, NULL); + + entry =3D trace_event_buffer_reserve(&fbuffer, trace_file, + sizeof(*entry) + tw->tp.size + dsize); + if (!entry) + return; + + entry->ip =3D instruction_pointer(regs); + store_trace_args(&entry[1], &tw->tp, (void *)addr, NULL, sizeof(*entry), = dsize); + + fbuffer.regs =3D regs; + trace_event_buffer_commit(&fbuffer); +} + +static void wprobe_perf_handler(struct perf_event *bp, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct trace_wprobe *tw =3D bp->overflow_handler_context; + struct event_file_link *link; + unsigned long addr =3D bp->attr.bp_addr; + + trace_probe_for_each_link_rcu(link, &tw->tp) + wprobe_trace_handler(tw, addr, regs, link->file); +} + +static int __register_trace_wprobe(struct trace_wprobe *tw) +{ + struct perf_event_attr attr; + + if (tw->bp_event) + return -EINVAL; + + hw_breakpoint_init(&attr); + attr.bp_addr =3D tw->addr; + attr.bp_len =3D tw->len; + attr.bp_type =3D tw->type; + + tw->bp_event =3D register_wide_hw_breakpoint(&attr, wprobe_perf_handler, = tw); + if (IS_ERR_PCPU(tw->bp_event)) { + int ret =3D PTR_ERR_PCPU(tw->bp_event); + + tw->bp_event =3D NULL; + return ret; + } + + return 0; +} + +static void __unregister_trace_wprobe(struct trace_wprobe *tw) +{ + if (tw->bp_event) { + unregister_wide_hw_breakpoint(tw->bp_event); + tw->bp_event =3D NULL; + } +} + +static void free_trace_wprobe(struct trace_wprobe *tw) +{ + if (tw) { + trace_probe_cleanup(&tw->tp); + kfree(tw->symbol); + kfree(tw); + } +} +DEFINE_FREE(free_trace_wprobe, struct trace_wprobe *, + if (!IS_ERR_OR_NULL(_T)) + free_trace_wprobe(_T)) + + +static struct trace_wprobe *alloc_trace_wprobe(const char *group, + const char *event, + const char *symbol, + unsigned long addr, + int len, int type, int nargs) +{ + struct trace_wprobe *tw __free(free_trace_wprobe) =3D NULL; + int ret; + + tw =3D kzalloc(struct_size(tw, tp.args, nargs), GFP_KERNEL); + if (!tw) + return ERR_PTR(-ENOMEM); + + if (symbol) { + tw->symbol =3D kstrdup(symbol, GFP_KERNEL); + if (!tw->symbol) + return ERR_PTR(-ENOMEM); + } + tw->addr =3D addr; + tw->len =3D len; + tw->type =3D type; + + ret =3D trace_probe_init(&tw->tp, event, group, false, nargs); + if (ret < 0) + return ERR_PTR(ret); + + dyn_event_init(&tw->devent, &trace_wprobe_ops); + return_ptr(tw); +} + +static struct trace_wprobe *find_trace_wprobe(const char *event, + const char *group) +{ + struct dyn_event *pos; + struct trace_wprobe *tw; + + for_each_trace_wprobe(tw, pos) + if (strcmp(trace_probe_name(&tw->tp), event) =3D=3D 0 && + strcmp(trace_probe_group_name(&tw->tp), group) =3D=3D 0) + return tw; + return NULL; +} + +static enum print_line_t +print_wprobe_event(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct wprobe_trace_entry_head *field; + struct trace_seq *s =3D &iter->seq; + struct trace_probe *tp; + + field =3D (struct wprobe_trace_entry_head *)iter->ent; + tp =3D trace_probe_primary_from_call( + container_of(event, struct trace_event_call, event)); + if (WARN_ON_ONCE(!tp)) + goto out; + + trace_seq_printf(s, "%s: (", trace_probe_name(tp)); + + if (!seq_print_ip_sym_offset(s, field->ip, flags)) + goto out; + + trace_seq_putc(s, ')'); + + if (trace_probe_print_args(s, tp->args, tp->nr_args, + (u8 *)&field[1], field) < 0) + goto out; + + trace_seq_putc(s, '\n'); +out: + return trace_handle_return(s); +} + +static int wprobe_event_define_fields(struct trace_event_call *event_call) +{ + int ret; + struct wprobe_trace_entry_head field; + struct trace_probe *tp; + + tp =3D trace_probe_primary_from_call(event_call); + if (WARN_ON_ONCE(!tp)) + return -ENOENT; + + DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0); + + return traceprobe_define_arg_fields(event_call, sizeof(field), tp); +} + +static struct trace_event_functions wprobe_funcs =3D { + .trace =3D print_wprobe_event +}; + +static struct trace_event_fields wprobe_fields_array[] =3D { + { .type =3D TRACE_FUNCTION_TYPE, + .define_fields =3D wprobe_event_define_fields }, + {} +}; + +static int wprobe_register(struct trace_event_call *event, + enum trace_reg type, void *data); + +static inline void init_trace_event_call(struct trace_wprobe *tw) +{ + struct trace_event_call *call =3D trace_probe_event_call(&tw->tp); + + call->event.funcs =3D &wprobe_funcs; + call->class->fields_array =3D wprobe_fields_array; + call->flags =3D TRACE_EVENT_FL_WPROBE; + call->class->reg =3D wprobe_register; +} + +static int register_wprobe_event(struct trace_wprobe *tw) +{ + init_trace_event_call(tw); + return trace_probe_register_event_call(&tw->tp); +} + +static int register_trace_wprobe_event(struct trace_wprobe *tw) +{ + struct trace_wprobe *old_tb; + int ret; + + guard(mutex)(&event_mutex); + + old_tb =3D find_trace_wprobe(trace_probe_name(&tw->tp), + trace_probe_group_name(&tw->tp)); + if (old_tb) + return -EBUSY; + + ret =3D register_wprobe_event(tw); + if (ret) + return ret; + + dyn_event_add(&tw->devent, trace_probe_event_call(&tw->tp)); + return 0; +} +static int unregister_wprobe_event(struct trace_wprobe *tw) +{ + return trace_probe_unregister_event_call(&tw->tp); +} + +static int unregister_trace_wprobe(struct trace_wprobe *tw) +{ + if (trace_probe_has_sibling(&tw->tp)) + goto unreg; + + if (trace_probe_is_enabled(&tw->tp)) + return -EBUSY; + + if (trace_event_dyn_busy(trace_probe_event_call(&tw->tp))) + return -EBUSY; + + if (unregister_wprobe_event(tw)) + return -EBUSY; + +unreg: + __unregister_trace_wprobe(tw); + dyn_event_remove(&tw->devent); + trace_probe_unlink(&tw->tp); + + return 0; +} + +static int enable_trace_wprobe(struct trace_event_call *call, + struct trace_event_file *file) +{ + struct trace_probe *tp; + struct trace_wprobe *tw; + bool enabled; + int ret =3D 0; + + tp =3D trace_probe_primary_from_call(call); + if (WARN_ON_ONCE(!tp)) + return -ENODEV; + enabled =3D trace_probe_is_enabled(tp); + + if (file) { + ret =3D trace_probe_add_file(tp, file); + if (ret) + return ret; + } else { + trace_probe_set_flag(tp, TP_FLAG_PROFILE); + } + + if (!enabled) { + list_for_each_entry(tw, trace_probe_probe_list(tp), tp.list) { + ret =3D __register_trace_wprobe(tw); + if (ret < 0) { + /* TODO: rollback */ + return ret; + } + } + } + + return 0; +} + +static int disable_trace_wprobe(struct trace_event_call *call, + struct trace_event_file *file) +{ + struct trace_wprobe *tw; + struct trace_probe *tp; + + tp =3D trace_probe_primary_from_call(call); + if (WARN_ON_ONCE(!tp)) + return -ENODEV; + + if (file) { + if (!trace_probe_get_file_link(tp, file)) + return -ENOENT; + if (!trace_probe_has_single_file(tp)) + goto out; + trace_probe_clear_flag(tp, TP_FLAG_TRACE); + } else { + trace_probe_clear_flag(tp, TP_FLAG_PROFILE); + } + + if (!trace_probe_is_enabled(tp)) { + list_for_each_entry(tw, trace_probe_probe_list(tp), tp.list) { + __unregister_trace_wprobe(tw); + } + } + +out: + if (file) + trace_probe_remove_file(tp, file); + + return 0; +} + +static int wprobe_register(struct trace_event_call *event, + enum trace_reg type, void *data) +{ + struct trace_event_file *file =3D data; + + switch (type) { + case TRACE_REG_REGISTER: + return enable_trace_wprobe(event, file); + case TRACE_REG_UNREGISTER: + return disable_trace_wprobe(event, file); + +#ifdef CONFIG_PERF_EVENTS + case TRACE_REG_PERF_REGISTER: + return enable_trace_wprobe(event, NULL); + case TRACE_REG_PERF_UNREGISTER: + return disable_trace_wprobe(event, NULL); + case TRACE_REG_PERF_OPEN: + case TRACE_REG_PERF_CLOSE: + case TRACE_REG_PERF_ADD: + case TRACE_REG_PERF_DEL: + return 0; +#endif + } + return 0; +} + +static int parse_address_spec(const char *spec, unsigned long *addr, int *= type, + int *len, char **symbol) +{ + char *_spec __free(kfree) =3D NULL; + int _len =3D HW_BREAKPOINT_LEN_4; + int _type =3D HW_BREAKPOINT_RW; + unsigned long _addr =3D 0; + char *at, *col; + + _spec =3D kstrdup(spec, GFP_KERNEL); + if (!_spec) + return -ENOMEM; + + at =3D strchr(_spec, '@'); + col =3D strchr(_spec, ':'); + + if (!at) { + trace_probe_log_err(0, BAD_ACCESS_FMT); + return -EINVAL; + } + + if (at !=3D _spec) { + *at =3D '\0'; + + if (strcmp(_spec, "r") =3D=3D 0) + _type =3D HW_BREAKPOINT_R; + else if (strcmp(_spec, "w") =3D=3D 0) + _type =3D HW_BREAKPOINT_W; + else if (strcmp(_spec, "rw") =3D=3D 0) + _type =3D HW_BREAKPOINT_RW; + else { + trace_probe_log_err(0, BAD_ACCESS_TYPE); + return -EINVAL; + } + } + + if (col) { + *col =3D '\0'; + if (kstrtoint(col + 1, 0, &_len)) { + trace_probe_log_err(col + 1 - _spec, BAD_ACCESS_LEN); + return -EINVAL; + } + + switch (_len) { + case 1: + _len =3D HW_BREAKPOINT_LEN_1; + break; + case 2: + _len =3D HW_BREAKPOINT_LEN_2; + break; + case 4: + _len =3D HW_BREAKPOINT_LEN_4; + break; + case 8: + _len =3D HW_BREAKPOINT_LEN_8; + break; + default: + trace_probe_log_err(col + 1 - _spec, BAD_ACCESS_LEN); + return -EINVAL; + } + } + + if (kstrtoul(at + 1, 0, &_addr) !=3D 0) { + char *off_str =3D strpbrk(at + 1, "+-"); + int offset =3D 0; + + if (off_str) { + if (kstrtoint(off_str, 0, &offset) !=3D 0) { + trace_probe_log_err(off_str - _spec, BAD_PROBE_ADDR); + return -EINVAL; + } + *off_str =3D '\0'; + } + _addr =3D kallsyms_lookup_name(at + 1); + if (!_addr) { + trace_probe_log_err(at + 1 - _spec, BAD_ACCESS_ADDR); + return -ENOENT; + } + _addr +=3D offset; + *symbol =3D kstrdup(at + 1, GFP_KERNEL); + if (!*symbol) + return -ENOMEM; + } + + *addr =3D _addr; + *type =3D _type; + *len =3D _len; + return 0; +} + +static int __trace_wprobe_create(int argc, const char *argv[]) +{ + /* + * Argument syntax: + * b[:[GRP/][EVENT]] SPEC + * + * SPEC: + * [r|w|rw]@[ADDR|SYMBOL[+OFFS]][:LEN] + */ + struct traceprobe_parse_context *ctx __free(traceprobe_parse_context) =3D= NULL; + struct trace_wprobe *tw __free(free_trace_wprobe) =3D NULL; + const char *event =3D NULL, *group =3D WPROBE_EVENT_SYSTEM; + const char *tplog __free(trace_probe_log_clear) =3D NULL; + char *symbol =3D NULL; + unsigned long addr; + int len, type, i; + int ret =3D 0; + + if (argv[0][0] !=3D 'w') + return -ECANCELED; + + if (argc < 2) + return -EINVAL; + + tplog =3D trace_probe_log_init("wprobe", argc, argv); + + if (argv[0][1] !=3D '\0') { + if (argv[0][1] !=3D ':') { + trace_probe_log_set_index(0); + trace_probe_log_err(1, BAD_MAXACT_TYPE); + /* Invalid format */ + return -EINVAL; + } + event =3D &argv[0][2]; + } + + trace_probe_log_set_index(1); + ret =3D parse_address_spec(argv[1], &addr, &type, &len, &symbol); + if (ret < 0) + return ret; + + if (!event) + event =3D symbol ? symbol : "wprobe"; + + argc -=3D 2; argv +=3D 2; + tw =3D alloc_trace_wprobe(group, event, symbol, addr, len, type, argc); + if (IS_ERR(tw)) + return PTR_ERR(tw); + + ctx =3D kzalloc_obj(*ctx); + if (!ctx) + return -ENOMEM; + + ctx->flags =3D TPARG_FL_KERNEL | TPARG_FL_WPROBE; + + /* parse arguments */ + for (i =3D 0; i < argc; i++) { + trace_probe_log_set_index(i + 2); + ctx->offset =3D 0; + ret =3D traceprobe_parse_probe_arg(&tw->tp, i, argv[i], ctx); + if (ret) + return ret; /* This can be -ENOMEM */ + } + + ret =3D traceprobe_set_print_fmt(&tw->tp, PROBE_PRINT_NORMAL); + if (ret < 0) + return ret; + + ret =3D register_trace_wprobe_event(tw); + if (!ret) + tw =3D NULL; /* To avoid free */ + + return ret; +} + +static int trace_wprobe_create(const char *raw_command) +{ + return trace_probe_create(raw_command, __trace_wprobe_create); +} + +static int trace_wprobe_release(struct dyn_event *ev) +{ + struct trace_wprobe *tw =3D to_trace_wprobe(ev); + int ret =3D unregister_trace_wprobe(tw); + + if (!ret) + free_trace_wprobe(tw); + return ret; +} + +static int trace_wprobe_show(struct seq_file *m, struct dyn_event *ev) +{ + struct trace_wprobe *tw =3D to_trace_wprobe(ev); + int i; + + seq_printf(m, "w:%s/%s", trace_probe_group_name(&tw->tp), + trace_probe_name(&tw->tp)); + + char type_char; + + if (tw->type =3D=3D HW_BREAKPOINT_R) + type_char =3D 'r'; + else if (tw->type =3D=3D HW_BREAKPOINT_W) + type_char =3D 'w'; + else + type_char =3D 'x'; /* Should be rw */ + + int len; + + if (tw->len =3D=3D HW_BREAKPOINT_LEN_1) + len =3D 1; + else if (tw->len =3D=3D HW_BREAKPOINT_LEN_2) + len =3D 2; + else if (tw->len =3D=3D HW_BREAKPOINT_LEN_4) + len =3D 4; + else + len =3D 8; + + if (tw->symbol) + seq_printf(m, " %c@%s:%d", type_char, tw->symbol, len); + else + seq_printf(m, " %c@0x%lx:%d", type_char, tw->addr, len); + + for (i =3D 0; i < tw->tp.nr_args; i++) + seq_printf(m, " %s=3D%s", tw->tp.args[i].name, tw->tp.args[i].comm); + seq_putc(m, '\n'); + + return 0; +} + +static __init int init_wprobe_trace(void) +{ + return dyn_event_register(&trace_wprobe_ops); +} +fs_initcall(init_wprobe_trace); + From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7DBC92E8897; Wed, 15 Jul 2026 01:44:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079870; cv=none; b=sB1L7LlKtubaifBZd6hfY2fuUPA/iIeaUb8MTNfwS1xhvALT+JnxlRDN7RfChhCA8o6R8y8kf/1nZkQnNs5kejM8WBA16Tgs3EdWaxHT2jXKOn6VvisCymF3OU3eAaMAhcdac95t54uffqxxjGrXgOQDTcA5J64UieK/cBw+Hys= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079870; c=relaxed/simple; bh=BFbZh+ntuWHBtFTy0Ki3TzUY5YSGmIRu5fpRYKoG3v0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=iOp7+8PRU6ZRlg3kOM8zDU36OmibtKuvTimN0NPcOOPTaGPFBv8CZYfZH8LlcUua3YSQ1KNOatoCELwV5OmP/7vF4Rtm2ffHRolISwlShM69VX2yxqOLAA1/LxL6Dkxio7B1EIZltrB1fnlLN0mEXb5X3LhDozfMsSB/mWDf3OQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AUx9KCLa; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="AUx9KCLa" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 163501F000E9; Wed, 15 Jul 2026 01:44:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079869; bh=M+9gUWXB1JxNkcnC7x8wAJsFruNd1Q8F6mv3i+BG7A4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=AUx9KCLag7Jt0GSDr2YrUtqkv4sdPaylmgQZhgQk2NABmoVZPgKR+5HWk8ArBNDoe /U4+DJOtR+5SNEXdjDsiE0RuN6+9KBAD9LTL+4+1XHkCFqp1RJXaEi40I5oSpZcK86 D/65w9aQuwqx1+fexB8jGQmgsnDKIOTVNYumAGnFvMobADyrU3d7qEQwZqk2G5FmGw PlBzsbEkHEOgjLMTEHGYTQejD6oynN0FAnF0DAp6wCHj0B3c1AFIruxNwy6WG0NRm3 omYDktiCqWpgvTM7yVLyQ761lasQvXwn66qWgqF6q9N+P3uH+ulhDPL7iKROLhjP1w RuTV8L82l+IVw== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 02/10] x86: hw_breakpoint: Add a kconfig to clarify when a breakpoint fires Date: Wed, 15 Jul 2026 10:44:21 +0900 Message-ID: <178407986179.95826.1630626264869856006.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) Add CONFIG_HAVE_POST_BREAKPOINT_HOOK which indicates the hw_breakpoint on that architecture fires after the target memory has been modified. This is currently x86 only behavior. Signed-off-by: Masami Hiramatsu (Google) --- arch/Kconfig | 10 ++++++++++ arch/x86/Kconfig | 1 + kernel/trace/Kconfig | 1 + 3 files changed, 12 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index fa7507ac8e13..959aee9568ff 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -457,6 +457,16 @@ config HAVE_MIXED_BREAKPOINTS_REGS Select this option if your arch implements breakpoints under the latter fashion. =20 +config HAVE_POST_BREAKPOINT_HOOK + bool + depends on HAVE_HW_BREAKPOINT + help + Depending on the arch implementation of hardware breakpoints, + some of them provide breakpoint hook after the target memory + is modified. + Select this option if your arch implements breakpoints overflow + handler hooks after the target memory is modified. + config HAVE_USER_RETURN_NOTIFIER bool =20 diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index bdad90f210e4..6b7e14ef8cfb 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_POST_BREAKPOINT_HOOK select HAVE_IOREMAP_PROT select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64 select HAVE_IRQ_TIME_ACCOUNTING diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index b58c2565024f..d9b6fa5c35d9 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -866,6 +866,7 @@ config WPROBE_EVENTS bool "Enable wprobe-based dynamic events" depends on TRACING depends on HAVE_HW_BREAKPOINT + depends on HAVE_POST_BREAKPOINT_HOOK select PROBE_EVENTS select DYNAMIC_EVENTS help From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8C6552E8897; Wed, 15 Jul 2026 01:44:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079881; cv=none; b=e5ZUvYKd1q02hR6mKhIcQwYPKLw1HAd5ISjQayjLOEW/xa4ueHsuKe83DHM1AaTKv4D3lggUr6e7Oc2EgBzCQVywJb7IckYVZPIwwOpKcRcngy59FUlyUee96AvASVQej/sqVFc+uPvA0zKCznp6Vv2Yl+cbKQZjSPFG4XY5bro= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079881; c=relaxed/simple; bh=XkgXf+BmWb1gm42X0hFSsPY2QiN+40F9WNZpKmrmhe0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=M24yiK3WUHOFAR0foK0OQXpiuCCPGhPKHTEA56rvmn7pd0m3U52P8nCtaZ96brcHbUtpMhdq8SEKCpWm3u1scCmPgthq+rcTdnD2ytXLlqOt0gbJWGDbddRRKreqiQ/htRF5SLF29ocCjRY8xvemEMdxL2L94J1tN4r5ejO6cxA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=av0+6RgK; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="av0+6RgK" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A76B51F000E9; Wed, 15 Jul 2026 01:44:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079880; bh=1dNoqBTEg7S9Qpsv/+4QI0aTEXe3+f5Z++F4nY+50YU=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=av0+6RgKGKhGad+TlsGrTD8wxX4RWSxNYMUn//2Pz+sWzdrc4kXA8Slw3F/di9v7z h+Bl58rYbxXn1EUpSOuLke91DTPl5NmpiZdCmlx91XS1NeUFtI1ymyOX3ElcJXF+OA 1JGfsiULSJzNd4Eovu280th2K2dJdgI3WNLHARfasEfa6MYdIRBziaqH3Zv7MC9/lW Mo8iQ+iQMWCxYcx0sIASOXO01joXc/JuxwB1eX+7fLi88S1FTzjBP3bIjl3G7VExNc qsllo8CKSni6NQK/yu0hJUMwBpo+QKlmnNiQkF+mmZVWDl7RqorW5p/JT6onnlTeCk 6CNiQtPT1mUqg== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 03/10] selftests: tracing: Add a basic testcase for wprobe Date: Wed, 15 Jul 2026 10:44:34 +0900 Message-ID: <178407987428.95826.3097357125404908509.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) Add 'add_remove_wprobe.tc' testcase for testing wprobe event that tests adding and removing operations of the wprobe event. Link: https://lore.kernel.org/all/175859026716.374439.14852239332989324292.= stgit@devnote2/ Signed-off-by: Masami Hiramatsu (Google) --- tools/testing/selftests/ftrace/config | 1=20 .../ftrace/test.d/dynevent/add_remove_wprobe.tc | 68 ++++++++++++++++= ++++ 2 files changed, 69 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remo= ve_wprobe.tc diff --git a/tools/testing/selftests/ftrace/config b/tools/testing/selftest= s/ftrace/config index 544de0db5f58..d2f503722020 100644 --- a/tools/testing/selftests/ftrace/config +++ b/tools/testing/selftests/ftrace/config @@ -27,3 +27,4 @@ CONFIG_STACK_TRACER=3Dy CONFIG_TRACER_SNAPSHOT=3Dy CONFIG_UPROBES=3Dy CONFIG_UPROBE_EVENTS=3Dy +CONFIG_WPROBE_EVENTS=3Dy diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_wpro= be.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_wprobe.tc new file mode 100644 index 000000000000..20774c7f69f8 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_wprobe.tc @@ -0,0 +1,68 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove wprobe events +# requires: dynamic_events "w[:[/][]] [r|w|rw]@[:= ]":README + +echo 0 > events/enable +echo > dynamic_events + +# Use jiffies as a variable that is frequently written to. +TARGET=3Djiffies + +echo "w:my_wprobe w@$TARGET" >> dynamic_events + +grep -q my_wprobe dynamic_events +if [ $? -ne 0 ]; then + echo "Failed to create wprobe event" + exit_fail +fi + +test -d events/wprobes/my_wprobe +if [ $? -ne 0 ]; then + echo "Failed to create wprobe event directory" + exit_fail +fi + +echo 1 > events/wprobes/my_wprobe/enable + +# Check if the event is enabled +cat events/wprobes/my_wprobe/enable | grep -q 1 +if [ $? -ne 0 ]; then + echo "Failed to enable wprobe event" + exit_fail +fi + +# Let some time pass to trigger the breakpoint +sleep 1 + +# Check if we got any trace output +if !grep -q my_wprobe trace; then + echo "wprobe event was not triggered" +fi + +echo 0 > events/wprobes/my_wprobe/enable + +# Check if the event is disabled +cat events/wprobes/my_wprobe/enable | grep -q 0 +if [ $? -ne 0 ]; then + echo "Failed to disable wprobe event" + exit_fail +fi + +echo "-:my_wprobe" >> dynamic_events + +! grep -q my_wprobe dynamic_events +if [ $? -ne 0 ]; then + echo "Failed to remove wprobe event" + exit_fail +fi + +! test -d events/wprobes/my_wprobe +if [ $? -ne 0 ]; then + echo "Failed to remove wprobe event directory" + exit_fail +fi + +clear_trace + +exit 0 From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3636837F8C4; Wed, 15 Jul 2026 01:44:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079893; cv=none; b=r1f4DuYXGhcBGgFbnwJUx8b3awgJQXSpJrnFMlKFUyCf8uxkNn7jwjx1ipiDxUeD5hiySpzzzEKcqzfBhefBF4XzsDnuWqao1jsDUzzBxz4EoqpjqdbjOgzLfhZ63BXM0Sn3lDYsuW8BirjSxA4mQG3NIRUxQZvK4Sw1c2pT+Q4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079893; c=relaxed/simple; bh=RnKOHNBPmj3Cu03w5U04I8JGBqtXnIak2+AQjeku/RI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lpeTJ/haO6PYTl0qoV1Ttf8sv47mlailUTFQ2wjA5VVT+xBtX8Pb3RjM1lxcdk3aSFCizC0ZpldgNmHOsZDXLgq9fao751prwBLO82vuj32Sn8dgtKRR8hKoBO9J2a2L07RTLdnYDXEhKITGvjm64xcbRJgTZ/6eUPj7OVPoU04= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fE2qLOSq; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fE2qLOSq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E79741F000E9; Wed, 15 Jul 2026 01:44:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079891; bh=bVuoFZz9XzNae8H4PardShx2MzQLtMk5zbZKcfpef8o=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=fE2qLOSqOU57v0pb+utlxItCp8SlK8kX1XoZzwlHoM6cYsQ0ck1IIfqUmLMuB8B+l lZSLy5f3Hg4r+ulpu8Ok9y2IB3yu3vbXVKFIxwuypHsKyoHewSfDmbpjlTHB6lWy1G ibEzqeStBenOnAtMMdM59Ru8MO1MTi66qrBMTgMs1Fjg/idjeww0eLXU7mmlypCjD3 1bWqgVbX0gos8RVhStD5q0UKNPep00BDDGvr055R3F08Q8atgQ06neN9HE/9tU1BGw lov2i8DAzBbPt6wlbLYVG7b7KSsgfSQEacqBVOxJ7P34vu2CAxB7bBdzHFZKUA1ZTT Qrr49iINpcMLw== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 04/10] selftests: tracing: Add syntax testcase for wprobe Date: Wed, 15 Jul 2026 10:44:45 +0900 Message-ID: <178407988551.95826.11515149189701096855.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) Add "wprobe_syntax_errors.tc" testcase for testing syntax errors of the watch probe events. Link: https://lore.kernel.org/all/175859027842.374439.6402700780945714048.s= tgit@devnote2/ Signed-off-by: Masami Hiramatsu (Google) --- .../test.d/dynevent/wprobes_syntax_errors.tc | 20 ++++++++++++++++= ++++ 1 file changed, 20 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/wprobes_= syntax_errors.tc diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/wprobes_syntax_= errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/wprobes_syntax_e= rrors.tc new file mode 100644 index 000000000000..56ac579d60ae --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/dynevent/wprobes_syntax_errors.= tc @@ -0,0 +1,20 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Watch probe event parser error log check +# requires: dynamic_events "w[:[/][]] [r|w|rw]@[:= ]":README + +check_error() { # command-with-error-pos-by-^ + ftrace_errlog_check 'wprobe' "$1" 'dynamic_events' +} + +check_error 'w ^symbol' # BAD_ACCESS_FMT +check_error 'w ^a@symbol' # BAD_ACCESS_TYPE +check_error 'w w@^symbol' # BAD_ACCESS_ADDR +check_error 'w w@jiffies^+offset' # BAD_ACCESS_ADDR +check_error 'w w@jiffies:^100' # BAD_ACCESS_LEN +check_error 'w w@jiffies ^$arg1' # BAD_VAR +check_error 'w w@jiffies ^$retval' # BAD_VAR +check_error 'w w@jiffies ^$stack' # BAD_VAR +check_error 'w w@jiffies ^%ax' # BAD_VAR + +exit 0 From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9CFF437FF67; Wed, 15 Jul 2026 01:45:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079904; cv=none; b=OFVzIGEw8SH7jTzNKxlFmGUaK8KVIk31Qlnyra8oXCvg9C6rMt/WAREKjY/0s97c2X1ce+7HWGAE0Q/isZP1Wr+0lgSLUs1IXN3IWiRE3F2dx8m06RlsICjyjdLKN0wSe61B7ip+2ynJ+zXSxB2X/G51nAq5PdSu6Izcy6wSByU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079904; c=relaxed/simple; bh=gE5E61SHGyfS34EqmtoCN5wjt/tCHOvKVE5y6Wg2Hp4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=X3990OhpFq8BNnPlf7BMxRvf2h89TO1jjQbgjyxDSRnHApWihLqEB8VkpuvP+O7GR1DEn/ECT4dT2kcSyNTheg+MN0Q95ZCcKsRNjaGtfu8ysYCbh05UJ/HcFk7LpPay4e7KKXf7dd/nfTifuDlz3U4R5xKUsCctF3ytRMNL1OM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Jh2tX1Nt; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Jh2tX1Nt" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7F9081F000E9; Wed, 15 Jul 2026 01:44:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079903; bh=rKR3a2cqrM6Ydesu7cGmeplW+brpFmLVctCH3lJIWws=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Jh2tX1Nt2GdpvDaJThIV9PmLuPTzpdCBpVI7WjH0Qi5AkKxqizv5FJkQ8QMFd6Wd2 swLyxyhB0EY43+L9NZmVlv60+Wq2Dk4MpO6f4Z9mPHBjeMKZzK9Lqfk9UrsvyN6xCu LtZTF2i1vtA7DouHdp1JLpCJ9hY74MLwdjF4V5UZkYxDQkN/mkVTn9OeH0he4MERTa k61qEcngSQZWiMhcpmCw8BeovSEMya9ReYsE4A0JVmXCI5JMKhaZRoBp23psDSK/Zy jGnBKg9mIy2pdll3boGb41ceLMRGtqIYynmWXYffrHhXqNff0Mlx8DcA4Vbu2uCckh SRDpVB/hfahPg== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 05/10] tracing: wprobe: Use a new seq_print_ip_sym_offset() wrapper Date: Wed, 15 Jul 2026 10:44:57 +0900 Message-ID: <178407989708.95826.17630423823637072384.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) Use a new seq_print_ip_sym_offset() wrapper function instead of using TRACE_ITER(SYM_OFFSET) mask directly. Link: https://lore.kernel.org/all/176226550596.59499.18020648957674458755.s= tgit@devnote2/ Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/trace_wprobe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace_wprobe.c b/kernel/trace/trace_wprobe.c index b52f3eac719f..dd310a87b333 100644 --- a/kernel/trace/trace_wprobe.c +++ b/kernel/trace/trace_wprobe.c @@ -20,6 +20,7 @@ #include =20 #include "trace_dynevent.h" +#include "trace_output.h" #include "trace_probe.h" #include "trace_probe_kernel.h" #include "trace_probe_tmpl.h" From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D5A47196C7C; Wed, 15 Jul 2026 01:45:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079917; cv=none; b=MG0N4CMgmfshISVGWZdSToNRETA3TLB1h/gW834evJho5TTG6h1B4u3WWPBF9bkW7RK2isbKP6l3v0DHwrRrd6IxBWUXwl/bF6hAstVelIVKpUNxP2OF0JgBnDZl8JAFHEYdCyhAoizuabu9nMWxd8h+lgMv0ClOyy26KJt/MyI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079917; c=relaxed/simple; bh=JEU4WBIYuuT37OeBiwFms6X98mwAE00m7G+jZVkf2xE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Q6lMuT0vPnE9FDADFEUDJFchiOU3do1TcOYrsOLvdQTtXZt3+CQrj8pDZRfNAe5JwnpxwzV2GNKLeergUS2nWTR6fiQgxA/LXzyqqwd/+A92TlaXswL+Ip2n4Xxw/2ynWIlbV8R9I5Q1yFZCEzY6yPpO0zyLvoRSkGf/d9JeLJs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=X9TJAE9x; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="X9TJAE9x" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E60D41F000E9; Wed, 15 Jul 2026 01:45:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079914; bh=lgDecu5q22CGPHO5P7dGfi6qI3hJSJOzKQO06G6GUlE=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=X9TJAE9xTnoV1HMPMmrasFckGFMN+rXjaNKWcifAhfoBxPw4sIvtzqgt+bjI+ZSov Yyho/Y3rw0XCsHqK/36JDF3g4mHUpBNjbtXZ+U9mOjAoExzel46PFBHuHG1GbbzqXW sR4mxCjwh9TUaLwxLvM8utLtgjyPqE9LbPxSpnS9KU2B+5IopAjpdJxxnFrV7dJ3af 8X8Nzir+ieyGC0HALuPqSGt2OG0FiaQLuvkQjeGlXXoumXOc3sOEGmoK8ZJQXTRGcA vpLD9DH0nQ2cU21DwIOiSgP6OD4lLDay1SR2RBOr/uTANPtRApebGZtQsrkhGXyIdt qgJX0oe+cNo8Q== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 06/10] x86/hw_breakpoint: Unify breakpoint install/uninstall Date: Wed, 15 Jul 2026 10:45:08 +0900 Message-ID: <178407990855.95826.6451473316040346375.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Jinchao Wang Consolidate breakpoint management to reduce code duplication. The diffstat was misleading, so the stripped code size is compared instead. After refactoring, it is reduced from 11976 bytes to 11448 bytes on my x86_64 system built with clang. This also makes it easier to introduce arch_reinstall_hw_breakpoint(). In addition, including linux/types.h to fix a missing build dependency. Signed-off-by: Jinchao Wang Reviewed-by: Masami Hiramatsu (Google) --- arch/x86/include/asm/hw_breakpoint.h | 6 + arch/x86/kernel/hw_breakpoint.c | 141 +++++++++++++++++++-----------= ---- 2 files changed, 84 insertions(+), 63 deletions(-) diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw= _breakpoint.h index 0bc931cd0698..aa6adac6c3a2 100644 --- a/arch/x86/include/asm/hw_breakpoint.h +++ b/arch/x86/include/asm/hw_breakpoint.h @@ -5,6 +5,7 @@ #include =20 #define __ARCH_HW_BREAKPOINT_H +#include =20 /* * The name should probably be something dealt in @@ -18,6 +19,11 @@ struct arch_hw_breakpoint { u8 type; }; =20 +enum bp_slot_action { + BP_SLOT_ACTION_INSTALL, + BP_SLOT_ACTION_UNINSTALL, +}; + #include #include #include diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoin= t.c index f846c15f21ca..877509539300 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c @@ -49,7 +49,6 @@ static DEFINE_PER_CPU(unsigned long, cpu_debugreg[HBP_NUM= ]); */ static DEFINE_PER_CPU(struct perf_event *, bp_per_reg[HBP_NUM]); =20 - static inline unsigned long __encode_dr7(int drnum, unsigned int len, unsigned int type) { @@ -86,96 +85,112 @@ int decode_dr7(unsigned long dr7, int bpnum, unsigned = *len, unsigned *type) } =20 /* - * Install a perf counter breakpoint. - * - * We seek a free debug address register and use it for this - * breakpoint. Eventually we enable it in the debug control register. - * - * Atomic: we hold the counter->ctx->lock and we only handle variables - * and registers local to this cpu. + * We seek a slot and change it or keep it based on the action. + * Returns slot number on success, negative error on failure. + * Must be called with IRQs disabled. */ -int arch_install_hw_breakpoint(struct perf_event *bp) +static int manage_bp_slot(struct perf_event *bp, enum bp_slot_action actio= n) { - struct arch_hw_breakpoint *info =3D counter_arch_bp(bp); - unsigned long *dr7; - int i; - - lockdep_assert_irqs_disabled(); + struct perf_event *old_bp; + struct perf_event *new_bp; + int slot; + + switch (action) { + case BP_SLOT_ACTION_INSTALL: + old_bp =3D NULL; + new_bp =3D bp; + break; + case BP_SLOT_ACTION_UNINSTALL: + old_bp =3D bp; + new_bp =3D NULL; + break; + default: + return -EINVAL; + } =20 - for (i =3D 0; i < HBP_NUM; i++) { - struct perf_event **slot =3D this_cpu_ptr(&bp_per_reg[i]); + for (slot =3D 0; slot < HBP_NUM; slot++) { + struct perf_event **curr =3D this_cpu_ptr(&bp_per_reg[slot]); =20 - if (!*slot) { - *slot =3D bp; - break; + if (*curr =3D=3D old_bp) { + *curr =3D new_bp; + return slot; } } =20 - if (WARN_ONCE(i =3D=3D HBP_NUM, "Can't find any breakpoint slot")) - return -EBUSY; + if (old_bp) { + WARN_ONCE(1, "Can't find matching breakpoint slot"); + return -EINVAL; + } + + WARN_ONCE(1, "No free breakpoint slots"); + return -EBUSY; +} + +static void setup_hwbp(struct arch_hw_breakpoint *info, int slot, bool ena= ble) +{ + unsigned long dr7; =20 - set_debugreg(info->address, i); - __this_cpu_write(cpu_debugreg[i], info->address); + set_debugreg(info->address, slot); + __this_cpu_write(cpu_debugreg[slot], info->address); =20 - dr7 =3D this_cpu_ptr(&cpu_dr7); - *dr7 |=3D encode_dr7(i, info->len, info->type); + dr7 =3D this_cpu_read(cpu_dr7); + if (enable) + dr7 |=3D encode_dr7(slot, info->len, info->type); + else + dr7 &=3D ~__encode_dr7(slot, info->len, info->type); =20 /* - * 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. + * 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) + this_cpu_write(cpu_dr7, dr7); + barrier(); =20 - set_debugreg(*dr7, 7); + set_debugreg(dr7, 7); + if (info->mask) - amd_set_dr_addr_mask(info->mask, i); + amd_set_dr_addr_mask(enable ? info->mask : 0, slot); =20 - return 0; + /* + * Disabling: + * 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. + */ + if (!enable) + this_cpu_write(cpu_dr7, dr7); } =20 /* - * Uninstall the breakpoint contained in the given counter. - * - * First we search the debug address register it uses and then we disable - * it. - * - * Atomic: we hold the counter->ctx->lock and we only handle variables - * and registers local to this cpu. + * find suitable breakpoint slot and set it up based on the action */ -void arch_uninstall_hw_breakpoint(struct perf_event *bp) +static int arch_manage_bp(struct perf_event *bp, enum bp_slot_action actio= n) { - struct arch_hw_breakpoint *info =3D counter_arch_bp(bp); - unsigned long dr7; - int i; + struct arch_hw_breakpoint *info; + int slot; =20 lockdep_assert_irqs_disabled(); =20 - for (i =3D 0; i < HBP_NUM; i++) { - struct perf_event **slot =3D this_cpu_ptr(&bp_per_reg[i]); - - if (*slot =3D=3D bp) { - *slot =3D NULL; - break; - } - } - - if (WARN_ONCE(i =3D=3D HBP_NUM, "Can't find any breakpoint slot")) - return; + slot =3D manage_bp_slot(bp, action); + if (slot < 0) + return slot; =20 - dr7 =3D this_cpu_read(cpu_dr7); - dr7 &=3D ~__encode_dr7(i, info->len, info->type); + info =3D counter_arch_bp(bp); + setup_hwbp(info, slot, action !=3D BP_SLOT_ACTION_UNINSTALL); =20 - set_debugreg(dr7, 7); - if (info->mask) - amd_set_dr_addr_mask(0, i); + return 0; +} =20 - /* - * 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(); +int arch_install_hw_breakpoint(struct perf_event *bp) +{ + return arch_manage_bp(bp, BP_SLOT_ACTION_INSTALL); +} =20 - this_cpu_write(cpu_dr7, dr7); +void arch_uninstall_hw_breakpoint(struct perf_event *bp) +{ + arch_manage_bp(bp, BP_SLOT_ACTION_UNINSTALL); } =20 static int arch_bp_generic_len(int x86_len) From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 73200481B1; Wed, 15 Jul 2026 01:45:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079927; cv=none; b=bxsplz2UyoaNccL3KOPI34KL28xObe6NKWHtJsppeNwC5mefQc9OilUNLeh+NNAyvjMhlttEs2eal/z47Qm3yWkGfiPfIEpJ5HTBg9v/r5GdQm3W1MzmDw2I6d/C75365TXY4RpYCOz0E26lNDtB0af5xMmPwIQkCUpyBlfT4aE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079927; c=relaxed/simple; bh=2fcSX4kIwFIDKw76TQlcptyp8280Ha4c+S8Uiz27Pus=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JZJTwFV5j3YBXmzadzVFrXwaJZCTs7KXJ8CIuM7a9sbk+SMob1YL93Vl40ZO5A5oNr0rEwb8fO03zfh9vnYquFFZZJ+wJKgWeaSD7FcE7ZyE5518FOOv0znWQf6bfxztTd8xaYsB0GpKGjC01E5OmyC7EXzTb7IYO7Lj4uNiDpQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HqlARus6; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HqlARus6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7004E1F000E9; Wed, 15 Jul 2026 01:45:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079926; bh=4B52rB4RAOUe4XE25Snyjh89ZAhqqDjce8UOM+tAp3M=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=HqlARus67PjzW/EhW1vyTOxSU/8cLNQWn8K+3A0ie30rxaEgnU6ofBAt5jSP/sh5p adnTut7tYlPfh5TBQ6SoNI6R4tDjsAsSl3Olg7k7vnSQdP4Jw4PSW2OKzyeeBNQwcW rAai42ejNXl9hDsIqBlXRF8SS+AmVNYeKn0yAEXbZWjn6Dg7JIyq4RKP8V6MXIMdMg KRxolFdq/2NsPBcqno9xXNJjyMWSqSecEQ0kuFkBhAWLOqPXP2CZy3Q9eZe8ykE2Ye jWXoMnj1rPRO1DSmYcFIkc4jlsbsdgrlBACyXx3bMjrjT+dwuAEeorDFcUGE4jc8aj CjV1MU4NQQTzQ== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 07/10] x86/hw_breakpoint: Add arch_reinstall_hw_breakpoint Date: Wed, 15 Jul 2026 10:45:20 +0900 Message-ID: <178407992003.95826.8437044383333615344.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Jinchao Wang 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 Reviewed-by: Masami Hiramatsu (Google) --- arch/x86/include/asm/hw_breakpoint.h | 2 ++ arch/x86/kernel/hw_breakpoint.c | 9 +++++++++ 2 files changed, 11 insertions(+) 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 { =20 enum bp_slot_action { BP_SLOT_ACTION_INSTALL, + BP_SLOT_ACTION_REINSTALL, BP_SLOT_ACTION_UNINSTALL, }; =20 @@ -65,6 +66,7 @@ extern int hw_breakpoint_exceptions_notify(struct notifie= r_block *unused, =20 =20 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_breakpoin= t.c index 877509539300..9af8d81075db 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 =3D NULL; new_bp =3D bp; break; + case BP_SLOT_ACTION_REINSTALL: + old_bp =3D bp; + new_bp =3D bp; + break; case BP_SLOT_ACTION_UNINSTALL: old_bp =3D bp; new_bp =3D NULL; @@ -188,6 +192,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp) return arch_manage_bp(bp, BP_SLOT_ACTION_INSTALL); } =20 +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); From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DA357248F7C; Wed, 15 Jul 2026 01:45:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079939; cv=none; b=HW40sTUQzrjd5242NC1NyGW0juIoT2SzxEashhyDXmsjnxnDwFcJHcJfzSXPh+V1jWwQama5Ivlstc6g6JB2hU+Bb2SvZa8PBcRm681myUppUMyqqtFXky05v0Ee3XVeqScZlMIHW0XfVTKF8goudcWRKZe4mcue/Io6UOQqLHo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079939; c=relaxed/simple; bh=TYkoOJ5jA0d1Zo2Jy2WroUs8zmcvWyGC5VHZb8FKrSc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=TL+E26EgxgVbhF8FbV4ErPta3v3rxLZ8a6N9y2PJvX+YztSLHGkF584tI+Mn0BqbRoEc/xUi79bE+X4D9fu28u7qcqoqEs6KPo+gxhifKcEtp4OyNnIS+5geHzwjorRocA3Rw+RQGdmdm4NbvT46/9tYByCIkA9WC/6CB4N7lqA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=loG9gkzT; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="loG9gkzT" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C9AA81F000E9; Wed, 15 Jul 2026 01:45:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079937; bh=IJryciSq3k4BPjrtKiKqhDvoLAoGQEetNuwDHTkN2J8=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=loG9gkzTjyGUfwkMhvt7BpFQkHqBNLTAZis1Ars9CaKMeYY1nyCoNHmo7rr5mHxuL Cq6NXotKV7SfU39qip67YUMA0H/eoy/2i61HBDH8AAmtTtbu2rnY+gegQ95MH9C8/m GhVCxAC0uwS5M0kXEgd216sMMxySs9KCaV0nyEwqnmlPJwQjaIFoojWXFvmbvhAkcw +maIM4finbkwaSwa/JiyRgD7eeHLhr0/zOR4SOd2tH3SFh3+3+ZCDGxe+DtxQs+I5c +i6jVZuuwCKkzPv6RXROKD3ILNuE2088DV+2uY7IkExcQ+wQI1831BDdRB1nb7YbGo 4MdwGuKIlsKnQ== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 08/10] HWBP: Add modify_wide_hw_breakpoint_local() API Date: Wed, 15 Jul 2026 10:45:31 +0900 Message-ID: <178407993140.95826.16095381350026536181.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) Add modify_wide_hw_breakpoint_local() arch-wide interface which allows hwbp users to update watch address on-line. This is available if the arch supports CONFIG_HAVE_REINSTALL_HW_BREAKPOINT. Note that this allows to change the type only for compatible types, because it does not release and reserve the hwbp slot based on type. For instance, you can not change HW_BREAKPOINT_W to HW_BREAKPOINT_X. Signed-off-by: Masami Hiramatsu (Google) --- Changes in v7: - Update bp->attr.bp_attr so that we can correctly check the address on it. - Use -EOPNOTSUPP instead of -ENOSYS. Changes in v4: - Update kerneldoc comment about modify_wide_hw_breakpoint_local according to Randy's comment. Changes in v2: - Check type compatibility by checking slot. (Thanks Jinchao!) --- arch/Kconfig | 10 ++++++++++ arch/x86/Kconfig | 1 + include/linux/hw_breakpoint.h | 6 ++++++ kernel/events/hw_breakpoint.c | 39 +++++++++++++++++++++++++++++++++++++= ++ 4 files changed, 56 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index 959aee9568ff..4a87e843bb4c 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -467,6 +467,16 @@ config HAVE_POST_BREAKPOINT_HOOK Select this option if your arch implements breakpoints overflow handler hooks after the target memory is modified. =20 +config HAVE_REINSTALL_HW_BREAKPOINT + bool + depends on HAVE_HW_BREAKPOINT + help + Depending on the arch implementation of hardware breakpoints, + some of them are able to update the breakpoint configuration + without release and reserve the hardware breakpoint register. + What configuration is able to update depends on hardware and + software implementation. + config HAVE_USER_RETURN_NOTIFIER bool =20 diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 6b7e14ef8cfb..588218da8f41 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -247,6 +247,7 @@ config X86 select HAVE_GCC_PLUGINS select HAVE_HW_BREAKPOINT select HAVE_POST_BREAKPOINT_HOOK + select HAVE_REINSTALL_HW_BREAKPOINT 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..6754ffbee9ed 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); =20 +extern int modify_wide_hw_breakpoint_local(struct perf_event *bp, + struct perf_event_attr *attr); + 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 *cp= u_events); @@ -124,6 +127,9 @@ register_wide_hw_breakpoint(struct perf_event_attr *att= r, perf_overflow_handler_t triggered, void *context) { return NULL; } static inline int +modify_wide_hw_breakpoint_local(struct perf_event *bp, + struct perf_event_attr *attr) { 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..4337688da397 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -888,6 +888,45 @@ void unregister_wide_hw_breakpoint(struct perf_event *= __percpu *cpu_events) } EXPORT_SYMBOL_GPL(unregister_wide_hw_breakpoint); =20 +/** + * modify_wide_hw_breakpoint_local - update breakpoint config for local CPU + * @bp: the hwbp perf event for this CPU + * @attr: the new attribute for @bp + * + * This does not release and reserve the slot of a HWBP; it just reuses the + * current slot on local CPU. So the users must update the other CPUs by + * themselves. + * Also, since this does not release/reserve the slot, this can not change= the + * type to incompatible type of the HWBP. + * Return err if attr is invalid or the CPU fails to update debug register + * for new @attr. + */ +#ifdef CONFIG_HAVE_REINSTALL_HW_BREAKPOINT +int modify_wide_hw_breakpoint_local(struct perf_event *bp, + struct perf_event_attr *attr) +{ + int ret; + + if (find_slot_idx(bp->attr.bp_type) !=3D find_slot_idx(attr->bp_type)) + return -EINVAL; + + ret =3D hw_breakpoint_arch_parse(bp, attr, counter_arch_bp(bp)); + if (ret) + return ret; + + bp->attr.bp_addr =3D attr->bp_addr; + + return arch_reinstall_hw_breakpoint(bp); +} +#else +int modify_wide_hw_breakpoint_local(struct perf_event *bp, + struct perf_event_attr *attr) +{ + return -EOPNOTSUPP; +} +#endif +EXPORT_SYMBOL_GPL(modify_wide_hw_breakpoint_local); + /** * hw_breakpoint_is_used - check if breakpoints are currently used * From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 046511F09AD; Wed, 15 Jul 2026 01:45:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079951; cv=none; b=bxtf087CH5U2N+QTGn+DXuiJGdaM/WMh6K0fpThvJNKTq6mX3AK0TzRAiNB2IacdK2YbOyAsvoaA0YrBp64kJbmmP4xRla+IihU56Tml97E/isA5fMQ/JGQU+tjO87Oirhol7rIvjPufAnIJEyifbzFUCNaJZhU9RDpgazSSSkQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079951; c=relaxed/simple; bh=naQ9FF9eHea8+3fVcGgWdxcymt5D/vTNbZcaIJZhOg8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=UC/tSACFyZ3wag0MfhTOsjNnpayf+QWtWTZQHnwnMepYZQz0k5VcKHjqC7bmDVgtV3b2oWX/hR6xWegLmur6li57Od8o7WtFNYXJeUJTJCBzLxg8GgZx1lhCxfAe9PavV4mdDC4osSR0yolnNMczVGIc887hnaguF/iffXNckmc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mDSayB0S; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="mDSayB0S" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2E2331F000E9; Wed, 15 Jul 2026 01:45:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079949; bh=OI+0vKlPZWXV69ghqhFaCSmuG32q356fhraUhOQ+LgQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=mDSayB0SK7qZ8FNWRncfrnJO1BIIJVZezAYNbaJq1zdmgiIWYdGjEY6l+fUpOANiT VZti0OAg9GhFI8FArlRf6QkJjB+MZIN4xbmmeZzQmSNV931zcTs8syOv3NpaIII2GU 1iYO4PwCDfceaTeKTQjudhArbmBdzVmYvjSdiHW2xiVl0OdWDLG/f1GkgAI4cOlntI 2xxgwHyOvcLeKcYMrbAeOChC/Z4m1gOO0yIUYz3JLR93/dpGx8CLGrLIsS0Kf+E4vL J4na3fiqVHfIkt6zgrvRzQKK1H2pqK76fGhnHjnp5IJ6YZwCxXohYD3dcUA3qUFptQ CpUSpRBUOkbFA== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 09/10] tracing: wprobe: Add wprobe event trigger Date: Wed, 15 Jul 2026 10:45:43 +0900 Message-ID: <178407994286.95826.437098855356129625.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) Add wprobe event trigger to set and clear the watch event dynamically. This allows us to set an watchpoint on a given local variables and a slab object instead of static objects. The trigger syntax is below: - set_wprobe:WPROBE:FIELD[+OFFSET] [if FILTER] - clear_wprobe:WPROBE[:FIELD[+OFFSET]] [if FILTER] set_wprobe sets the address pointed by FIELD[+offset] to the WPROBE event. The FIELD is the field name of trigger event. clear_wprobe clears the watch address of WPROBE event. If the FIELD option is specified, it clears only if the current watch address is same as the given FIELD[+OFFSET] value. The set_wprobe trigger does not change the type and length, these must be set when creating a new wprobe. Also, the WPROBE event must be disabled when setting the new trigger and it will be busy afterwards. Recommended usage is to add a new wprobe at NULL address and keep disabled. Signed-off-by: Masami Hiramatsu (Google) --- Changes in v7: - Use kzalloc_obj(). - Update sample code in document. Changes in v6: - Update according to the latest change of trigger ops. Changes in v5: - Following the suggestions, the documentation was revised to suit rst. Changes in v3: - Add FIELD option support for clear_wprobe and update document. - Fix to unregister/free event_trigger_data on file correctly. - Fix syntax comments. Changes in v2: - Getting local cpu perf_event from trace_wprobe directly. - Remove trace_wprobe_local_perf() because it is conditionally unused. - Make CONFIG_WPROBE_TRIGGERS a hidden config. --- Documentation/trace/wprobetrace.rst | 89 ++++++++ include/linux/trace_events.h | 1=20 kernel/trace/Kconfig | 10 + kernel/trace/trace_wprobe.c | 415 +++++++++++++++++++++++++++++++= ++++ 4 files changed, 515 insertions(+) diff --git a/Documentation/trace/wprobetrace.rst b/Documentation/trace/wpro= betrace.rst index 025b4c39b809..6f3d2afb35e3 100644 --- a/Documentation/trace/wprobetrace.rst +++ b/Documentation/trace/wprobetrace.rst @@ -67,3 +67,92 @@ Here is an example to add a wprobe event on a variable `= jiffies`. -0 [000] d.Z1. 717.026373: my_jiffies: (tick_do_up= date_jiffies64+0xbe/0x130) =20 You can see the code which writes to `jiffies` is `tick_do_update_jiffies6= 4()`. + +Combination with trigger action +------------------------------- +The event trigger action can extend the utilization of this wprobe. + +- set_wprobe:WPEVENT:FIELD[+|-ADJUST] +- clear_wprobe:WPEVENT[:FIELD[+|-]ADJUST] + +Set these triggers to the target event, then the WPROBE event will be +setup to trace the memory access at FIELD[+|-ADJUST] address. +When clear_wprobe is hit, if FIELD is NOT specified, the WPEVENT is +forcibly cleared. If FIELD[[+|-]ADJUST] is set, it clears WPEVENT only +if its watching address is the same as the FIELD[[+|-]ADJUST] value. + +Notes: +The set_wprobe trigger does not change the type and length, these +must be set when creating a new wprobe. + +The WPROBE event must be disabled when setting the new trigger +and it will be busy afterwards. Recommended usage is to add a new +wprobe at NULL address and keep disabled. + + +For example, trace the first 8 bytes of the dentry data structure passed +to do_truncate() until it is deleted by dentry_kill(). +(Note: all tracefs setup uses '>>' so that it does not kick do_truncate()) +:: + + # echo 'w:watch rw@0:8 address=3D$addr value=3D+0($addr)' >> dynamic_eve= nts + # echo 'f:truncate do_truncate dentry=3D$arg2' >> dynamic_events + # echo 'set_wprobe:watch:dentry' >> events/fprobes/truncate/trigger + # echo 'f:dentry_kill dentry_kill dentry=3D$arg1' >> dynamic_events + # echo 'clear_wprobe:watch:dentry' >> events/fprobes/dentry_kill/trigger + # echo 1 >> events/fprobes/truncate/enable + # echo 1 >> events/fprobes/dentry_kill/enable + + # echo aaa > /tmp/hoge + # echo bbb > /tmp/hoge + # echo ccc > /tmp/hoge + # rm /tmp/hoge + +Then, the trace data will show:: + + # tracer: nop + # + # entries-in-buffer/entries-written: 32/32 #P:8 + # + # _-----=3D> irqs-off/BH-disabled + # / _----=3D> need-resched + # | / _---=3D> hardirq/softirq + # || / _--=3D> preempt-depth + # ||| / _-=3D> migrate-disable + # |||| / delay + # TASK-PID CPU# ||||| TIMESTAMP FUNCTION + # | | | ||||| | | + sh-107 [004] ...1. 9.990418: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff888004ad6618 + sh-107 [004] ...1. 9.990914: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff888004b3de78 + sh-107 [004] ...1. 9.993175: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff8880049ddd40 + sh-107 [004] ..... 9.995198: truncate: (do_truncate= +0x4/0x120) dentry=3D0xffff8880048083a8 + sh-107 [004] ...1. 9.995389: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff8880049db998 + sh-107 [004] ..Zff 9.997503: watch: (lookup_fast+0x= aa/0x150) address=3D0xffff8880048083a8 value=3D0x8200080 + sh-107 [004] ..Zff 9.997509: watch: (path_openat+0x= 211/0xda0) address=3D0xffff8880048083a8 value=3D0x8200080 + sh-107 [004] ..Zff 9.997514: watch: (path_openat+0x= a56/0xda0) address=3D0xffff8880048083a8 value=3D0x8200080 + sh-107 [004] ..Zff 9.997518: watch: (path_openat+0x= ae2/0xda0) address=3D0xffff8880048083a8 value=3D0x8200080 + sh-107 [004] ..... 9.997521: truncate: (do_truncate= +0x4/0x120) dentry=3D0xffff8880048083a8 + sh-107 [004] ...1. 9.997582: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff888004808270 + sh-107 [004] ...1. 9.999365: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff8880049db728 + sh-107 [004] ...1. 9.999388: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff888004b1c000 + rm-113 [005] ..Zff 10.000965: watch: (lookup_fast+0x= aa/0x150) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] ..Zff 10.000971: watch: (path_lookupat+= 0x97/0x1e0) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] ..Zff 10.000984: watch: (lookup_fast+0x= aa/0x150) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] ..Zff 10.000988: watch: (path_lookupat+= 0x97/0x1e0) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] ..Zff 10.001010: watch: (lookup_one_qst= r_excl+0x28/0x140) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] ..Zff 10.001014: watch: (lookup_one_qst= r_excl+0xd1/0x140) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] ..Zff 10.001018: watch: (may_delete_den= try+0x1c/0x200) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] ..Zff 10.001021: watch: (may_delete_den= try+0x195/0x200) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] ..Zff 10.001031: watch: (vfs_unlink+0x5= e/0x260) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] d.Z.. 10.001067: watch: (d_make_discard= able+0x1b/0x40) address=3D0xffff8880048083a8 value=3D0x8200080 + rm-113 [005] d.Z.. 10.001071: watch: (d_make_discard= able+0x29/0x40) address=3D0xffff8880048083a8 value=3D0x200080 + rm-113 [005] ...1. 10.001072: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff8880048083a8 + rm-113 [005] ...1. 10.001218: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff8880048083a8 + sh-107 [004] ...1. 10.001416: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff8880049db110 + sh-107 [004] ...1. 10.001444: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff8880049db248 + sh-107 [004] ...1. 10.001500: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff888004ad6618 + sh-107 [004] ...1. 10.002067: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff888004b41e78 + sh-107 [004] ...1. 10.904920: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff888004b41e78 + sh-107 [004] ...1. 10.905129: dentry_kill: (dentry_k= ill+0x0/0x2c0) dentry=3D0xffff888004ad6618 + +You can see the watch event is correctly configured on the dentry. diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index d1e5ab71d928..e6f3bbcbb9af 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -729,6 +729,7 @@ enum event_trigger_type { ETT_EVENT_HIST =3D (1 << 4), ETT_HIST_ENABLE =3D (1 << 5), ETT_EVENT_EPROBE =3D (1 << 6), + ETT_EVENT_WPROBE =3D (1 << 7), }; =20 extern int filter_match_preds(struct event_filter *filter, void *rec); diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index d9b6fa5c35d9..af570fa2a882 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -876,6 +876,16 @@ config WPROBE_EVENTS Those events can be inserted wherever hardware breakpoints can be set, and record accessed memory address and values. =20 +config WPROBE_TRIGGERS + depends on WPROBE_EVENTS + depends on HAVE_REINSTALL_HW_BREAKPOINT + bool + default y + help + This adds an event trigger which will set the wprobe on a specific + field of an event. This allows user to trace the memory access of + an address pointed by the event field. + config BPF_EVENTS depends on BPF_SYSCALL depends on (KPROBE_EVENTS || UPROBE_EVENTS) && PERF_EVENTS diff --git a/kernel/trace/trace_wprobe.c b/kernel/trace/trace_wprobe.c index dd310a87b333..1812b1e74111 100644 --- a/kernel/trace/trace_wprobe.c +++ b/kernel/trace/trace_wprobe.c @@ -6,6 +6,8 @@ */ #define pr_fmt(fmt) "trace_wprobe: " fmt =20 +#include +#include #include #include #include @@ -14,11 +16,14 @@ #include #include #include +#include #include #include +#include =20 #include =20 +#include "trace.h" #include "trace_dynevent.h" #include "trace_output.h" #include "trace_probe.h" @@ -691,3 +696,413 @@ static __init int init_wprobe_trace(void) } fs_initcall(init_wprobe_trace); =20 +#ifdef CONFIG_WPROBE_TRIGGERS + +static int wprobe_trigger_global_enabled; + +#define SET_WPROBE_STR "set_wprobe" +#define CLEAR_WPROBE_STR "clear_wprobe" +#define WPROBE_DEFAULT_CLEAR_ADDRESS ((unsigned long)&wprobe_trigger_globa= l_enabled) + +struct wprobe_trigger_data { + struct trace_event_file *file; + struct trace_wprobe *tw; + + struct perf_event_attr attr; + raw_spinlock_t lock; /* lock protects attr */ + struct work_struct work;// TBD: use work + IPI or use sched/raw_syscall e= vent? + unsigned int offset; + long adjust; + const char *field; + // size must be unsigned long because it should be an address. + bool clear; +}; + +static int trace_wprobe_update_local(struct trace_wprobe *tw, + struct perf_event_attr *attr) +{ + struct perf_event *bp =3D *this_cpu_ptr(tw->bp_event); + + return modify_wide_hw_breakpoint_local(bp, attr); +} + +static void wprobe_smp_update_func(void *data) +{ + struct wprobe_trigger_data *trigger_data =3D data; + unsigned long flags; + + raw_spin_lock_irqsave(&trigger_data->lock, flags); + trace_wprobe_update_local(trigger_data->tw, &trigger_data->attr); + raw_spin_unlock_irqrestore(&trigger_data->lock, flags); +} + +static void wprobe_work_func(struct work_struct *work) +{ + struct wprobe_trigger_data *data =3D container_of(work, struct wprobe_tri= gger_data, work); + + on_each_cpu(wprobe_smp_update_func, data, false); +} + +static void wprobe_trigger(struct event_trigger_data *data, + struct trace_buffer *buffer, void *rec, + struct ring_buffer_event *event) +{ + struct wprobe_trigger_data *wprobe_data =3D data->private_data; + struct perf_event_attr *attr =3D &wprobe_data->attr; + struct trace_wprobe *tw =3D wprobe_data->tw; + unsigned long addr, flags; + int ret =3D -EBUSY; + + addr =3D *(unsigned long *)(rec + wprobe_data->offset); + addr +=3D wprobe_data->adjust; + + raw_spin_lock_irqsave(&wprobe_data->lock, flags); + + if (!wprobe_data->clear) { + if (tw->addr !=3D WPROBE_DEFAULT_CLEAR_ADDRESS) + goto unlock; + + tw->addr =3D attr->bp_addr =3D addr; + ret =3D trace_wprobe_update_local(tw, attr); + if (WARN_ON_ONCE(ret)) + goto unlock; + clear_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &wprobe_data->file->flags); + } else { + if (tw->addr =3D=3D WPROBE_DEFAULT_CLEAR_ADDRESS) + goto unlock; + if (wprobe_data->field && tw->addr !=3D addr) + goto unlock; + + tw->addr =3D attr->bp_addr =3D WPROBE_DEFAULT_CLEAR_ADDRESS; + ret =3D trace_wprobe_update_local(tw, attr); + if (WARN_ON_ONCE(ret)) + goto unlock; + set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &wprobe_data->file->flags); + } + schedule_work(&wprobe_data->work); +unlock: + raw_spin_unlock_irqrestore(&wprobe_data->lock, flags); +} + +static void free_wprobe_trigger_data(struct wprobe_trigger_data *wprobe_da= ta) +{ + if (wprobe_data) + kfree(wprobe_data->field); + kfree(wprobe_data); +} + +DEFINE_FREE(free_wprobe_trigger_data, struct wprobe_trigger_data *, free_w= probe_trigger_data(_T)); + +static int wprobe_trigger_print(struct seq_file *m, + struct event_trigger_data *data) +{ + struct wprobe_trigger_data *wprobe_data =3D data->private_data; + + if (wprobe_data->clear) { + seq_printf(m, "%s:%s", CLEAR_WPROBE_STR, + trace_event_name(wprobe_data->file->event_call)); + if (wprobe_data->field) { + seq_printf(m, ":%s%+ld", + wprobe_data->field, wprobe_data->adjust); + } + } else + seq_printf(m, "%s:%s:%s%+ld", SET_WPROBE_STR, + trace_event_name(wprobe_data->file->event_call), + wprobe_data->field, wprobe_data->adjust); + + if (data->filter_str) + seq_printf(m, " if %s\n", data->filter_str); + else + seq_putc(m, '\n'); + + return 0; +} + +static struct wprobe_trigger_data * +wprobe_trigger_alloc(struct trace_wprobe *tw, struct trace_event_file *fil= e, + bool clear) +{ + struct wprobe_trigger_data *wprobe_data; + struct perf_event_attr *attr; + + wprobe_data =3D kzalloc_obj(*wprobe_data); + if (!wprobe_data) + return NULL; + + wprobe_data->tw =3D tw; + wprobe_data->clear =3D clear; + wprobe_data->file =3D file; + + attr =3D &wprobe_data->attr; + hw_breakpoint_init(attr); + attr->bp_type =3D tw->type; + attr->bp_addr =3D WPROBE_DEFAULT_CLEAR_ADDRESS; + attr->bp_len =3D tw->len; + + raw_spin_lock_init(&wprobe_data->lock); + INIT_WORK(&wprobe_data->work, wprobe_work_func); + + return wprobe_data; +} + +static void wprobe_trigger_free(struct event_trigger_data *data) +{ + struct wprobe_trigger_data *wprobe_data =3D data->private_data; + + if (WARN_ON_ONCE(data->ref <=3D 0)) + return; + + data->ref--; + if (!data->ref) { + /* Remove the SOFT_MODE flag */ + trace_event_enable_disable(wprobe_data->file, 0, 1); + trace_event_put_ref(wprobe_data->file->event_call); + trigger_data_free(data); + free_wprobe_trigger_data(wprobe_data); + } +} + +static int wprobe_trigger_cmd_parse(struct event_command *cmd_ops, + struct trace_event_file *file, + char *glob, char *cmd, + char *param_and_filter) +{ + /* + * set_wprobe:EVENT:FIELD[+OFFS] + * clear_wprobe:EVENT[:FIELD[+OFFS]] + */ + struct wprobe_trigger_data *wprobe_data __free(free_wprobe_trigger_data) = =3D NULL; + struct event_trigger_data *trigger_data __free(kfree) =3D NULL; + struct ftrace_event_field *field =3D NULL; + struct trace_event_file *wprobe_file; + struct trace_array *tr =3D file->tr; + struct trace_event_call *event; + struct perf_event_attr *attr; + char *event_str, *field_str; + bool remove, clear =3D false; + struct trace_wprobe *tw; + char *param, *filter; + int ret; + + remove =3D event_trigger_check_remove(glob); + + if (!strcmp(cmd, CLEAR_WPROBE_STR)) + clear =3D true; + + if (event_trigger_empty_param(param_and_filter)) + return -EINVAL; + + ret =3D event_trigger_separate_filter(param_and_filter, ¶m, &filter, = true); + if (ret) + return ret; + + event_str =3D strsep(¶m, ":"); + + /* Find target wprobe */ + tw =3D find_trace_wprobe(event_str, WPROBE_EVENT_SYSTEM); + if (!tw) + return -ENOENT; + /* The target wprobe must not be used (unless clear) */ + if (!remove && !clear && trace_probe_is_enabled(&tw->tp)) + return -EBUSY; + + wprobe_file =3D find_event_file(tr, WPROBE_EVENT_SYSTEM, event_str); + if (!wprobe_file) + return -EINVAL; + + wprobe_data =3D wprobe_trigger_alloc(tw, wprobe_file, clear); + if (!wprobe_data) + return -ENOMEM; + attr =3D &wprobe_data->attr; + + /* Find target field, which must be equivarent to "void *" */ + field_str =3D strsep(¶m, ":"); + /* trigger removing or clear_wprobe does not need field. */ + if (!remove && !clear && !field_str) + return -EINVAL; + + if (field_str) { + char *offs; + + offs =3D strpbrk(field_str, "+-"); + if (offs) { + long val; + + if (kstrtol(offs, 0, &val) < 0) + return -EINVAL; + wprobe_data->adjust =3D val; + *offs =3D '\0'; + } + + event =3D file->event_call; + field =3D trace_find_event_field(event, field_str); + if (!field) + return -ENOENT; + + if (field->size !=3D sizeof(void *)) + return -ENOEXEC; + wprobe_data->offset =3D field->offset; + wprobe_data->field =3D kstrdup(field_str, GFP_KERNEL); + if (!wprobe_data->field) + return -ENOMEM; + } + + trigger_data =3D trigger_data_alloc(cmd_ops, cmd, param, wprobe_data); + if (!trigger_data) + return -ENOMEM; + + /* Up the trigger_data count to make sure nothing frees it on failure */ + event_trigger_init(trigger_data); + + if (remove) { + event_trigger_unregister(cmd_ops, file, glob+1, trigger_data); + return 0; + } + + ret =3D event_trigger_parse_num(param, trigger_data); + if (ret) + return ret; + + ret =3D event_trigger_set_filter(cmd_ops, file, filter, trigger_data); + if (ret < 0) + return ret; + + /* Soft-enable (register) wprobe event on WPROBE_DEFAULT_CLEAR_ADDRESS */ + tw->addr =3D attr->bp_addr =3D WPROBE_DEFAULT_CLEAR_ADDRESS; + ret =3D trace_event_enable_disable(wprobe_file, 1, 1); + if (ret < 0) { + event_trigger_reset_filter(cmd_ops, trigger_data); + return ret; + } + ret =3D event_trigger_register(cmd_ops, file, glob, trigger_data); + if (ret) { + event_trigger_reset_filter(cmd_ops, trigger_data); + trace_event_enable_disable(wprobe_file, 0, 1); + return ret; + } + /* Make it NULL to avoid freeing trigger_data and wprobe_data by __free()= */ + trigger_data =3D NULL; + wprobe_data =3D NULL; + + return 0; +} + +/* Return event_trigger_data if there is a trigger which points the same w= probe */ +static struct event_trigger_data * +wprobe_trigger_find_same(struct event_trigger_data *test, + struct trace_event_file *file) +{ + struct wprobe_trigger_data *test_wprobe_data =3D test->private_data; + struct wprobe_trigger_data *wprobe_data; + struct event_trigger_data *iter; + + list_for_each_entry(iter, &file->triggers, list) { + wprobe_data =3D iter->private_data; + if (!wprobe_data || + iter->cmd_ops->trigger_type !=3D + test->cmd_ops->trigger_type) + continue; + if (wprobe_data->tw =3D=3D test_wprobe_data->tw) + return iter; + } + return NULL; +} + +static int wprobe_register_trigger(char *glob, + struct event_trigger_data *data, + struct trace_event_file *file) +{ + int ret =3D 0; + + lockdep_assert_held(&event_mutex); + + /* The same wprobe is not accept on the same file (event) */ + if (wprobe_trigger_find_same(data, file)) + return -EEXIST; + + if (data->cmd_ops->init) { + ret =3D data->cmd_ops->init(data); + if (ret < 0) + return ret; + } + + list_add_rcu(&data->list, &file->triggers); + + update_cond_flag(file); + ret =3D trace_event_trigger_enable_disable(file, 1); + if (ret < 0) { + list_del_rcu(&data->list); + update_cond_flag(file); + } + return ret; +} + +static void wprobe_unregister_trigger(char *glob, + struct event_trigger_data *test, + struct trace_event_file *file) +{ + struct event_trigger_data *data; + + lockdep_assert_held(&event_mutex); + + data =3D wprobe_trigger_find_same(test, file); + if (!data) + return; + + list_del_rcu(&data->list); + trace_event_trigger_enable_disable(file, 0); + update_cond_flag(file); + if (data->cmd_ops->free) + data->cmd_ops->free(data); +} + +static struct event_command trigger_wprobe_set_cmd =3D { + .name =3D SET_WPROBE_STR, + .trigger_type =3D ETT_EVENT_WPROBE, + /* This triggers after when the event is recorded. */ + .flags =3D EVENT_CMD_FL_NEEDS_REC, + .parse =3D wprobe_trigger_cmd_parse, + .reg =3D wprobe_register_trigger, + .unreg =3D wprobe_unregister_trigger, + .set_filter =3D set_trigger_filter, + .trigger =3D wprobe_trigger, + .count_func =3D event_trigger_count, + .print =3D wprobe_trigger_print, + .init =3D event_trigger_init, + .free =3D wprobe_trigger_free, +}; + +static struct event_command trigger_wprobe_clear_cmd =3D { + .name =3D CLEAR_WPROBE_STR, + .trigger_type =3D ETT_EVENT_WPROBE, + /* This triggers after when the event is recorded. */ + .flags =3D EVENT_CMD_FL_NEEDS_REC, + .parse =3D wprobe_trigger_cmd_parse, + .reg =3D wprobe_register_trigger, + .unreg =3D wprobe_unregister_trigger, + .set_filter =3D set_trigger_filter, + .trigger =3D wprobe_trigger, + .count_func =3D event_trigger_count, + .print =3D wprobe_trigger_print, + .init =3D event_trigger_init, + .free =3D wprobe_trigger_free, +}; + +static __init int init_trigger_wprobe_cmds(void) +{ + int ret; + + ret =3D register_event_command(&trigger_wprobe_set_cmd); + if (WARN_ON(ret < 0)) + return ret; + ret =3D register_event_command(&trigger_wprobe_clear_cmd); + if (WARN_ON(ret < 0)) + unregister_event_command(&trigger_wprobe_set_cmd); + + if (!ret) + wprobe_trigger_global_enabled =3D 1; + + return ret; +} +fs_initcall(init_trigger_wprobe_cmds); +#endif /* CONFIG_WPROBE_TRIGGERS */ From nobody Sat Jul 25 18:05:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 44578265621; Wed, 15 Jul 2026 01:46:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079962; cv=none; b=q/JqeYGUzFz8GdaoX0ZApN70K/u0DhI0fYicLTXWAiOGBZL9JMigPwBJwMxau5dr6Vp1Rzph1I7tLJrmj0jC3zybQ5Z9JnKR/e1PlQjSJoBGrD2yPY7SUGvvHNbzgaEJVXKpJ3fc+rjL447UF2yOYGBA0QG0lA9/cKvchRKOIfE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784079962; c=relaxed/simple; bh=rVJjGKr3S17TbNUAdbL+4V5+FKj6xNfm6cz6+ooptS8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Ff64+Bbcxt/k/eOqTi5ZP7009Uy+sS26971BBLQFtFFTG9l9SXW5PXBV45nrR17zLFvIkPN4LHUuRqfTon6e6O8dhvxFkfEeTENI640L5KOahB+rdsJ8FH6XftIQd6kaz/Zz5NFlNOVidb3K9huwnF+cOMwqTKeyHBE4ExR3Bt4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cmwpOsy4; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="cmwpOsy4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5C54D1F000E9; Wed, 15 Jul 2026 01:45:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784079961; bh=O7i/gpCoexgVrVsCcRbivjoHnZ4LRYbdacut/L4Lst4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=cmwpOsy44aG3Yj3AQwXEKwkYlCzcvBxaWAAnWnkKWhDpMpWLfKZG1OE/PBUMcNgGS /AzaPSPmdgls84UvQvv6P260IGZZkS6j6m3vuhzB8Pggv6juQwdgU8iwxQfaDwL+uP Y90yZ8v1NtYwYuU2RhFE0ImJ6HylpEjT8V9cgPaF/cT4oHAwWStPUdqNiKlFJV3paW 98Dk5iQq7vjbLMF6B3RVRWrgTXXHnvubaiyPIica7/1HR3sHPK3HHOrEDBf4+/JvSC lfozewtPkfvVh2cTvWxAdzG6zn7zTCcDhEsZWSUsPYl5iCWvPbzHDd8U5ymkeZ7trw YddTrXQMaQkYg== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Peter Zijlstra , Ingo Molnar , x86@kernel.org Cc: Jinchao Wang , Mathieu Desnoyers , Masami Hiramatsu , Thomas Gleixner , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , Alexander Shishkin , Ian Rogers , linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-perf-users@vger.kernel.org Subject: [PATCH v7 10/10] selftests: ftrace: Add wprobe trigger testcase Date: Wed, 15 Jul 2026 10:45:55 +0900 Message-ID: <178407995495.95826.11582585035165074036.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <178407983818.95826.12714571928538799781.stgit@devnote2> References: <178407983818.95826.12714571928538799781.stgit@devnote2> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) Add a testcase for checking wprobe trigger. This sets set_wprobe and clear_wprobe triggers on fprobe events to watch dentry access. So this depends on both wprobe and fprobe. Signed-off-by: Masami Hiramatsu (Google) --- Changes in v7: - Add a newline at the end of file.(style fix) - Use dentry_kill instead of __dentry_kill. Changes in v5: - Enable CONFIG_WPROBE_TRIGGERS in the config for ftrace test. Changes in v3: - Newly added. --- tools/testing/selftests/ftrace/config | 1=20 .../ftrace/test.d/trigger/trigger-wprobe.tc | 48 ++++++++++++++++= ++++ 2 files changed, 49 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-w= probe.tc diff --git a/tools/testing/selftests/ftrace/config b/tools/testing/selftest= s/ftrace/config index d2f503722020..ecdee77f360f 100644 --- a/tools/testing/selftests/ftrace/config +++ b/tools/testing/selftests/ftrace/config @@ -28,3 +28,4 @@ CONFIG_TRACER_SNAPSHOT=3Dy CONFIG_UPROBES=3Dy CONFIG_UPROBE_EVENTS=3Dy CONFIG_WPROBE_EVENTS=3Dy +CONFIG_WPROBE_TRIGGERS=3Dy diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe.t= c b/tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe.tc new file mode 100644 index 000000000000..cc7a0532d7ff --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe.tc @@ -0,0 +1,48 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: event trigger - test wprobe trigger +# requires: dynamic_events "w[:[/][]] [r|w|rw]@[:= ]":README events/sched/sched_process_fork/trigger + +echo 0 > tracing_on + +:;: "Add a wprobe event used by trigger" ;: +echo 'w:watch rw@0:8 address=3D$addr value=3D+0($addr)' > dynamic_events + +:;: "Add events for triggering wprobe" ;: +echo 'f:truncate do_truncate dentry=3D$arg2' >> dynamic_events +echo 'f:dentry_kill dentry_kill dentry=3D$arg1' >> dynamic_events + +:;: "Add wprobe triggers" ;: +echo 'set_wprobe:watch:dentry' >> events/fprobes/truncate/trigger +echo 'clear_wprobe:watch:dentry' >> events/fprobes/dentry_kill/trigger +cat events/fprobes/truncate/trigger | grep ^set_wprobe +cat events/fprobes/dentry_kill/trigger | grep ^clear_wprobe + +:;: "Ensure wprobe is still disabled" ;: +cat events/wprobes/watch/enable | grep 0 + +:;: "Enable events for triggers" ;: +echo 1 >> events/fprobes/truncate/enable +echo 1 >> events/fprobes/dentry_kill/enable + +:;: "Start test workload" ;: +echo 1 >> tracing_on + +echo aaa > /tmp/hoge +echo bbb > /tmp/hoge +echo ccc > /tmp/hoge +rm /tmp/hoge + +:;: "Check trace results" ;: +cat trace | grep watch + +:;: "Ensure wprobe becomes disabled again" ;: +cat events/wprobes/watch/enable | grep 0 + +:;: "Remove wprobe triggers" ;: +echo '!set_wprobe:watch:dentry' >> events/fprobes/truncate/trigger +echo '!clear_wprobe:watch' >> events/fprobes/dentry_kill/trigger +! grep ^set_wprobe events/fprobes/truncate/trigger +! grep ^clear_wprobe events/fprobes/dentry_kill/trigger + +exit 0