* Re: [PATCH v4 2/5] perf tools: Minimal DEFERRED_CALLCHAIN support
From: Ian Rogers @ 2025-11-19 16:48 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, James Clark, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users,
Steven Rostedt, Josh Poimboeuf, Indu Bhagat, Jens Remus,
Mathieu Desnoyers, linux-trace-kernel, bpf
In-Reply-To: <20251115234106.348571-3-namhyung@kernel.org>
On Sat, Nov 15, 2025 at 3:41 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Add a new event type for deferred callchains and a new callback for the
> struct perf_tool. For now it doesn't actually handle the deferred
> callchains but it just marks the sample if it has the PERF_CONTEXT_
> USER_DEFFERED in the callchain array.
>
> At least, perf report can dump the raw data with this change. Actually
> this requires the next commit to enable attr.defer_callchain, but if you
> already have a data file, it'll show the following result.
>
> $ perf report -D
> ...
> 0x2158@perf.data [0x40]: event: 22
> .
> . ... raw event: size 64 bytes
> . 0000: 16 00 00 00 02 00 40 00 06 00 00 00 0b 00 00 00 ......@.........
> . 0010: 03 00 00 00 00 00 00 00 a7 7f 33 fe 18 7f 00 00 ..........3.....
> . 0020: 0f 0e 33 fe 18 7f 00 00 48 14 33 fe 18 7f 00 00 ..3.....H.3.....
> . 0030: 08 09 00 00 08 09 00 00 e6 7a e7 35 1c 00 00 00 .........z.5....
>
> 121163447014 0x2158 [0x40]: PERF_RECORD_CALLCHAIN_DEFERRED(IP, 0x2): 2312/2312: 0xb00000006
> ... FP chain: nr:3
> ..... 0: 00007f18fe337fa7
> ..... 1: 00007f18fe330e0f
> ..... 2: 00007f18fe331448
> : unhandled!
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> ---
> tools/lib/perf/include/perf/event.h | 13 ++++++++++
> tools/perf/util/event.c | 1 +
> tools/perf/util/evsel.c | 31 +++++++++++++++++++++--
> tools/perf/util/machine.c | 1 +
> tools/perf/util/perf_event_attr_fprintf.c | 2 ++
> tools/perf/util/sample.h | 2 ++
> tools/perf/util/session.c | 20 +++++++++++++++
> tools/perf/util/tool.c | 1 +
> tools/perf/util/tool.h | 3 ++-
> 9 files changed, 71 insertions(+), 3 deletions(-)
>
> diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h
> index aa1e91c97a226e1a..43a8cb04994fa033 100644
> --- a/tools/lib/perf/include/perf/event.h
> +++ b/tools/lib/perf/include/perf/event.h
> @@ -151,6 +151,18 @@ struct perf_record_switch {
> __u32 next_prev_tid;
> };
>
> +struct perf_record_callchain_deferred {
> + struct perf_event_header header;
> + /*
> + * This is to match kernel and (deferred) user stacks together.
> + * The kernel part will be in the sample callchain array after
> + * the PERF_CONTEXT_USER_DEFERRED entry.
> + */
> + __u64 cookie;
> + __u64 nr;
> + __u64 ips[];
> +};
> +
> struct perf_record_header_attr {
> struct perf_event_header header;
> struct perf_event_attr attr;
> @@ -523,6 +535,7 @@ union perf_event {
> struct perf_record_read read;
> struct perf_record_throttle throttle;
> struct perf_record_sample sample;
> + struct perf_record_callchain_deferred callchain_deferred;
> struct perf_record_bpf_event bpf;
> struct perf_record_ksymbol ksymbol;
> struct perf_record_text_poke_event text_poke;
> diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
> index fcf44149feb20c35..4c92cc1a952c1d9f 100644
> --- a/tools/perf/util/event.c
> +++ b/tools/perf/util/event.c
> @@ -61,6 +61,7 @@ static const char *perf_event__names[] = {
> [PERF_RECORD_CGROUP] = "CGROUP",
> [PERF_RECORD_TEXT_POKE] = "TEXT_POKE",
> [PERF_RECORD_AUX_OUTPUT_HW_ID] = "AUX_OUTPUT_HW_ID",
> + [PERF_RECORD_CALLCHAIN_DEFERRED] = "CALLCHAIN_DEFERRED",
> [PERF_RECORD_HEADER_ATTR] = "ATTR",
> [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
> [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> index 989c56d4a23f74f4..5ee3e7dee93fbbcb 100644
> --- a/tools/perf/util/evsel.c
> +++ b/tools/perf/util/evsel.c
> @@ -3089,6 +3089,20 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
> data->data_src = PERF_MEM_DATA_SRC_NONE;
> data->vcpu = -1;
>
> + if (event->header.type == PERF_RECORD_CALLCHAIN_DEFERRED) {
> + const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
> +
> + data->callchain = (struct ip_callchain *)&event->callchain_deferred.nr;
> + if (data->callchain->nr > max_callchain_nr)
> + return -EFAULT;
> +
> + data->deferred_cookie = event->callchain_deferred.cookie;
> +
> + if (evsel->core.attr.sample_id_all)
> + perf_evsel__parse_id_sample(evsel, event, data);
> + return 0;
> + }
> +
> if (event->header.type != PERF_RECORD_SAMPLE) {
> if (!evsel->core.attr.sample_id_all)
> return 0;
> @@ -3213,12 +3227,25 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
>
> if (type & PERF_SAMPLE_CALLCHAIN) {
> const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
> + u64 callchain_nr;
>
> OVERFLOW_CHECK_u64(array);
> data->callchain = (struct ip_callchain *)array++;
> - if (data->callchain->nr > max_callchain_nr)
> + callchain_nr = data->callchain->nr;
> + if (callchain_nr > max_callchain_nr)
> return -EFAULT;
> - sz = data->callchain->nr * sizeof(u64);
> + sz = callchain_nr * sizeof(u64);
> + /*
> + * Save the cookie for the deferred user callchain. The last 2
> + * entries in the callchain should be the context marker and the
> + * cookie. The cookie will be used to match PERF_RECORD_
> + * CALLCHAIN_DEFERRED later.
> + */
> + if (evsel->core.attr.defer_callchain && callchain_nr >= 2 &&
> + data->callchain->ips[callchain_nr - 2] == PERF_CONTEXT_USER_DEFERRED) {
> + data->deferred_cookie = data->callchain->ips[callchain_nr - 1];
> + data->deferred_callchain = true;
> + }
> OVERFLOW_CHECK(array, sz, max_size);
> array = (void *)array + sz;
> }
> diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
> index b5dd42588c916d91..841b711d970e9457 100644
> --- a/tools/perf/util/machine.c
> +++ b/tools/perf/util/machine.c
> @@ -2124,6 +2124,7 @@ static int add_callchain_ip(struct thread *thread,
> *cpumode = PERF_RECORD_MISC_KERNEL;
> break;
> case PERF_CONTEXT_USER:
> + case PERF_CONTEXT_USER_DEFERRED:
> *cpumode = PERF_RECORD_MISC_USER;
> break;
> default:
> diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c
> index 66b666d9ce649dd7..741c3d657a8b6ae7 100644
> --- a/tools/perf/util/perf_event_attr_fprintf.c
> +++ b/tools/perf/util/perf_event_attr_fprintf.c
> @@ -343,6 +343,8 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
> PRINT_ATTRf(inherit_thread, p_unsigned);
> PRINT_ATTRf(remove_on_exec, p_unsigned);
> PRINT_ATTRf(sigtrap, p_unsigned);
> + PRINT_ATTRf(defer_callchain, p_unsigned);
> + PRINT_ATTRf(defer_output, p_unsigned);
>
> PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned, false);
> PRINT_ATTRf(bp_type, p_unsigned);
> diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h
> index fae834144ef42105..a8307b20a9ea8066 100644
> --- a/tools/perf/util/sample.h
> +++ b/tools/perf/util/sample.h
> @@ -107,6 +107,8 @@ struct perf_sample {
> /** @weight3: On x86 holds retire_lat, on powerpc holds p_stage_cyc. */
> u16 weight3;
> bool no_hw_idx; /* No hw_idx collected in branch_stack */
> + bool deferred_callchain; /* Has deferred user callchains */
> + u64 deferred_cookie;
> char insn[MAX_INSN];
> void *raw_data;
> struct ip_callchain *callchain;
> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> index 4b0236b2df2913e1..361e15c1f26a96d0 100644
> --- a/tools/perf/util/session.c
> +++ b/tools/perf/util/session.c
> @@ -720,6 +720,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
> [PERF_RECORD_CGROUP] = perf_event__cgroup_swap,
> [PERF_RECORD_TEXT_POKE] = perf_event__text_poke_swap,
> [PERF_RECORD_AUX_OUTPUT_HW_ID] = perf_event__all64_swap,
> + [PERF_RECORD_CALLCHAIN_DEFERRED] = perf_event__all64_swap,
> [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap,
> [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap,
> [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
> @@ -854,6 +855,9 @@ static void callchain__printf(struct evsel *evsel,
> for (i = 0; i < callchain->nr; i++)
> printf("..... %2d: %016" PRIx64 "\n",
> i, callchain->ips[i]);
> +
> + if (sample->deferred_callchain)
> + printf("...... (deferred)\n");
> }
>
> static void branch_stack__printf(struct perf_sample *sample,
> @@ -1123,6 +1127,19 @@ static void dump_sample(struct evsel *evsel, union perf_event *event,
> sample_read__printf(sample, evsel->core.attr.read_format);
> }
>
> +static void dump_deferred_callchain(struct evsel *evsel, union perf_event *event,
> + struct perf_sample *sample)
> +{
> + if (!dump_trace)
> + return;
> +
> + printf("(IP, 0x%x): %d/%d: %#" PRIx64 "\n",
> + event->header.misc, sample->pid, sample->tid, sample->deferred_cookie);
> +
> + if (evsel__has_callchain(evsel))
> + callchain__printf(evsel, sample);
> +}
> +
> static void dump_read(struct evsel *evsel, union perf_event *event)
> {
> struct perf_record_read *read_event = &event->read;
> @@ -1353,6 +1370,9 @@ static int machines__deliver_event(struct machines *machines,
> return tool->text_poke(tool, event, sample, machine);
> case PERF_RECORD_AUX_OUTPUT_HW_ID:
> return tool->aux_output_hw_id(tool, event, sample, machine);
> + case PERF_RECORD_CALLCHAIN_DEFERRED:
> + dump_deferred_callchain(evsel, event, sample);
> + return tool->callchain_deferred(tool, event, sample, evsel, machine);
> default:
> ++evlist->stats.nr_unknown_events;
> return -1;
> diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c
> index 22a8a4ffe05f778e..f732d33e7f895ed4 100644
> --- a/tools/perf/util/tool.c
> +++ b/tools/perf/util/tool.c
> @@ -287,6 +287,7 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events)
> tool->read = process_event_sample_stub;
> tool->throttle = process_event_stub;
> tool->unthrottle = process_event_stub;
> + tool->callchain_deferred = process_event_sample_stub;
nit: there's a similar change needed to delegate_tool__init, that code
is currently unused.
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> tool->attr = process_event_synth_attr_stub;
> tool->event_update = process_event_synth_event_update_stub;
> tool->tracing_data = process_event_synth_tracing_data_stub;
> diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
> index 88337cee1e3e2be3..9b9f0a8cbf3de4b5 100644
> --- a/tools/perf/util/tool.h
> +++ b/tools/perf/util/tool.h
> @@ -44,7 +44,8 @@ enum show_feature_header {
>
> struct perf_tool {
> event_sample sample,
> - read;
> + read,
> + callchain_deferred;
> event_op mmap,
> mmap2,
> comm,
> --
> 2.52.0.rc1.455.g30608eb744-goog
>
^ permalink raw reply
* Re: [PATCH v4 1/5] tools headers UAPI: Sync linux/perf_event.h for deferred callchains
From: Ian Rogers @ 2025-11-19 16:49 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, James Clark, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users,
Steven Rostedt, Josh Poimboeuf, Indu Bhagat, Jens Remus,
Mathieu Desnoyers, linux-trace-kernel, bpf
In-Reply-To: <20251115234106.348571-2-namhyung@kernel.org>
On Sat, Nov 15, 2025 at 3:41 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> It needs to sync with the kernel to support user space changes for the
> deferred callchains.
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/include/uapi/linux/perf_event.h | 21 ++++++++++++++++++++-
> 1 file changed, 20 insertions(+), 1 deletion(-)
>
> diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h
> index 78a362b8002776e5..d292f96bc06f86bc 100644
> --- a/tools/include/uapi/linux/perf_event.h
> +++ b/tools/include/uapi/linux/perf_event.h
> @@ -463,7 +463,9 @@ struct perf_event_attr {
> inherit_thread : 1, /* children only inherit if cloned with CLONE_THREAD */
> remove_on_exec : 1, /* event is removed from task on exec */
> sigtrap : 1, /* send synchronous SIGTRAP on event */
> - __reserved_1 : 26;
> + defer_callchain: 1, /* request PERF_RECORD_CALLCHAIN_DEFERRED records */
> + defer_output : 1, /* output PERF_RECORD_CALLCHAIN_DEFERRED records */
> + __reserved_1 : 24;
>
> union {
> __u32 wakeup_events; /* wake up every n events */
> @@ -1239,6 +1241,22 @@ enum perf_event_type {
> */
> PERF_RECORD_AUX_OUTPUT_HW_ID = 21,
>
> + /*
> + * This user callchain capture was deferred until shortly before
> + * returning to user space. Previous samples would have kernel
> + * callchains only and they need to be stitched with this to make full
> + * callchains.
> + *
> + * struct {
> + * struct perf_event_header header;
> + * u64 cookie;
> + * u64 nr;
> + * u64 ips[nr];
> + * struct sample_id sample_id;
> + * };
> + */
> + PERF_RECORD_CALLCHAIN_DEFERRED = 22,
> +
> PERF_RECORD_MAX, /* non-ABI */
> };
>
> @@ -1269,6 +1287,7 @@ enum perf_callchain_context {
> PERF_CONTEXT_HV = (__u64)-32,
> PERF_CONTEXT_KERNEL = (__u64)-128,
> PERF_CONTEXT_USER = (__u64)-512,
> + PERF_CONTEXT_USER_DEFERRED = (__u64)-640,
>
> PERF_CONTEXT_GUEST = (__u64)-2048,
> PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176,
> --
> 2.52.0.rc1.455.g30608eb744-goog
>
^ permalink raw reply
* Re: [PATCH v4 3/5] perf record: Add --call-graph fp,defer option for deferred callchains
From: Ian Rogers @ 2025-11-19 16:50 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, James Clark, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users,
Steven Rostedt, Josh Poimboeuf, Indu Bhagat, Jens Remus,
Mathieu Desnoyers, linux-trace-kernel, bpf
In-Reply-To: <20251115234106.348571-4-namhyung@kernel.org>
On Sat, Nov 15, 2025 at 3:41 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Add a new callchain record mode option for deferred callchains. For now
> it only works with FP (frame-pointer) mode.
>
> And add the missing feature detection logic to clear the flag on old
> kernels.
>
> $ perf record --call-graph fp,defer -vv true
> ...
> ------------------------------------------------------------
> perf_event_attr:
> type 0 (PERF_TYPE_HARDWARE)
> size 136
> config 0 (PERF_COUNT_HW_CPU_CYCLES)
> { sample_period, sample_freq } 4000
> sample_type IP|TID|TIME|CALLCHAIN|PERIOD
> read_format ID|LOST
> disabled 1
> inherit 1
> mmap 1
> comm 1
> freq 1
> enable_on_exec 1
> task 1
> sample_id_all 1
> mmap2 1
> comm_exec 1
> ksymbol 1
> bpf_event 1
> defer_callchain 1
> defer_output 1
> ------------------------------------------------------------
> sys_perf_event_open: pid 162755 cpu 0 group_fd -1 flags 0x8
> sys_perf_event_open failed, error -22
> switching off deferred callchain support
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/Documentation/perf-config.txt | 3 +++
> tools/perf/Documentation/perf-record.txt | 4 ++++
> tools/perf/util/callchain.c | 16 +++++++++++++---
> tools/perf/util/callchain.h | 1 +
> tools/perf/util/evsel.c | 19 +++++++++++++++++++
> tools/perf/util/evsel.h | 1 +
> 6 files changed, 41 insertions(+), 3 deletions(-)
>
> diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
> index c6f33565966735fe..642d1c490d9e3bcd 100644
> --- a/tools/perf/Documentation/perf-config.txt
> +++ b/tools/perf/Documentation/perf-config.txt
> @@ -452,6 +452,9 @@ Variables
> kernel space is controlled not by this option but by the
> kernel config (CONFIG_UNWINDER_*).
>
> + The 'defer' mode can be used with 'fp' mode to enable deferred
> + user callchains (like 'fp,defer').
> +
> call-graph.dump-size::
> The size of stack to dump in order to do post-unwinding. Default is 8192 (byte).
> When using dwarf into record-mode, the default size will be used if omitted.
> diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
> index 067891bd7da6edc8..e8b9aadbbfa50574 100644
> --- a/tools/perf/Documentation/perf-record.txt
> +++ b/tools/perf/Documentation/perf-record.txt
> @@ -325,6 +325,10 @@ OPTIONS
> by default. User can change the number by passing it after comma
> like "--call-graph fp,32".
>
> + Also "defer" can be used with "fp" (like "--call-graph fp,defer") to
> + enable deferred user callchain which will collect user-space callchains
> + when the thread returns to the user space.
> +
> -q::
> --quiet::
> Don't print any warnings or messages, useful for scripting.
> diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
> index d7b7eef740b9d6ed..2884187ccbbecfdc 100644
> --- a/tools/perf/util/callchain.c
> +++ b/tools/perf/util/callchain.c
> @@ -275,9 +275,13 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)
> if (tok) {
> unsigned long size;
>
> - size = strtoul(tok, &name, 0);
> - if (size < (unsigned) sysctl__max_stack())
> - param->max_stack = size;
> + if (!strncmp(tok, "defer", sizeof("defer"))) {
> + param->defer = true;
> + } else {
> + size = strtoul(tok, &name, 0);
> + if (size < (unsigned) sysctl__max_stack())
> + param->max_stack = size;
> + }
> }
> break;
>
> @@ -314,6 +318,12 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)
> } while (0);
>
> free(buf);
> +
> + if (param->defer && param->record_mode != CALLCHAIN_FP) {
> + pr_err("callchain: deferred callchain only works with FP\n");
> + return -EINVAL;
> + }
> +
> return ret;
> }
>
> diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
> index 86ed9e4d04f9ee7b..d5ae4fbb7ce5fa44 100644
> --- a/tools/perf/util/callchain.h
> +++ b/tools/perf/util/callchain.h
> @@ -98,6 +98,7 @@ extern bool dwarf_callchain_users;
>
> struct callchain_param {
> bool enabled;
> + bool defer;
> enum perf_call_graph_mode record_mode;
> u32 dump_size;
> enum chain_mode mode;
> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> index 5ee3e7dee93fbbcb..7772ee9cfe3ac1c7 100644
> --- a/tools/perf/util/evsel.c
> +++ b/tools/perf/util/evsel.c
> @@ -1065,6 +1065,9 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o
> pr_info("Disabling user space callchains for function trace event.\n");
> attr->exclude_callchain_user = 1;
> }
> +
> + if (param->defer && !attr->exclude_callchain_user)
> + attr->defer_callchain = 1;
> }
>
> void evsel__config_callchain(struct evsel *evsel, struct record_opts *opts,
> @@ -1511,6 +1514,7 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
> attr->mmap2 = track && !perf_missing_features.mmap2;
> attr->comm = track;
> attr->build_id = track && opts->build_id;
> + attr->defer_output = track && callchain->defer;
>
> /*
> * ksymbol is tracked separately with text poke because it needs to be
> @@ -2199,6 +2203,10 @@ static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,
>
> static void evsel__disable_missing_features(struct evsel *evsel)
> {
> + if (perf_missing_features.defer_callchain && evsel->core.attr.defer_callchain)
> + evsel->core.attr.defer_callchain = 0;
> + if (perf_missing_features.defer_callchain && evsel->core.attr.defer_output)
> + evsel->core.attr.defer_output = 0;
> if (perf_missing_features.inherit_sample_read && evsel->core.attr.inherit &&
> (evsel->core.attr.sample_type & PERF_SAMPLE_READ))
> evsel->core.attr.inherit = 0;
> @@ -2473,6 +2481,13 @@ static bool evsel__detect_missing_features(struct evsel *evsel, struct perf_cpu
>
> /* Please add new feature detection here. */
>
> + attr.defer_callchain = true;
> + if (has_attr_feature(&attr, /*flags=*/0))
> + goto found;
> + perf_missing_features.defer_callchain = true;
> + pr_debug2("switching off deferred callchain support\n");
> + attr.defer_callchain = false;
> +
> attr.inherit = true;
> attr.sample_type = PERF_SAMPLE_READ | PERF_SAMPLE_TID;
> if (has_attr_feature(&attr, /*flags=*/0))
> @@ -2584,6 +2599,10 @@ static bool evsel__detect_missing_features(struct evsel *evsel, struct perf_cpu
> errno = old_errno;
>
> check:
> + if ((evsel->core.attr.defer_callchain || evsel->core.attr.defer_output) &&
> + perf_missing_features.defer_callchain)
> + return true;
> +
> if (evsel->core.attr.inherit &&
> (evsel->core.attr.sample_type & PERF_SAMPLE_READ) &&
> perf_missing_features.inherit_sample_read)
> diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
> index 3ae4ac8f9a37e009..a08130ff2e47a887 100644
> --- a/tools/perf/util/evsel.h
> +++ b/tools/perf/util/evsel.h
> @@ -221,6 +221,7 @@ struct perf_missing_features {
> bool branch_counters;
> bool aux_action;
> bool inherit_sample_read;
> + bool defer_callchain;
> };
>
> extern struct perf_missing_features perf_missing_features;
> --
> 2.52.0.rc1.455.g30608eb744-goog
>
^ permalink raw reply
* Re: [PATCH v4 4/5] perf script: Display PERF_RECORD_CALLCHAIN_DEFERRED
From: Ian Rogers @ 2025-11-19 16:50 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, James Clark, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users,
Steven Rostedt, Josh Poimboeuf, Indu Bhagat, Jens Remus,
Mathieu Desnoyers, linux-trace-kernel, bpf
In-Reply-To: <20251115234106.348571-5-namhyung@kernel.org>
On Sat, Nov 15, 2025 at 3:41 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Handle the deferred callchains in the script output.
>
> $ perf script
> ...
> pwd 2312 121.163435: 249113 cpu/cycles/P:
> ffffffff845b78d8 __build_id_parse.isra.0+0x218 ([kernel.kallsyms])
> ffffffff83bb5bf6 perf_event_mmap+0x2e6 ([kernel.kallsyms])
> ffffffff83c31959 mprotect_fixup+0x1e9 ([kernel.kallsyms])
> ffffffff83c31dc5 do_mprotect_pkey+0x2b5 ([kernel.kallsyms])
> ffffffff83c3206f __x64_sys_mprotect+0x1f ([kernel.kallsyms])
> ffffffff845e6692 do_syscall_64+0x62 ([kernel.kallsyms])
> ffffffff8360012f entry_SYSCALL_64_after_hwframe+0x76 ([kernel.kallsyms])
> b00000006 (cookie) ([unknown])
>
> pwd 2312 121.163447: DEFERRED CALLCHAIN [cookie: b00000006]
> 7f18fe337fa7 mprotect+0x7 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
> 7f18fe330e0f _dl_sysdep_start+0x7f (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
> 7f18fe331448 _dl_start_user+0x0 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Thanks,
Ian
> ---
> tools/perf/builtin-script.c | 89 +++++++++++++++++++++++++++++++++
> tools/perf/util/evsel_fprintf.c | 5 +-
> 2 files changed, 93 insertions(+), 1 deletion(-)
>
> diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
> index 011962e1ee0f6898..85b42205a71b3993 100644
> --- a/tools/perf/builtin-script.c
> +++ b/tools/perf/builtin-script.c
> @@ -2706,6 +2706,94 @@ static int process_sample_event(const struct perf_tool *tool,
> return ret;
> }
>
> +static int process_deferred_sample_event(const struct perf_tool *tool,
> + union perf_event *event,
> + struct perf_sample *sample,
> + struct evsel *evsel,
> + struct machine *machine)
> +{
> + struct perf_script *scr = container_of(tool, struct perf_script, tool);
> + struct perf_event_attr *attr = &evsel->core.attr;
> + struct evsel_script *es = evsel->priv;
> + unsigned int type = output_type(attr->type);
> + struct addr_location al;
> + FILE *fp = es->fp;
> + int ret = 0;
> +
> + if (output[type].fields == 0)
> + return 0;
> +
> + /* Set thread to NULL to indicate addr_al and al are not initialized */
> + addr_location__init(&al);
> +
> + if (perf_time__ranges_skip_sample(scr->ptime_range, scr->range_num,
> + sample->time)) {
> + goto out_put;
> + }
> +
> + if (debug_mode) {
> + if (sample->time < last_timestamp) {
> + pr_err("Samples misordered, previous: %" PRIu64
> + " this: %" PRIu64 "\n", last_timestamp,
> + sample->time);
> + nr_unordered++;
> + }
> + last_timestamp = sample->time;
> + goto out_put;
> + }
> +
> + if (filter_cpu(sample))
> + goto out_put;
> +
> + if (machine__resolve(machine, &al, sample) < 0) {
> + pr_err("problem processing %d event, skipping it.\n",
> + event->header.type);
> + ret = -1;
> + goto out_put;
> + }
> +
> + if (al.filtered)
> + goto out_put;
> +
> + if (!show_event(sample, evsel, al.thread, &al, NULL))
> + goto out_put;
> +
> + if (evswitch__discard(&scr->evswitch, evsel))
> + goto out_put;
> +
> + perf_sample__fprintf_start(scr, sample, al.thread, evsel,
> + PERF_RECORD_CALLCHAIN_DEFERRED, fp);
> + fprintf(fp, "DEFERRED CALLCHAIN [cookie: %llx]",
> + (unsigned long long)event->callchain_deferred.cookie);
> +
> + if (PRINT_FIELD(IP)) {
> + struct callchain_cursor *cursor = NULL;
> +
> + if (symbol_conf.use_callchain && sample->callchain) {
> + cursor = get_tls_callchain_cursor();
> + if (thread__resolve_callchain(al.thread, cursor, evsel,
> + sample, NULL, NULL,
> + scripting_max_stack)) {
> + pr_info("cannot resolve deferred callchains\n");
> + cursor = NULL;
> + }
> + }
> +
> + fputc(cursor ? '\n' : ' ', fp);
> + sample__fprintf_sym(sample, &al, 0, output[type].print_ip_opts,
> + cursor, symbol_conf.bt_stop_list, fp);
> + }
> +
> + fprintf(fp, "\n");
> +
> + if (verbose > 0)
> + fflush(fp);
> +
> +out_put:
> + addr_location__exit(&al);
> + return ret;
> +}
> +
> // Used when scr->per_event_dump is not set
> static struct evsel_script es_stdout;
>
> @@ -4303,6 +4391,7 @@ int cmd_script(int argc, const char **argv)
>
> perf_tool__init(&script.tool, !unsorted_dump);
> script.tool.sample = process_sample_event;
> + script.tool.callchain_deferred = process_deferred_sample_event;
> script.tool.mmap = perf_event__process_mmap;
> script.tool.mmap2 = perf_event__process_mmap2;
> script.tool.comm = perf_event__process_comm;
> diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c
> index 103984b29b1e10ae..10f1a03c28601e36 100644
> --- a/tools/perf/util/evsel_fprintf.c
> +++ b/tools/perf/util/evsel_fprintf.c
> @@ -168,7 +168,10 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
> node_al.addr = addr;
> node_al.map = map__get(map);
>
> - if (print_symoffset) {
> + if (sample->deferred_callchain &&
> + sample->deferred_cookie == node->ip) {
> + printed += fprintf(fp, "(cookie)");
> + } else if (print_symoffset) {
> printed += __symbol__fprintf_symname_offs(sym, &node_al,
> print_unknown_as_addr,
> true, fp);
> --
> 2.52.0.rc1.455.g30608eb744-goog
>
^ permalink raw reply
* Re: [PATCH v4 5/5] perf tools: Merge deferred user callchains
From: Ian Rogers @ 2025-11-19 16:54 UTC (permalink / raw)
To: Namhyung Kim
Cc: Arnaldo Carvalho de Melo, James Clark, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users,
Steven Rostedt, Josh Poimboeuf, Indu Bhagat, Jens Remus,
Mathieu Desnoyers, linux-trace-kernel, bpf
In-Reply-To: <20251115234106.348571-6-namhyung@kernel.org>
On Sat, Nov 15, 2025 at 3:41 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Save samples with deferred callchains in a separate list and deliver
> them after merging the user callchains. If users don't want to merge
> they can set tool->merge_deferred_callchains to false to prevent the
> behavior.
>
> With previous result, now perf script will show the merged callchains.
>
> $ perf script
> ...
> pwd 2312 121.163435: 249113 cpu/cycles/P:
> ffffffff845b78d8 __build_id_parse.isra.0+0x218 ([kernel.kallsyms])
> ffffffff83bb5bf6 perf_event_mmap+0x2e6 ([kernel.kallsyms])
> ffffffff83c31959 mprotect_fixup+0x1e9 ([kernel.kallsyms])
> ffffffff83c31dc5 do_mprotect_pkey+0x2b5 ([kernel.kallsyms])
> ffffffff83c3206f __x64_sys_mprotect+0x1f ([kernel.kallsyms])
> ffffffff845e6692 do_syscall_64+0x62 ([kernel.kallsyms])
> ffffffff8360012f entry_SYSCALL_64_after_hwframe+0x76 ([kernel.kallsyms])
> 7f18fe337fa7 mprotect+0x7 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
> 7f18fe330e0f _dl_sysdep_start+0x7f (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
> 7f18fe331448 _dl_start_user+0x0 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
> ...
>
> The old output can be get using --no-merge-callchain option.
> Also perf report can get the user callchain entry at the end.
>
> $ perf report --no-children --stdio -q -S __build_id_parse.isra.0
> # symbol: __build_id_parse.isra.0
> 8.40% pwd [kernel.kallsyms]
> |
> ---__build_id_parse.isra.0
> perf_event_mmap
> mprotect_fixup
> do_mprotect_pkey
> __x64_sys_mprotect
> do_syscall_64
> entry_SYSCALL_64_after_hwframe
> mprotect
> _dl_sysdep_start
> _dl_start_user
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> ---
> tools/perf/Documentation/perf-script.txt | 5 ++
> tools/perf/builtin-inject.c | 1 +
> tools/perf/builtin-report.c | 1 +
> tools/perf/builtin-script.c | 4 ++
> tools/perf/util/callchain.c | 29 ++++++++++
> tools/perf/util/callchain.h | 3 ++
> tools/perf/util/evlist.c | 1 +
> tools/perf/util/evlist.h | 2 +
> tools/perf/util/session.c | 67 +++++++++++++++++++++++-
> tools/perf/util/tool.c | 1 +
> tools/perf/util/tool.h | 1 +
> 11 files changed, 114 insertions(+), 1 deletion(-)
>
> diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
> index 28bec7e78bc858ba..03d1129606328d6d 100644
> --- a/tools/perf/Documentation/perf-script.txt
> +++ b/tools/perf/Documentation/perf-script.txt
> @@ -527,6 +527,11 @@ include::itrace.txt[]
> The known limitations include exception handing such as
> setjmp/longjmp will have calls/returns not match.
>
> +--merge-callchains::
> + Enable merging deferred user callchains if available. This is the
> + default behavior. If you want to see separate CALLCHAIN_DEFERRED
> + records for some reason, use --no-merge-callchains explicitly.
> +
> :GMEXAMPLECMD: script
> :GMEXAMPLESUBCMD:
> include::guest-files.txt[]
> diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
> index bd9245d2dd41aa48..51d2721b6db9dccb 100644
> --- a/tools/perf/builtin-inject.c
> +++ b/tools/perf/builtin-inject.c
> @@ -2527,6 +2527,7 @@ int cmd_inject(int argc, const char **argv)
> inject.tool.auxtrace = perf_event__repipe_auxtrace;
> inject.tool.bpf_metadata = perf_event__repipe_op2_synth;
> inject.tool.dont_split_sample_group = true;
> + inject.tool.merge_deferred_callchains = false;
> inject.session = __perf_session__new(&data, &inject.tool,
> /*trace_event_repipe=*/inject.output.is_pipe,
> /*host_env=*/NULL);
> diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
> index 2bc269f5fcef8023..add6b1c2aaf04270 100644
> --- a/tools/perf/builtin-report.c
> +++ b/tools/perf/builtin-report.c
> @@ -1614,6 +1614,7 @@ int cmd_report(int argc, const char **argv)
> report.tool.event_update = perf_event__process_event_update;
> report.tool.feature = process_feature_event;
> report.tool.ordering_requires_timestamps = true;
> + report.tool.merge_deferred_callchains = !dump_trace;
>
> session = perf_session__new(&data, &report.tool);
> if (IS_ERR(session)) {
> diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
> index 85b42205a71b3993..62e43d3c5ad731a0 100644
> --- a/tools/perf/builtin-script.c
> +++ b/tools/perf/builtin-script.c
> @@ -4009,6 +4009,7 @@ int cmd_script(int argc, const char **argv)
> bool header_only = false;
> bool script_started = false;
> bool unsorted_dump = false;
> + bool merge_deferred_callchains = true;
> char *rec_script_path = NULL;
> char *rep_script_path = NULL;
> struct perf_session *session;
> @@ -4162,6 +4163,8 @@ int cmd_script(int argc, const char **argv)
> "Guest code can be found in hypervisor process"),
> OPT_BOOLEAN('\0', "stitch-lbr", &script.stitch_lbr,
> "Enable LBR callgraph stitching approach"),
> + OPT_BOOLEAN('\0', "merge-callchains", &merge_deferred_callchains,
> + "Enable merge deferred user callchains"),
> OPTS_EVSWITCH(&script.evswitch),
> OPT_END()
> };
> @@ -4418,6 +4421,7 @@ int cmd_script(int argc, const char **argv)
> script.tool.throttle = process_throttle_event;
> script.tool.unthrottle = process_throttle_event;
> script.tool.ordering_requires_timestamps = true;
> + script.tool.merge_deferred_callchains = merge_deferred_callchains;
> session = perf_session__new(&data, &script.tool);
> if (IS_ERR(session))
> return PTR_ERR(session);
> diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
> index 2884187ccbbecfdc..71dc5a070065dd2a 100644
> --- a/tools/perf/util/callchain.c
> +++ b/tools/perf/util/callchain.c
> @@ -1838,3 +1838,32 @@ int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel,
> }
> return 0;
> }
> +
> +int sample__merge_deferred_callchain(struct perf_sample *sample_orig,
> + struct perf_sample *sample_callchain)
> +{
> + u64 nr_orig = sample_orig->callchain->nr - 1;
> + u64 nr_deferred = sample_callchain->callchain->nr;
> + struct ip_callchain *callchain;
> +
> + if (sample_orig->callchain->nr < 2) {
> + sample_orig->deferred_callchain = false;
> + return -EINVAL;
> + }
> +
> + callchain = calloc(1 + nr_orig + nr_deferred, sizeof(u64));
> + if (callchain == NULL) {
> + sample_orig->deferred_callchain = false;
> + return -ENOMEM;
> + }
> +
> + callchain->nr = nr_orig + nr_deferred;
> + /* copy original including PERF_CONTEXT_USER_DEFERRED (but the cookie) */
> + memcpy(callchain->ips, sample_orig->callchain->ips, nr_orig * sizeof(u64));
> + /* copy deferred user callchains */
> + memcpy(&callchain->ips[nr_orig], sample_callchain->callchain->ips,
> + nr_deferred * sizeof(u64));
> +
> + sample_orig->callchain = callchain;
> + return 0;
> +}
> diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
> index d5ae4fbb7ce5fa44..2a52af8c80ace33c 100644
> --- a/tools/perf/util/callchain.h
> +++ b/tools/perf/util/callchain.h
> @@ -318,4 +318,7 @@ int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel,
> struct perf_sample *sample, int max_stack,
> bool symbols, callchain_iter_fn cb, void *data);
>
> +int sample__merge_deferred_callchain(struct perf_sample *sample_orig,
> + struct perf_sample *sample_callchain);
> +
> #endif /* __PERF_CALLCHAIN_H */
> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
> index e8217efdda5323c6..03674d2cbd015e4f 100644
> --- a/tools/perf/util/evlist.c
> +++ b/tools/perf/util/evlist.c
> @@ -85,6 +85,7 @@ void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus,
> evlist->ctl_fd.pos = -1;
> evlist->nr_br_cntr = -1;
> metricgroup__rblist_init(&evlist->metric_events);
> + INIT_LIST_HEAD(&evlist->deferred_samples);
> }
>
> struct evlist *evlist__new(void)
> diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
> index 5e71e3dc60423079..911834ae7c2a6f76 100644
> --- a/tools/perf/util/evlist.h
> +++ b/tools/perf/util/evlist.h
> @@ -92,6 +92,8 @@ struct evlist {
> * of struct metric_expr.
> */
> struct rblist metric_events;
> + /* samples with deferred_callchain would wait here. */
> + struct list_head deferred_samples;
> };
>
> struct evsel_str_handler {
> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> index 361e15c1f26a96d0..2e777fd1bcf6707b 100644
> --- a/tools/perf/util/session.c
> +++ b/tools/perf/util/session.c
> @@ -1285,6 +1285,60 @@ static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool
> per_thread);
> }
>
> +struct deferred_event {
> + struct list_head list;
> + union perf_event *event;
What's going on with the memory here? Doesn't the event need to be a
copy to avoid the memory going away in between processing events? It
is definitely worth commenting. Note, I don't see a copy being made
for the event here in machines__deliver_event.
Thanks,
Ian
> +};
> +
> +static int evlist__deliver_deferred_samples(struct evlist *evlist,
> + const struct perf_tool *tool,
> + union perf_event *event,
> + struct perf_sample *sample,
> + struct machine *machine)
> +{
> + struct deferred_event *de, *tmp;
> + struct evsel *evsel;
> + int ret = 0;
> +
> + if (!tool->merge_deferred_callchains) {
> + evsel = evlist__id2evsel(evlist, sample->id);
> + return tool->callchain_deferred(tool, event, sample,
> + evsel, machine);
> + }
> +
> + list_for_each_entry_safe(de, tmp, &evlist->deferred_samples, list) {
> + struct perf_sample orig_sample;
> +
> + ret = evlist__parse_sample(evlist, de->event, &orig_sample);
> + if (ret < 0) {
> + pr_err("failed to parse original sample\n");
> + break;
> + }
> +
> + if (sample->tid != orig_sample.tid)
> + continue;
> +
> + if (event->callchain_deferred.cookie == orig_sample.deferred_cookie)
> + sample__merge_deferred_callchain(&orig_sample, sample);
> + else
> + orig_sample.deferred_callchain = false;
> +
> + evsel = evlist__id2evsel(evlist, orig_sample.id);
> + ret = evlist__deliver_sample(evlist, tool, de->event,
> + &orig_sample, evsel, machine);
> +
> + if (orig_sample.deferred_callchain)
> + free(orig_sample.callchain);
> +
> + list_del(&de->list);
> + free(de);
> +
> + if (ret)
> + break;
> + }
> + return ret;
> +}
> +
> static int machines__deliver_event(struct machines *machines,
> struct evlist *evlist,
> union perf_event *event,
> @@ -1313,6 +1367,16 @@ static int machines__deliver_event(struct machines *machines,
> return 0;
> }
> dump_sample(evsel, event, sample, perf_env__arch(machine->env));
> + if (sample->deferred_callchain && tool->merge_deferred_callchains) {
> + struct deferred_event *de = malloc(sizeof(*de));
> +
> + if (de == NULL)
> + return -ENOMEM;
> +
> + de->event = event;
> + list_add_tail(&de->list, &evlist->deferred_samples);
> + return 0;
> + }
> return evlist__deliver_sample(evlist, tool, event, sample, evsel, machine);
> case PERF_RECORD_MMAP:
> return tool->mmap(tool, event, sample, machine);
> @@ -1372,7 +1436,8 @@ static int machines__deliver_event(struct machines *machines,
> return tool->aux_output_hw_id(tool, event, sample, machine);
> case PERF_RECORD_CALLCHAIN_DEFERRED:
> dump_deferred_callchain(evsel, event, sample);
> - return tool->callchain_deferred(tool, event, sample, evsel, machine);
> + return evlist__deliver_deferred_samples(evlist, tool, event,
> + sample, machine);
> default:
> ++evlist->stats.nr_unknown_events;
> return -1;
> diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c
> index f732d33e7f895ed4..c5d3b464b2a433b3 100644
> --- a/tools/perf/util/tool.c
> +++ b/tools/perf/util/tool.c
> @@ -266,6 +266,7 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events)
> tool->cgroup_events = false;
> tool->no_warn = false;
> tool->show_feat_hdr = SHOW_FEAT_NO_HEADER;
> + tool->merge_deferred_callchains = true;
>
> tool->sample = process_event_sample_stub;
> tool->mmap = process_event_stub;
> diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
> index 9b9f0a8cbf3de4b5..e96b69d25a5b737d 100644
> --- a/tools/perf/util/tool.h
> +++ b/tools/perf/util/tool.h
> @@ -90,6 +90,7 @@ struct perf_tool {
> bool cgroup_events;
> bool no_warn;
> bool dont_split_sample_group;
> + bool merge_deferred_callchains;
> enum show_feature_header show_feat_hdr;
> };
>
> --
> 2.52.0.rc1.455.g30608eb744-goog
>
^ permalink raw reply
* Re: [PATCH v8 21/28] KVM: arm64: Add tracing capability for the pKVM hyp
From: Marc Zyngier @ 2025-11-19 17:06 UTC (permalink / raw)
To: Vincent Donnefort
Cc: rostedt, mhiramat, mathieu.desnoyers, linux-trace-kernel,
oliver.upton, joey.gouly, suzuki.poulose, yuzenghui, kvmarm,
linux-arm-kernel, jstultz, qperret, will, aneesh.kumar,
kernel-team, linux-kernel
In-Reply-To: <20251107093840.3779150-22-vdonnefort@google.com>
On Fri, 07 Nov 2025 09:38:33 +0000,
Vincent Donnefort <vdonnefort@google.com> wrote:
>
> When running with protected mode, the host has very little knowledge
> about what is happening in the hypervisor. Of course this is an
> essential feature for security but nonetheless, that piece of code
> growing with more responsibilities, we need now a way to debug and
> profile it. Tracefs by its reliability, versatility and support for
> user-space is the perfect tool.
>
> There's no way the hypervisor could log events directly into the host
> tracefs ring-buffers. So instead let's use our own, where the hypervisor
> is the writer and the host the reader.
>
> Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
>
> diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
> index 9da54d4ee49e..ad02dee140d3 100644
> --- a/arch/arm64/include/asm/kvm_asm.h
> +++ b/arch/arm64/include/asm/kvm_asm.h
> @@ -89,6 +89,10 @@ enum __kvm_host_smccc_func {
> __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load,
> __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put,
> __KVM_HOST_SMCCC_FUNC___pkvm_tlb_flush_vmid,
> + __KVM_HOST_SMCCC_FUNC___pkvm_load_tracing,
> + __KVM_HOST_SMCCC_FUNC___pkvm_unload_tracing,
> + __KVM_HOST_SMCCC_FUNC___pkvm_enable_tracing,
> + __KVM_HOST_SMCCC_FUNC___pkvm_swap_reader_tracing,
> };
>
> #define DECLARE_KVM_VHE_SYM(sym) extern char sym[]
> diff --git a/arch/arm64/include/asm/kvm_hyptrace.h b/arch/arm64/include/asm/kvm_hyptrace.h
> new file mode 100644
> index 000000000000..9c30a479bc36
> --- /dev/null
> +++ b/arch/arm64/include/asm/kvm_hyptrace.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef __ARM64_KVM_HYPTRACE_H_
> +#define __ARM64_KVM_HYPTRACE_H_
> +
> +#include <linux/ring_buffer.h>
> +
> +struct hyp_trace_desc {
> + unsigned long bpages_backing_start;
Why is this an integer type? You keep casting it all over the place,
which tells me that's not the ideal type.
> + size_t bpages_backing_size;
> + struct trace_buffer_desc trace_buffer_desc;
> +
> +};
> +#endif
> diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
> index 4f803fd1c99a..580426cdbe77 100644
> --- a/arch/arm64/kvm/Kconfig
> +++ b/arch/arm64/kvm/Kconfig
> @@ -83,4 +83,11 @@ config PTDUMP_STAGE2_DEBUGFS
>
> If in doubt, say N.
>
> +config PKVM_TRACING
> + bool
> + depends on KVM
> + depends on TRACING
> + select SIMPLE_RING_BUFFER
> + default y
I'd rather this is made to depend on NVHE_EL2_DEBUG, just like the
other debug options.
> +
> endif # VIRTUALIZATION
> diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h
> new file mode 100644
> index 000000000000..996e90c0974f
> --- /dev/null
> +++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef __ARM64_KVM_HYP_NVHE_TRACE_H
> +#define __ARM64_KVM_HYP_NVHE_TRACE_H
> +#include <asm/kvm_hyptrace.h>
> +
> +#ifdef CONFIG_PKVM_TRACING
> +void *tracing_reserve_entry(unsigned long length);
> +void tracing_commit_entry(void);
> +
> +int __pkvm_load_tracing(unsigned long desc_va, size_t desc_size);
> +void __pkvm_unload_tracing(void);
> +int __pkvm_enable_tracing(bool enable);
> +int __pkvm_swap_reader_tracing(unsigned int cpu);
> +#else
> +static inline void *tracing_reserve_entry(unsigned long length) { return NULL; }
> +static inline void tracing_commit_entry(void) { }
> +
> +static inline int __pkvm_load_tracing(unsigned long desc_va, size_t desc_size) { return -ENODEV; }
> +static inline void __pkvm_unload_tracing(void) { }
> +static inline int __pkvm_enable_tracing(bool enable) { return -ENODEV; }
> +static inline int __pkvm_swap_reader_tracing(unsigned int cpu) { return -ENODEV; }
> +#endif
> +#endif
> diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile
> index f55a9a17d38f..504c3b9caef8 100644
> --- a/arch/arm64/kvm/hyp/nvhe/Makefile
> +++ b/arch/arm64/kvm/hyp/nvhe/Makefile
> @@ -29,7 +29,7 @@ hyp-obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \
> ../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o
> hyp-obj-y += ../../../kernel/smccc-call.o
> hyp-obj-$(CONFIG_LIST_HARDENED) += list_debug.o
> -hyp-obj-$(CONFIG_PKVM_TRACING) += clock.o
> +hyp-obj-$(CONFIG_PKVM_TRACING) += clock.o trace.o ../../../../../kernel/trace/simple_ring_buffer.o
Can we get something less awful here? Surely there is a way to get an
absolute path from the kbuild infrastructure? $(objtree) springs to
mind...
> hyp-obj-y += $(lib-objs)
>
> ##
> diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> index 29430c031095..6381e50ff531 100644
> --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> @@ -18,6 +18,7 @@
> #include <nvhe/mem_protect.h>
> #include <nvhe/mm.h>
> #include <nvhe/pkvm.h>
> +#include <nvhe/trace.h>
> #include <nvhe/trap_handler.h>
>
> DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
> @@ -585,6 +586,35 @@ static void handle___pkvm_teardown_vm(struct kvm_cpu_context *host_ctxt)
> cpu_reg(host_ctxt, 1) = __pkvm_teardown_vm(handle);
> }
>
> +static void handle___pkvm_load_tracing(struct kvm_cpu_context *host_ctxt)
> +{
> + DECLARE_REG(unsigned long, desc_hva, host_ctxt, 1);
> + DECLARE_REG(size_t, desc_size, host_ctxt, 2);
> +
> + cpu_reg(host_ctxt, 1) = __pkvm_load_tracing(desc_hva, desc_size);
> +}
> +
> +static void handle___pkvm_unload_tracing(struct kvm_cpu_context *host_ctxt)
> +{
> + __pkvm_unload_tracing();
> +
> + cpu_reg(host_ctxt, 1) = 0;
> +}
> +
> +static void handle___pkvm_enable_tracing(struct kvm_cpu_context *host_ctxt)
> +{
> + DECLARE_REG(bool, enable, host_ctxt, 1);
> +
> + cpu_reg(host_ctxt, 1) = __pkvm_enable_tracing(enable);
> +}
> +
> +static void handle___pkvm_swap_reader_tracing(struct kvm_cpu_context *host_ctxt)
> +{
> + DECLARE_REG(unsigned int, cpu, host_ctxt, 1);
> +
> + cpu_reg(host_ctxt, 1) = __pkvm_swap_reader_tracing(cpu);
> +}
> +
> typedef void (*hcall_t)(struct kvm_cpu_context *);
>
> #define HANDLE_FUNC(x) [__KVM_HOST_SMCCC_FUNC_##x] = (hcall_t)handle_##x
> @@ -626,6 +656,10 @@ static const hcall_t host_hcall[] = {
> HANDLE_FUNC(__pkvm_vcpu_load),
> HANDLE_FUNC(__pkvm_vcpu_put),
> HANDLE_FUNC(__pkvm_tlb_flush_vmid),
> + HANDLE_FUNC(__pkvm_load_tracing),
> + HANDLE_FUNC(__pkvm_unload_tracing),
> + HANDLE_FUNC(__pkvm_enable_tracing),
> + HANDLE_FUNC(__pkvm_swap_reader_tracing),
> };
>
> static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
> diff --git a/arch/arm64/kvm/hyp/nvhe/trace.c b/arch/arm64/kvm/hyp/nvhe/trace.c
> new file mode 100644
> index 000000000000..def5cbc75722
> --- /dev/null
> +++ b/arch/arm64/kvm/hyp/nvhe/trace.c
> @@ -0,0 +1,257 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2025 Google LLC
> + * Author: Vincent Donnefort <vdonnefort@google.com>
> + */
> +
> +#include <nvhe/clock.h>
> +#include <nvhe/mem_protect.h>
> +#include <nvhe/mm.h>
> +#include <nvhe/trace.h>
> +
> +#include <asm/percpu.h>
> +#include <asm/kvm_mmu.h>
> +#include <asm/local.h>
> +
> +#include <linux/simple_ring_buffer.h>
> +
> +static DEFINE_PER_CPU(struct simple_rb_per_cpu, __simple_rbs);
> +
> +static struct hyp_trace_buffer {
> + struct simple_rb_per_cpu __percpu *simple_rbs;
> + unsigned long bpages_backing_start;
> + size_t bpages_backing_size;
> + hyp_spinlock_t lock;
> +} trace_buffer = {
> + .simple_rbs = &__simple_rbs,
> + .lock = __HYP_SPIN_LOCK_UNLOCKED,
> +};
> +
> +static bool hyp_trace_buffer_loaded(struct hyp_trace_buffer *trace_buffer)
> +{
> + return trace_buffer->bpages_backing_size > 0;
> +}
> +
> +void *tracing_reserve_entry(unsigned long length)
> +{
> + return simple_ring_buffer_reserve(this_cpu_ptr(trace_buffer.simple_rbs), length,
> + trace_clock());
> +}
> +
> +void tracing_commit_entry(void)
> +{
> + simple_ring_buffer_commit(this_cpu_ptr(trace_buffer.simple_rbs));
> +}
> +
> +static int hyp_trace_buffer_load_bpage_backing(struct hyp_trace_buffer *trace_buffer,
> + struct hyp_trace_desc *desc)
> +{
> + unsigned long start = kern_hyp_va(desc->bpages_backing_start);
> + size_t size = desc->bpages_backing_size;
> + int ret;
> +
> + if (!PAGE_ALIGNED(start) || !PAGE_ALIGNED(size))
> + return -EINVAL;
> +
> + ret = __pkvm_host_donate_hyp(hyp_virt_to_pfn((void *)start), size >> PAGE_SHIFT);
> + if (ret)
> + return ret;
> +
> + memset((void *)start, 0, size);
> +
> + trace_buffer->bpages_backing_start = start;
> + trace_buffer->bpages_backing_size = size;
> +
> + return 0;
> +}
> +
> +static void hyp_trace_buffer_unload_bpage_backing(struct hyp_trace_buffer *trace_buffer)
> +{
> + unsigned long start = trace_buffer->bpages_backing_start;
> + size_t size = trace_buffer->bpages_backing_size;
> +
> + if (!size)
> + return;
> +
> + memset((void *)start, 0, size);
> +
> + WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(start), size >> PAGE_SHIFT));
> +
> + trace_buffer->bpages_backing_start = 0;
> + trace_buffer->bpages_backing_size = 0;
> +}
> +
> +static void *__pin_shared_page(unsigned long kern_va)
> +{
> + void *va = kern_hyp_va((void *)kern_va);
> +
> + return hyp_pin_shared_mem(va, va + PAGE_SIZE) ? NULL : va;
> +}
> +
> +static void __unpin_shared_page(void *va)
> +{
> + hyp_unpin_shared_mem(va, va + PAGE_SIZE);
> +}
> +
> +static void hyp_trace_buffer_unload(struct hyp_trace_buffer *trace_buffer)
> +{
> + int cpu;
> +
> + hyp_assert_lock_held(&trace_buffer->lock);
> +
> + if (!hyp_trace_buffer_loaded(trace_buffer))
> + return;
> +
> + for (cpu = 0; cpu < hyp_nr_cpus; cpu++)
> + __simple_ring_buffer_unload(per_cpu_ptr(trace_buffer->simple_rbs, cpu),
> + __unpin_shared_page);
> +
> + hyp_trace_buffer_unload_bpage_backing(trace_buffer);
> +}
> +
> +static int hyp_trace_buffer_load(struct hyp_trace_buffer *trace_buffer,
> + struct hyp_trace_desc *desc)
> +{
> + struct simple_buffer_page *bpages;
> + struct ring_buffer_desc *rb_desc;
> + int ret, cpu;
> +
> + hyp_assert_lock_held(&trace_buffer->lock);
> +
> + if (hyp_trace_buffer_loaded(trace_buffer))
> + return -EINVAL;
> +
> + ret = hyp_trace_buffer_load_bpage_backing(trace_buffer, desc);
> + if (ret)
> + return ret;
> +
> + bpages = (struct simple_buffer_page *)trace_buffer->bpages_backing_start;
> + for_each_ring_buffer_desc(rb_desc, cpu, &desc->trace_buffer_desc) {
> + ret = __simple_ring_buffer_init(per_cpu_ptr(trace_buffer->simple_rbs, cpu),
> + bpages, rb_desc, __pin_shared_page,
> + __unpin_shared_page);
> + if (ret)
> + break;
> +
> + bpages += rb_desc->nr_page_va;
> + }
> +
> + if (ret)
> + hyp_trace_buffer_unload(trace_buffer);
> +
> + return ret;
> +}
> +
> +static bool hyp_trace_desc_validate(struct hyp_trace_desc *desc, size_t desc_size)
> +{
> + struct simple_buffer_page *bpages = (struct simple_buffer_page *)desc->bpages_backing_start;
> + struct ring_buffer_desc *rb_desc;
> + void *bpages_end, *desc_end;
> + unsigned int cpu;
> +
> + desc_end = (void *)desc + desc_size; /* __pkvm_host_donate_hyp validates desc_size */
> +
> + bpages_end = (void *)desc->bpages_backing_start + desc->bpages_backing_size;
> + if (bpages_end < (void *)desc->bpages_backing_start)
> + return false;
> +
> + for_each_ring_buffer_desc(rb_desc, cpu, &desc->trace_buffer_desc) {
> + /* Can we read nr_page_va? */
> + if ((void *)rb_desc + struct_size(rb_desc, page_va, 0) > desc_end)
> + return false;
> +
> + /* Overflow desc? */
> + if ((void *)rb_desc + struct_size(rb_desc, page_va, rb_desc->nr_page_va) > desc_end)
> + return false;
> +
> + /* Overflow bpages backing memory? */
> + if ((void *)(bpages + rb_desc->nr_page_va) > bpages_end)
> + return false;
> +
> + if (cpu >= hyp_nr_cpus)
> + return false;
> +
> + if (cpu != rb_desc->cpu)
> + return false;
> +
> + bpages += rb_desc->nr_page_va;
> + }
> +
> + return true;
> +}
> +
> +int __pkvm_load_tracing(unsigned long desc_hva, size_t desc_size)
> +{
> + struct hyp_trace_desc *desc = (struct hyp_trace_desc *)kern_hyp_va(desc_hva);
> + int ret;
> +
> + if (!desc_size || !PAGE_ALIGNED(desc_hva) || !PAGE_ALIGNED(desc_size))
> + return -EINVAL;
> +
> + ret = __pkvm_host_donate_hyp(hyp_virt_to_pfn((void *)desc),
> + desc_size >> PAGE_SHIFT);
> + if (ret)
> + return ret;
> +
> + if (!hyp_trace_desc_validate(desc, desc_size))
> + goto err_donate_desc;
> +
> + hyp_spin_lock(&trace_buffer.lock);
> +
> + ret = hyp_trace_buffer_load(&trace_buffer, desc);
> +
> + hyp_spin_unlock(&trace_buffer.lock);
> +
> +err_donate_desc:
> + WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn((void *)desc),
> + desc_size >> PAGE_SHIFT));
That's basically a guaranteed panic if anything goes wrong. Are you
sure you want to do that?
> + return ret;
> +}
> +
> +void __pkvm_unload_tracing(void)
> +{
> + hyp_spin_lock(&trace_buffer.lock);
> + hyp_trace_buffer_unload(&trace_buffer);
> + hyp_spin_unlock(&trace_buffer.lock);
> +}
> +
> +int __pkvm_enable_tracing(bool enable)
> +{
> + int cpu, ret = enable ? -EINVAL : 0;
> +
> + hyp_spin_lock(&trace_buffer.lock);
> +
> + if (!hyp_trace_buffer_loaded(&trace_buffer))
> + goto unlock;
> +
> + for (cpu = 0; cpu < hyp_nr_cpus; cpu++)
> + simple_ring_buffer_enable_tracing(per_cpu_ptr(trace_buffer.simple_rbs, cpu),
> + enable);
> +
> + ret = 0;
> +
> +unlock:
> + hyp_spin_unlock(&trace_buffer.lock);
> +
> + return ret;
> +}
> +
> +int __pkvm_swap_reader_tracing(unsigned int cpu)
> +{
> + int ret;
> +
> + if (cpu >= hyp_nr_cpus)
> + return -EINVAL;
> +
> + hyp_spin_lock(&trace_buffer.lock);
> +
> + if (hyp_trace_buffer_loaded(&trace_buffer))
> + ret = simple_ring_buffer_swap_reader_page(
> + per_cpu_ptr(trace_buffer.simple_rbs, cpu));
Please keep these things on a single line. I don't care what people
(of checkpatch) say.
> + else
> + ret = -ENODEV;
> +
> + hyp_spin_unlock(&trace_buffer.lock);
> +
> + return ret;
> +}
> --
> 2.51.2.1041.gc1ab5b90ca-goog
>
>
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v8 22/28] KVM: arm64: Add trace remote for the pKVM hyp
From: Marc Zyngier @ 2025-11-19 17:31 UTC (permalink / raw)
To: Vincent Donnefort
Cc: rostedt, mhiramat, mathieu.desnoyers, linux-trace-kernel,
oliver.upton, joey.gouly, suzuki.poulose, yuzenghui, kvmarm,
linux-arm-kernel, jstultz, qperret, will, aneesh.kumar,
kernel-team, linux-kernel
In-Reply-To: <20251107093840.3779150-23-vdonnefort@google.com>
On Fri, 07 Nov 2025 09:38:34 +0000,
Vincent Donnefort <vdonnefort@google.com> wrote:
>
> When running with KVM protected mode, the hypervisor is able to generate
> events into tracefs compatible ring-buffers. Create a trace remote so
> the kernel can read those buffers.
>
> This currently doesn't provide any event support which will come later.
>
> Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
>
> diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
> index 580426cdbe77..64db254f0448 100644
> --- a/arch/arm64/kvm/Kconfig
> +++ b/arch/arm64/kvm/Kconfig
> @@ -87,6 +87,7 @@ config PKVM_TRACING
> bool
> depends on KVM
> depends on TRACING
> + select TRACE_REMOTE
> select SIMPLE_RING_BUFFER
> default y
>
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index 3ebc0570345c..2c184e3abd8e 100644
> --- a/arch/arm64/kvm/Makefile
> +++ b/arch/arm64/kvm/Makefile
> @@ -30,6 +30,8 @@ kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu.o
> kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o
> kvm-$(CONFIG_PTDUMP_STAGE2_DEBUGFS) += ptdump.o
>
> +kvm-$(CONFIG_PKVM_TRACING) += hyp_trace.o
> +
> always-y := hyp_constants.h hyp-constants.s
>
> define rule_gen_hyp_constants
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 870953b4a8a7..c485e54417e2 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -24,6 +24,7 @@
>
> #define CREATE_TRACE_POINTS
> #include "trace_arm.h"
> +#include "hyp_trace.h"
>
> #include <linux/uaccess.h>
> #include <asm/ptrace.h>
> @@ -2345,6 +2346,9 @@ static int __init init_subsystems(void)
>
> kvm_register_perf_callbacks(NULL);
>
> + err = hyp_trace_init();
> + if (err)
> + kvm_err("Failed to initialize Hyp tracing\n");
> out:
> if (err)
> hyp_cpu_pm_exit();
> diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
> new file mode 100644
> index 000000000000..98051c3fb0c2
> --- /dev/null
> +++ b/arch/arm64/kvm/hyp_trace.c
> @@ -0,0 +1,210 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2025 Google LLC
> + * Author: Vincent Donnefort <vdonnefort@google.com>
> + */
> +
> +#include <linux/trace_remote.h>
> +#include <linux/simple_ring_buffer.h>
> +
> +#include <asm/kvm_host.h>
> +#include <asm/kvm_hyptrace.h>
> +
> +#include "hyp_trace.h"
> +
> +/* Access to this struct within the trace_remote_callbacks are protected by the trace_remote lock */
> +static struct hyp_trace_buffer {
> + struct hyp_trace_desc *desc;
> + size_t desc_size;
> +} trace_buffer;
> +
> +static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_buffer, size_t size)
> +{
> + int nr_bpages = (PAGE_ALIGN(size) / PAGE_SIZE) + 1;
> + size_t backing_size;
> + void *start;
> +
> + backing_size = PAGE_ALIGN(sizeof(struct simple_buffer_page) * nr_bpages *
> + num_possible_cpus());
> +
> + start = alloc_pages_exact(backing_size, GFP_KERNEL_ACCOUNT);
> + if (!start)
> + return -ENOMEM;
> +
> + trace_buffer->desc->bpages_backing_start = (unsigned long)start;
> + trace_buffer->desc->bpages_backing_size = backing_size;
> +
> + return 0;
> +}
> +
> +static void hyp_trace_buffer_free_bpages_backing(struct hyp_trace_buffer *trace_buffer)
> +{
> + free_pages_exact((void *)trace_buffer->desc->bpages_backing_start,
> + trace_buffer->desc->bpages_backing_size);
> +}
> +
> +static int __load_page(unsigned long va)
> +{
> + return kvm_call_hyp_nvhe(__pkvm_host_share_hyp, virt_to_pfn((void *)va), 1);
> +}
I struggle a bit with the nomenclature here. Why is that called
"load"? Surely this is a "map" operation, right? Is that because this
is called at "vcpu load" time? Something else?
Also, how is this working without pKVM, in a normal nVHE environment?
Being able to trace in nVHE is a basic requirement, and I don't see
how this works here.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [RFC PATCH v7 29/31] x86/mm/pti: Implement a TLB flush immediately after a switch to kernel CR3
From: Andy Lutomirski @ 2025-11-19 17:31 UTC (permalink / raw)
To: Valentin Schneider, Linux Kernel Mailing List, linux-mm, rcu,
the arch/x86 maintainers, linux-arm-kernel, loongarch,
linux-riscv, linux-arch, linux-trace-kernel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
H. Peter Anvin, Peter Zijlstra (Intel), Arnaldo Carvalho de Melo,
Josh Poimboeuf, Paolo Bonzini, Arnd Bergmann, Frederic Weisbecker,
Paul E. McKenney, Jason Baron, Steven Rostedt, Ard Biesheuvel,
Sami Tolvanen, David S. Miller, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Boqun Feng, Uladzislau Rezki, Mathieu Desnoyers,
Mel Gorman, Andrew Morton, Masahiro Yamada, Han Shen,
Rik van Riel, Jann Horn, Dan Carpenter, Oleg Nesterov, Juri Lelli,
Clark Williams, Yair Podemsky, Marcelo Tosatti, Daniel Wagner,
Petr Tesarik, Shrikanth Hegde
In-Reply-To: <xhsmhecpukowa.mognet@vschneid-thinkpadt14sgen2i.remote.csb>
On Wed, Nov 19, 2025, at 7:44 AM, Valentin Schneider wrote:
> On 19/11/25 06:31, Andy Lutomirski wrote:
>> On Fri, Nov 14, 2025, at 7:14 AM, Valentin Schneider wrote:
>>> Deferring kernel range TLB flushes requires the guarantee that upon
>>> entering the kernel, no stale entry may be accessed. The simplest way to
>>> provide such a guarantee is to issue an unconditional flush upon switching
>>> to the kernel CR3, as this is the pivoting point where such stale entries
>>> may be accessed.
>>>
>>
>> Doing this together with the PTI CR3 switch has no actual benefit: MOV CR3 doesn’t flush global pages. And doing this in asm is pretty gross. We don’t even get a free sync_core() out of it because INVPCID is not documented as being serializing.
>>
>> Why can’t we do it in C? What’s the actual risk? In order to trip over a stale TLB entry, we would need to deference a pointer to newly allocated kernel virtual memory that was not valid prior to our entry into user mode. I can imagine BPF doing this, but plain noinstr C in the entry path? Especially noinstr C *that has RCU disabled*? We already can’t follow an RCU pointer, and ISTM the only style of kernel code that might do this would use RCU to protect the pointer, and we are already doomed if we follow an RCU pointer to any sort of memory.
>>
>
> So v4 and earlier had the TLB flush faff done in C in the context_tracking entry
> just like sync_core().
>
> My biggest issue with it was that I couldn't figure out a way to instrument
> memory accesses such that I would get an idea of where vmalloc'd accesses
> happen - even with a hackish thing just to survey the landscape. So while I
> agree with your reasoning wrt entry noinstr code, I don't have any way to
> prove it.
> That's unlike the text_poke sync_core() deferral for which I have all of
> that nice objtool instrumentation.
>
> Dave also pointed out that the whole stale entry flush deferral is a risky
> move, and that the sanest thing would be to execute the deferred flush just
> after switching to the kernel CR3.
>
> See the thread surrounding:
> https://lore.kernel.org/lkml/20250114175143.81438-30-vschneid@redhat.com/
>
> mainly Dave's reply and subthread:
> https://lore.kernel.org/lkml/352317e3-c7dc-43b4-b4cb-9644489318d0@intel.com/
>
>> We do need to watch out for NMI/MCE hitting before we flush.
I read a decent fraction of that thread.
Let's consider what we're worried about:
1. Architectural access to a kernel virtual address that has been unmapped, in asm or early C. If it hasn't been remapped, then we oops anyway. If it has, then that means we're accessing a pointer where either the pointer has changed or the pointee has been remapped while we're in user mode, and that's a very strange thing to do for anything that the asm points to or that early C points to, unless RCU is involved. But RCU is already disallowed in the entry paths that might be in extended quiescent states, so I think this is mostly a nonissue.
2. Non-speculative access via GDT access, etc. We can't control this at all, but we're not avoid to move the GDT, IDT, LDT etc of a running task while that task is in user mode. We do move the LDT, but that's quite thoroughly synchronized via IPI. (Should probably be double checked. I wrote that code, but that doesn't mean I remember it exactly.)
3. Speculative TLB fills. We can't control this at all. We have had actual machine checks, on AMD IIRC, due to messing this up. This is why we can't defer a flush after freeing a page table.
4. Speculative or other nonarchitectural loads. One would hope that these are not dangerous. For example, an early version of TDX would machine check if we did a speculative load from TDX memory, but that was fixed. I don't see why this would be materially different between actual userspace execution (without LASS, anyway), kernel asm, and kernel C.
5. Writes to page table dirty bits. I don't think we use these.
In any case, the current implementation in your series is really, really, utterly horrifically slow. It's probably fine for a task that genuinely sits in usermode forever, but I don't think it's likely to be something that we'd be willing to enable for normal kernels and normal tasks. And it would be really nice for the don't-interrupt-user-code still to move toward being always available rather than further from it.
I admit that I'm kind of with dhansen: Zen 3+ can use INVLPGB and doesn't need any of this. Some Intel CPUs support RAR and will eventually be able to use RAR, possibly even for sync_core().
^ permalink raw reply
* Re: [PATCH 2/2] mm/khugepaged: return EAGAIN for transient dirty pages in MADV_COLLAPSE
From: Lorenzo Stoakes @ 2025-11-19 18:16 UTC (permalink / raw)
To: Garg, Shivank
Cc: Andrew Morton, David Hildenbrand, Zi Yan, Baolin Wang,
Liam R . Howlett, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Zach O'Keefe, linux-mm, linux-kernel, linux-trace-kernel
In-Reply-To: <a2770a50-5732-4483-b958-4693f0f08c7e@amd.com>
On Wed, Nov 19, 2025 at 03:55:29PM +0530, Garg, Shivank wrote:
>
>
> On 11/10/2025 5:26 PM, Lorenzo Stoakes wrote:
> > Please, please, please send a cover letter when there's > 1 patch :)
> >
> > This 2/2 replying to 1/2 is a pain (not your fault that perhaps you're not aware
> > of typical mm series style but FYI :P)
> >
> Sure, will do this in V2 (posting today).
>
> > Also there is some tiny conflict on khugepaged.c in mm-new, but it's literally 1
> > #include so probably nothing to worry about.
> > > On Mon, Nov 10, 2025 at 11:32:55AM +0000, Shivank Garg wrote:
> >> When MADV_COLLAPSE encounters dirty file-backed pages, it currently
> >> returns -EINVAL, this is misleading as EINVAL suggests invalid arguments,
> >> whereas dirty pages are a transient condition that may resolve on retry.
> >>
> >> Introduce SCAN_PAGE_DIRTY and map it to -EAGAIN. For khugepaged, this
> >> is harmless as it will revisit the range after async writeback completes.
> >>
> >> Signed-off-by: Shivank Garg <shivankg@amd.com>
> >
> > With comments below addressed, LGTM so:
> >
> > Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
>
> Thank you for the review.
> >
> >> ---
> >> include/trace/events/huge_memory.h | 3 ++-
> >> mm/khugepaged.c | 4 +++-
> >> 2 files changed, 5 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/include/trace/events/huge_memory.h b/include/trace/events/huge_memory.h
> >> index dd94d14a2427..9014a9bbe64c 100644
> >> --- a/include/trace/events/huge_memory.h
> >> +++ b/include/trace/events/huge_memory.h
> >> @@ -38,7 +38,8 @@
> >> EM( SCAN_PAGE_HAS_PRIVATE, "page_has_private") \
> >> EM( SCAN_STORE_FAILED, "store_failed") \
> >> EM( SCAN_COPY_MC, "copy_poisoned_page") \
> >> - EMe(SCAN_PAGE_FILLED, "page_filled")
> >> + EM(SCAN_PAGE_FILLED, "page_filled") \
> >> + EMe(SCAN_PAGE_DIRTY, "page_dirty")
> >>
> >> #undef EM
> >> #undef EMe
> >> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> >> index d08ed6eb9ce1..7df329c9c87d 100644
> >> --- a/mm/khugepaged.c
> >> +++ b/mm/khugepaged.c
> >> @@ -60,6 +60,7 @@ enum scan_result {
> >> SCAN_STORE_FAILED,
> >> SCAN_COPY_MC,
> >> SCAN_PAGE_FILLED,
> >> + SCAN_PAGE_DIRTY,
> >
> > it feels like a lot to add a scan result for this, but I mean... probably
> > actually valid.
> >
> >> };
> >>
> >> #define CREATE_TRACE_POINTS
> >> @@ -1967,7 +1968,7 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
> >> */
> >> xas_unlock_irq(&xas);
> >> filemap_flush(mapping);
> >> - result = SCAN_FAIL;
> >> + result = SCAN_PAGE_DIRTY;
> >> goto xa_unlocked;
> >
> > Hmmm shmem dirty is going to be weird but we also have:
> >
> > if (!is_shmem && (folio_test_dirty(folio) ||
> > folio_test_writeback(folio))) {
> > /*
> > * khugepaged only works on read-only fd, so this
> > * folio is dirty because it hasn't been flushed
> > * since first write.
> > */
> > result = SCAN_FAIL;
> > goto out_unlock;
> > }
> >
> > It's weird though, why would we have writeback, surely handled by swap, and
> > won't it be like anon, i.e. pretty well always dirty? This comment seems
> > copy/pasta wrong.
> >
> > We do need to at least mention in commit message that shmem is explicitly
> > excluded.
> >
>
> Looking at the code, the dirty/writeback checks where I'm making changes
> are all in the !is_shmem branch, so it only affects regular files, not
> shmem.
Yeah sorry I think I was being somehow blind to the fact that each
dirty/writeback test has !is_shmem, esp. given I literally quote it there :)
So err ignore me I think here haha
>
> Should I mention in the commit message that these changes are limited
> to regular files and don't affect shmem?
No that's fine.
>
> I'm not sure I fully understood your concern on shmem. Could you please elaborate?
Yeah I think I just misread the code after a long day :P
>
> Thanks,
> Shivank
Cheers, Lorenzo
>
>
> >
> >> } else if (folio_test_writeback(folio)) {
> >> xas_unlock_irq(&xas);
> >> @@ -2747,6 +2748,7 @@ static int madvise_collapse_errno(enum scan_result r)
> >> case SCAN_PAGE_LRU:
> >> case SCAN_DEL_PAGE_LRU:
> >> case SCAN_PAGE_FILLED:
> >> + case SCAN_PAGE_DIRTY:
> >> return -EAGAIN;
> >> /*
> >> * Other: Trying again likely not to succeed / error intrinsic to
> >> --
> >> 2.43.0
> >>
>
^ permalink raw reply
* Re: [RFC PATCH v7 30/31] x86/mm, mm/vmalloc: Defer kernel TLB flush IPIs under CONFIG_COALESCE_TLBI=y
From: Dave Hansen @ 2025-11-19 18:31 UTC (permalink / raw)
To: Valentin Schneider, linux-kernel, linux-mm, rcu, x86,
linux-arm-kernel, loongarch, linux-riscv, linux-arch,
linux-trace-kernel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
H. Peter Anvin, Andy Lutomirski, Peter Zijlstra,
Arnaldo Carvalho de Melo, Josh Poimboeuf, Paolo Bonzini,
Arnd Bergmann, Frederic Weisbecker, Paul E. McKenney, Jason Baron,
Steven Rostedt, Ard Biesheuvel, Sami Tolvanen, David S. Miller,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Mathieu Desnoyers, Mel Gorman, Andrew Morton,
Masahiro Yamada, Han Shen, Rik van Riel, Jann Horn, Dan Carpenter,
Oleg Nesterov, Juri Lelli, Clark Williams, Yair Podemsky,
Marcelo Tosatti, Daniel Wagner, Petr Tesarik, Shrikanth Hegde
In-Reply-To: <20251114151428.1064524-10-vschneid@redhat.com>
On 11/14/25 07:14, Valentin Schneider wrote:
> +static bool flush_tlb_kernel_cond(int cpu, void *info)
> +{
> + return housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE) ||
> + per_cpu(kernel_cr3_loaded, cpu);
> +}
Is it OK that 'kernel_cr3_loaded' can be be stale? Since it's not part
of the instruction that actually sets CR3, there's a window between when
'kernel_cr3_loaded' is set (or cleared) and CR3 is actually written.
Is that OK?
It seems like it could lead to both unnecessary IPIs being sent and for
IPIs to be missed.
I still _really_ wish folks would be willing to get newer CPUs to get
this behavior rather than going through all this complexity. RAR in
particular was *specifically* designed to keep TLB flushing IPIs from
blipping userspace for too long.
^ permalink raw reply
* Re: [RFC PATCH v7 30/31] x86/mm, mm/vmalloc: Defer kernel TLB flush IPIs under CONFIG_COALESCE_TLBI=y
From: Andy Lutomirski @ 2025-11-19 18:33 UTC (permalink / raw)
To: Dave Hansen, Valentin Schneider, Linux Kernel Mailing List,
linux-mm, rcu, the arch/x86 maintainers, linux-arm-kernel,
loongarch, linux-riscv, linux-arch, linux-trace-kernel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
H. Peter Anvin, Peter Zijlstra (Intel), Arnaldo Carvalho de Melo,
Josh Poimboeuf, Paolo Bonzini, Arnd Bergmann, Frederic Weisbecker,
Paul E. McKenney, Jason Baron, Steven Rostedt, Ard Biesheuvel,
Sami Tolvanen, David S. Miller, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Boqun Feng, Uladzislau Rezki, Mathieu Desnoyers,
Mel Gorman, Andrew Morton, Masahiro Yamada, Han Shen,
Rik van Riel, Jann Horn, Dan Carpenter, Oleg Nesterov, Juri Lelli,
Clark Williams, Yair Podemsky, Marcelo Tosatti, Daniel Wagner,
Petr Tesarik, Shrikanth Hegde
In-Reply-To: <bad9eaaa-561a-498a-90d1-fe3a3ab8ba37@intel.com>
On Wed, Nov 19, 2025, at 10:31 AM, Dave Hansen wrote:
> On 11/14/25 07:14, Valentin Schneider wrote:
>> +static bool flush_tlb_kernel_cond(int cpu, void *info)
>> +{
>> + return housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE) ||
>> + per_cpu(kernel_cr3_loaded, cpu);
>> +}
>
> Is it OK that 'kernel_cr3_loaded' can be be stale? Since it's not part
> of the instruction that actually sets CR3, there's a window between when
> 'kernel_cr3_loaded' is set (or cleared) and CR3 is actually written.
>
> Is that OK?
>
> It seems like it could lead to both unnecessary IPIs being sent and for
> IPIs to be missed.
I read the code earlier today and I *think* it’s maybe okay. It’s quite confusing that this thing is split among multiple patches, and the memory ordering issues need comments.
The fact that the big flush is basically unconditional at this point helps. The fact that it’s tangled up with CR3 even though the current implementation has nothing to do with CR3 does not help.
I’m kind of with dhansen though — the fact that the implementation is so nasty coupled with the fact that modern CPUs can do this in hardware makes the whole thing kind of unpalatable.
>
> I still _really_ wish folks would be willing to get newer CPUs to get
> this behavior rather than going through all this complexity. RAR in
> particular was *specifically* designed to keep TLB flushing IPIs from
> blipping userspace for too long.
^ permalink raw reply
* Re: [PATCH] selftests: tracing: Add tprobe enable/disable testcase
From: Shuah Khan @ 2025-11-19 21:44 UTC (permalink / raw)
To: Masami Hiramatsu (Google), Steven Rostedt, Shuah Khan
Cc: Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest, Shuah Khan
In-Reply-To: <176252610176.214996.3978515319000806265.stgit@devnote2>
On 11/7/25 07:35, Masami Hiramatsu (Google) wrote:
> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>
> Commit 2867495dea86 ("tracing: tprobe-events: Register tracepoint when
> enable tprobe event") caused regression bug and tprobe did not work.
> To prevent similar problems, add a testcase which enables/disables a
> tprobe and check the results.
>
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Steve, do you want me to take this through my tree?
thanks,
-- Shuah
^ permalink raw reply
* Re: [PATCH] selftests: tracing: Add tprobe enable/disable testcase
From: Steven Rostedt @ 2025-11-19 22:06 UTC (permalink / raw)
To: Shuah Khan
Cc: Masami Hiramatsu (Google), Shuah Khan, Mathieu Desnoyers,
linux-kernel, linux-trace-kernel, linux-kselftest
In-Reply-To: <f5f272e1-e164-4bb1-bfd0-42edd5a125c5@linuxfoundation.org>
On Wed, 19 Nov 2025 14:44:22 -0700
Shuah Khan <skhan@linuxfoundation.org> wrote:
> On 11/7/25 07:35, Masami Hiramatsu (Google) wrote:
> > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> >
> > Commit 2867495dea86 ("tracing: tprobe-events: Register tracepoint when
> > enable tprobe event") caused regression bug and tprobe did not work.
> > To prevent similar problems, add a testcase which enables/disables a
> > tprobe and check the results.
> >
> > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>
> Steve, do you want me to take this through my tree?
Yes please. Masami's an official maintainer and mostly handles all things
"probe" related. This is his domain ;-)
Thanks,
-- Steve
^ permalink raw reply
* Re: [PATCH] selftests: tracing: Update fprobe selftest for ftrace based fprobe
From: Shuah Khan @ 2025-11-19 22:16 UTC (permalink / raw)
To: Masami Hiramatsu (Google), Steven Rostedt, Shuah Khan,
Heiko Carstens
Cc: Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest, Shuah Khan
In-Reply-To: <176295318112.431538.11780280333728368327.stgit@devnote2>
On 11/12/25 06:13, Masami Hiramatsu (Google) wrote:
> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>
> Since the ftrace fprobe is both fgraph and ftrace based implemented,
> the selftest needs to be updated. This does not count the actual
> number of lines, but just check the differences.
>
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
> .../ftrace/test.d/dynevent/add_remove_fprobe.tc | 18 ++++--------------
> 1 file changed, 4 insertions(+), 14 deletions(-)
>
Steve, do you want me to take this through my tree?
thanks,
-- Shuah
^ permalink raw reply
* Re: [PATCH] selftests: tracing: Update fprobe selftest for ftrace based fprobe
From: Steven Rostedt @ 2025-11-19 22:34 UTC (permalink / raw)
To: Shuah Khan
Cc: Masami Hiramatsu (Google), Shuah Khan, Heiko Carstens,
Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest
In-Reply-To: <f6831d9a-4ea6-4100-9b1b-716ac93e1cdd@linuxfoundation.org>
On Wed, 19 Nov 2025 15:16:04 -0700
Shuah Khan <skhan@linuxfoundation.org> wrote:
> On 11/12/25 06:13, Masami Hiramatsu (Google) wrote:
> > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> >
> > Since the ftrace fprobe is both fgraph and ftrace based implemented,
> > the selftest needs to be updated. This does not count the actual
> > number of lines, but just check the differences.
> >
> > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > ---
> > .../ftrace/test.d/dynevent/add_remove_fprobe.tc | 18 ++++--------------
> > 1 file changed, 4 insertions(+), 14 deletions(-)
> >
>
> Steve, do you want me to take this through my tree?
Yes please, unless Masami thinks otherwise.
-- Steve
^ permalink raw reply
* [PATCH 35/44] bpf: use min() instead of min_t()
From: david.laight.linux @ 2025-11-19 22:41 UTC (permalink / raw)
To: linux-kernel, bpf, linux-trace-kernel
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, KP Singh,
Masami Hiramatsu, Matt Bobrowski, Song Liu, Steven Rostedt,
David Laight
In-Reply-To: <20251119224140.8616-1-david.laight.linux@gmail.com>
From: David Laight <david.laight.linux@gmail.com>
min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'.
Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long'
and so cannot discard significant bits.
In this case the 'unsigned long' value is small enough that the result
is ok.
Detected by an extra check added to min_t().
Signed-off-by: David Laight <david.laight.linux@gmail.com>
---
kernel/trace/bpf_trace.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 4f87c16d915a..ee3152df767c 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1515,7 +1515,7 @@ BPF_CALL_4(bpf_read_branch_records, struct bpf_perf_event_data_kern *, ctx,
if (!buf || (size % br_entry_size != 0))
return -EINVAL;
- to_copy = min_t(u32, br_stack->nr * br_entry_size, size);
+ to_copy = min(br_stack->nr * br_entry_size, size);
memcpy(buf, br_stack->entries, to_copy);
return to_copy;
--
2.39.5
^ permalink raw reply related
* [PATCH 00/44] Change a lot of min_t() that might mask high bits
From: david.laight.linux @ 2025-11-19 22:40 UTC (permalink / raw)
To: linux-kernel
Cc: Alan Stern, Alexander Viro, Alexei Starovoitov, Andi Shyti,
Andreas Dilger, Andrew Lunn, Andrew Morton, Andrii Nakryiko,
Andy Shevchenko, Ard Biesheuvel, Arnaldo Carvalho de Melo,
Bjorn Helgaas, Borislav Petkov, Christian Brauner,
Christian König, Christoph Hellwig, Daniel Borkmann,
Dan Williams, Dave Hansen, Dave Jiang, David Ahern,
David Hildenbrand, Davidlohr Bueso, David S. Miller, Dennis Zhou,
Eric Dumazet, Greg Kroah-Hartman, Herbert Xu, Ingo Molnar,
Jakub Kicinski, Jakub Sitnicki, James E.J. Bottomley,
Jarkko Sakkinen, Jason A. Donenfeld, Jens Axboe, Jiri Slaby,
Johannes Weiner, John Allen, Jonathan Cameron, Juergen Gross,
Kees Cook, KP Singh, Linus Walleij, Martin K. Petersen,
Matthew Wilcox (Oracle), Mika Westerberg, Mike Rapoport,
Miklos Szeredi, Namhyung Kim, Neal Cardwell, nic_swsd,
OGAWA Hirofumi, Olivia Mackall, Paolo Abeni, Paolo Bonzini,
Peter Huewe, Peter Zijlstra, Rafael J. Wysocki,
Sean Christopherson, Srinivas Kandagatla, Stefano Stabellini,
Steven Rostedt, Tejun Heo, Theodore Ts'o, Thomas Gleixner,
Tom Lendacky, Willem de Bruijn, x86, Yury Norov, amd-gfx, bpf,
cgroups, dri-devel, io-uring, kvm, linux-acpi, linux-block,
linux-crypto, linux-cxl, linux-efi, linux-ext4, linux-fsdevel,
linux-gpio, linux-i2c, linux-integrity, linux-mm, linux-nvme,
linux-pci, linux-perf-users, linux-scsi, linux-serial,
linux-trace-kernel, linux-usb, mptcp, netdev, usb-storage,
David Laight
From: David Laight <david.laight.linux@gmail.com>
It in not uncommon for code to use min_t(uint, a, b) when one of a or b
is 64bit and can have a value that is larger than 2^32;
This is particularly prevelant with:
uint_var = min_t(uint, uint_var, uint64_expression);
Casts to u8 and u16 are very likely to discard significant bits.
These can be detected at compile time by changing min_t(), for example:
#define CHECK_SIZE(fn, type, val) \
BUILD_BUG_ON_MSG(sizeof (val) > sizeof (type) && \
!statically_true(((val) >> 8 * (sizeof (type) - 1)) < 256), \
fn "() significant bits of '" #val "' may be discarded")
#define min_t(type, x, y) ({ \
CHECK_SIZE("min_t", type, x); \
CHECK_SIZE("min_t", type, y); \
__cmp_once(min, type, x, y); })
(and similar changes to max_t() and clamp_t().)
This shows up some real bugs, some unlikely bugs and some false positives.
In most cases both arguments are unsigned type (just different ones)
and min_t() can just be replaced by min().
The patches are all independant and are most of the ones needed to
get the x86-64 kernel I build to compile.
I've not tried building an allyesconfig or allmodconfig kernel.
I've also not included the patch to minmax.h itself.
I've tried to put the patches that actually fix things first.
The last one is 0009.
I gave up on fixing sched/fair.c - it is too broken for a single patch!
The patch for net/ipv4/tcp.c is also absent because do_tcp_getsockopt()
needs multiple/larger changes to make it 'sane'.
I've had to trim the 124 maintainers/lists that get_maintainer.pl finds
from 124 to under 100 to be able to send the cover letter.
The individual patches only go to the addresses found for the associated files.
That reduces the number of emails to a less unsane number.
David Laight (44):
x86/asm/bitops: Change the return type of variable__ffs() to unsigned
int
ext4: Fix saturation of 64bit inode times for old filesystems
perf: Fix branch stack callchain limit
io_uring/net: Change some dubious min_t()
ipc/msg: Fix saturation of percpu counts in msgctl_info()
bpf: Verifier, remove some unusual uses of min_t() and max_t()
net/core/flow_dissector: Fix cap of __skb_flow_dissect() return value.
net: ethtool: Use min3() instead of nested min_t(u16,...)
ipv6: __ip6_append_data() don't abuse max_t() casts
x86/crypto: ctr_crypt() use min() instead of min_t()
arch/x96/kvm: use min() instead of min_t()
block: use min() instead of min_t()
drivers/acpi: use min() instead of min_t()
drivers/char/hw_random: use min3() instead of nested min_t()
drivers/char/tpm: use min() instead of min_t()
drivers/crypto/ccp: use min() instead of min_t()
drivers/cxl: use min() instead of min_t()
drivers/gpio: use min() instead of min_t()
drivers/gpu/drm/amd: use min() instead of min_t()
drivers/i2c/busses: use min() instead of min_t()
drivers/net/ethernet/realtek: use min() instead of min_t()
drivers/nvme: use min() instead of min_t()
arch/x86/mm: use min() instead of min_t()
drivers/nvmem: use min() instead of min_t()
drivers/pci: use min() instead of min_t()
drivers/scsi: use min() instead of min_t()
drivers/tty/vt: use umin() instead of min_t(u16, ...) for row/col
limits
drivers/usb/storage: use min() instead of min_t()
drivers/xen: use min() instead of min_t()
fs: use min() or umin() instead of min_t()
block: bvec.h: use min() instead of min_t()
nodemask: use min() instead of min_t()
ipc: use min() instead of min_t()
bpf: use min() instead of min_t()
bpf_trace: use min() instead of min_t()
lib/bucket_locks: use min() instead of min_t()
lib/crypto/mpi: use min() instead of min_t()
lib/dynamic_queue_limits: use max() instead of max_t()
mm: use min() instead of min_t()
net: Don't pass bitfields to max_t()
net/core: Change loop conditions so min() can be used
net: use min() instead of min_t()
net/netlink: Use umin() to avoid min_t(int, ...) discarding high bits
net/mptcp: Change some dubious min_t(int, ...) to min()
arch/x86/crypto/aesni-intel_glue.c | 3 +-
arch/x86/include/asm/bitops.h | 18 +++++-------
arch/x86/kvm/emulate.c | 3 +-
arch/x86/kvm/lapic.c | 2 +-
arch/x86/kvm/mmu/mmu.c | 2 +-
arch/x86/mm/pat/set_memory.c | 12 ++++----
block/blk-iocost.c | 6 ++--
block/blk-settings.c | 2 +-
block/partitions/efi.c | 3 +-
drivers/acpi/property.c | 2 +-
drivers/char/hw_random/core.c | 2 +-
drivers/char/tpm/tpm1-cmd.c | 2 +-
drivers/char/tpm/tpm_tis_core.c | 4 +--
drivers/crypto/ccp/ccp-dev.c | 2 +-
drivers/cxl/core/mbox.c | 2 +-
drivers/gpio/gpiolib-acpi-core.c | 2 +-
.../gpu/drm/amd/amdgpu/amdgpu_doorbell_mgr.c | 4 +--
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 2 +-
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 +-
drivers/i2c/busses/i2c-designware-master.c | 2 +-
drivers/net/ethernet/realtek/r8169_main.c | 3 +-
drivers/nvme/host/pci.c | 3 +-
drivers/nvme/host/zns.c | 3 +-
drivers/nvmem/core.c | 2 +-
drivers/pci/probe.c | 3 +-
drivers/scsi/hosts.c | 2 +-
drivers/tty/vt/selection.c | 9 +++---
drivers/usb/storage/protocol.c | 3 +-
drivers/xen/grant-table.c | 2 +-
fs/buffer.c | 2 +-
fs/exec.c | 2 +-
fs/ext4/ext4.h | 2 +-
fs/ext4/mballoc.c | 3 +-
fs/ext4/resize.c | 2 +-
fs/ext4/super.c | 2 +-
fs/fat/dir.c | 4 +--
fs/fat/file.c | 3 +-
fs/fuse/dev.c | 2 +-
fs/fuse/file.c | 8 ++---
fs/splice.c | 2 +-
include/linux/bvec.h | 3 +-
include/linux/nodemask.h | 9 +++---
include/linux/perf_event.h | 2 +-
include/net/tcp_ecn.h | 5 ++--
io_uring/net.c | 6 ++--
ipc/mqueue.c | 4 +--
ipc/msg.c | 6 ++--
kernel/bpf/core.c | 4 +--
kernel/bpf/log.c | 2 +-
kernel/bpf/verifier.c | 29 +++++++------------
kernel/trace/bpf_trace.c | 2 +-
lib/bucket_locks.c | 2 +-
lib/crypto/mpi/mpicoder.c | 2 +-
lib/dynamic_queue_limits.c | 2 +-
mm/gup.c | 4 +--
mm/memblock.c | 2 +-
mm/memory.c | 2 +-
mm/percpu.c | 2 +-
mm/truncate.c | 3 +-
mm/vmscan.c | 2 +-
net/core/datagram.c | 6 ++--
net/core/flow_dissector.c | 7 ++---
net/core/net-sysfs.c | 3 +-
net/core/skmsg.c | 4 +--
net/ethtool/cmis_cdb.c | 7 ++---
net/ipv4/fib_trie.c | 2 +-
net/ipv4/tcp_input.c | 4 +--
net/ipv4/tcp_output.c | 5 ++--
net/ipv4/tcp_timer.c | 4 +--
net/ipv6/addrconf.c | 8 ++---
net/ipv6/ip6_output.c | 7 +++--
net/ipv6/ndisc.c | 5 ++--
net/mptcp/protocol.c | 8 ++---
net/netlink/genetlink.c | 9 +++---
net/packet/af_packet.c | 2 +-
net/unix/af_unix.c | 4 +--
76 files changed, 141 insertions(+), 176 deletions(-)
--
2.39.5
^ permalink raw reply
* Re: [PATCH] selftests: tracing: Update fprobe selftest for ftrace based fprobe
From: Shuah Khan @ 2025-11-19 22:56 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu (Google), Shuah Khan, Heiko Carstens,
Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest, Shuah Khan
In-Reply-To: <20251119173441.3aa33415@gandalf.local.home>
On 11/19/25 15:34, Steven Rostedt wrote:
> On Wed, 19 Nov 2025 15:16:04 -0700
> Shuah Khan <skhan@linuxfoundation.org> wrote:
>
>> On 11/12/25 06:13, Masami Hiramatsu (Google) wrote:
>>> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>>>
>>> Since the ftrace fprobe is both fgraph and ftrace based implemented,
>>> the selftest needs to be updated. This does not count the actual
>>> number of lines, but just check the differences.
>>>
>>> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>>> ---
>>> .../ftrace/test.d/dynevent/add_remove_fprobe.tc | 18 ++++--------------
>>> 1 file changed, 4 insertions(+), 14 deletions(-)
>>>
>>
>> Steve, do you want me to take this through my tree?
>
> Yes please, unless Masami thinks otherwise.
>
Applied to linux-kselftest next for Linux 6.19-rc1.
thanks,
-- Shuah
^ permalink raw reply
* Re: [PATCH] selftests: tracing: Add tprobe enable/disable testcase
From: Shuah Khan @ 2025-11-19 22:56 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu (Google), Shuah Khan, Mathieu Desnoyers,
linux-kernel, linux-trace-kernel, linux-kselftest, Shuah Khan
In-Reply-To: <20251119170611.6eff8df7@gandalf.local.home>
On 11/19/25 15:06, Steven Rostedt wrote:
> On Wed, 19 Nov 2025 14:44:22 -0700
> Shuah Khan <skhan@linuxfoundation.org> wrote:
>
>> On 11/7/25 07:35, Masami Hiramatsu (Google) wrote:
>>> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>>>
>>> Commit 2867495dea86 ("tracing: tprobe-events: Register tracepoint when
>>> enable tprobe event") caused regression bug and tprobe did not work.
>>> To prevent similar problems, add a testcase which enables/disables a
>>> tprobe and check the results.
>>>
>>> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>>
>> Steve, do you want me to take this through my tree?
>
> Yes please. Masami's an official maintainer and mostly handles all things
> "probe" related. This is his domain ;-)
>
> Thanks,
>
> -- Steve
Applied to linux-kselftest next for Linux 6.19-rc1.
thanks,
-- Shuah
^ permalink raw reply
* Re: [PATCH v4 2/5] perf tools: Minimal DEFERRED_CALLCHAIN support
From: Namhyung Kim @ 2025-11-20 0:56 UTC (permalink / raw)
To: Ian Rogers
Cc: Arnaldo Carvalho de Melo, James Clark, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users,
Steven Rostedt, Josh Poimboeuf, Indu Bhagat, Jens Remus,
Mathieu Desnoyers, linux-trace-kernel, bpf
In-Reply-To: <CAP-5=fW0D3A5kZ8xKgH8rzczk5ezQGrymddE5_EszarQG--TtQ@mail.gmail.com>
On Wed, Nov 19, 2025 at 08:48:35AM -0800, Ian Rogers wrote:
> On Sat, Nov 15, 2025 at 3:41 PM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > Add a new event type for deferred callchains and a new callback for the
> > struct perf_tool. For now it doesn't actually handle the deferred
> > callchains but it just marks the sample if it has the PERF_CONTEXT_
> > USER_DEFFERED in the callchain array.
> >
> > At least, perf report can dump the raw data with this change. Actually
> > this requires the next commit to enable attr.defer_callchain, but if you
> > already have a data file, it'll show the following result.
> >
> > $ perf report -D
> > ...
> > 0x2158@perf.data [0x40]: event: 22
> > .
> > . ... raw event: size 64 bytes
> > . 0000: 16 00 00 00 02 00 40 00 06 00 00 00 0b 00 00 00 ......@.........
> > . 0010: 03 00 00 00 00 00 00 00 a7 7f 33 fe 18 7f 00 00 ..........3.....
> > . 0020: 0f 0e 33 fe 18 7f 00 00 48 14 33 fe 18 7f 00 00 ..3.....H.3.....
> > . 0030: 08 09 00 00 08 09 00 00 e6 7a e7 35 1c 00 00 00 .........z.5....
> >
> > 121163447014 0x2158 [0x40]: PERF_RECORD_CALLCHAIN_DEFERRED(IP, 0x2): 2312/2312: 0xb00000006
> > ... FP chain: nr:3
> > ..... 0: 00007f18fe337fa7
> > ..... 1: 00007f18fe330e0f
> > ..... 2: 00007f18fe331448
> > : unhandled!
> >
> > Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> > ---
> > tools/lib/perf/include/perf/event.h | 13 ++++++++++
> > tools/perf/util/event.c | 1 +
> > tools/perf/util/evsel.c | 31 +++++++++++++++++++++--
> > tools/perf/util/machine.c | 1 +
> > tools/perf/util/perf_event_attr_fprintf.c | 2 ++
> > tools/perf/util/sample.h | 2 ++
> > tools/perf/util/session.c | 20 +++++++++++++++
> > tools/perf/util/tool.c | 1 +
> > tools/perf/util/tool.h | 3 ++-
> > 9 files changed, 71 insertions(+), 3 deletions(-)
> >
> > diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h
> > index aa1e91c97a226e1a..43a8cb04994fa033 100644
> > --- a/tools/lib/perf/include/perf/event.h
> > +++ b/tools/lib/perf/include/perf/event.h
> > @@ -151,6 +151,18 @@ struct perf_record_switch {
> > __u32 next_prev_tid;
> > };
> >
> > +struct perf_record_callchain_deferred {
> > + struct perf_event_header header;
> > + /*
> > + * This is to match kernel and (deferred) user stacks together.
> > + * The kernel part will be in the sample callchain array after
> > + * the PERF_CONTEXT_USER_DEFERRED entry.
> > + */
> > + __u64 cookie;
> > + __u64 nr;
> > + __u64 ips[];
> > +};
> > +
> > struct perf_record_header_attr {
> > struct perf_event_header header;
> > struct perf_event_attr attr;
> > @@ -523,6 +535,7 @@ union perf_event {
> > struct perf_record_read read;
> > struct perf_record_throttle throttle;
> > struct perf_record_sample sample;
> > + struct perf_record_callchain_deferred callchain_deferred;
> > struct perf_record_bpf_event bpf;
> > struct perf_record_ksymbol ksymbol;
> > struct perf_record_text_poke_event text_poke;
> > diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
> > index fcf44149feb20c35..4c92cc1a952c1d9f 100644
> > --- a/tools/perf/util/event.c
> > +++ b/tools/perf/util/event.c
> > @@ -61,6 +61,7 @@ static const char *perf_event__names[] = {
> > [PERF_RECORD_CGROUP] = "CGROUP",
> > [PERF_RECORD_TEXT_POKE] = "TEXT_POKE",
> > [PERF_RECORD_AUX_OUTPUT_HW_ID] = "AUX_OUTPUT_HW_ID",
> > + [PERF_RECORD_CALLCHAIN_DEFERRED] = "CALLCHAIN_DEFERRED",
> > [PERF_RECORD_HEADER_ATTR] = "ATTR",
> > [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
> > [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
> > diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> > index 989c56d4a23f74f4..5ee3e7dee93fbbcb 100644
> > --- a/tools/perf/util/evsel.c
> > +++ b/tools/perf/util/evsel.c
> > @@ -3089,6 +3089,20 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
> > data->data_src = PERF_MEM_DATA_SRC_NONE;
> > data->vcpu = -1;
> >
> > + if (event->header.type == PERF_RECORD_CALLCHAIN_DEFERRED) {
> > + const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
> > +
> > + data->callchain = (struct ip_callchain *)&event->callchain_deferred.nr;
> > + if (data->callchain->nr > max_callchain_nr)
> > + return -EFAULT;
> > +
> > + data->deferred_cookie = event->callchain_deferred.cookie;
> > +
> > + if (evsel->core.attr.sample_id_all)
> > + perf_evsel__parse_id_sample(evsel, event, data);
> > + return 0;
> > + }
> > +
> > if (event->header.type != PERF_RECORD_SAMPLE) {
> > if (!evsel->core.attr.sample_id_all)
> > return 0;
> > @@ -3213,12 +3227,25 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
> >
> > if (type & PERF_SAMPLE_CALLCHAIN) {
> > const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
> > + u64 callchain_nr;
> >
> > OVERFLOW_CHECK_u64(array);
> > data->callchain = (struct ip_callchain *)array++;
> > - if (data->callchain->nr > max_callchain_nr)
> > + callchain_nr = data->callchain->nr;
> > + if (callchain_nr > max_callchain_nr)
> > return -EFAULT;
> > - sz = data->callchain->nr * sizeof(u64);
> > + sz = callchain_nr * sizeof(u64);
> > + /*
> > + * Save the cookie for the deferred user callchain. The last 2
> > + * entries in the callchain should be the context marker and the
> > + * cookie. The cookie will be used to match PERF_RECORD_
> > + * CALLCHAIN_DEFERRED later.
> > + */
> > + if (evsel->core.attr.defer_callchain && callchain_nr >= 2 &&
> > + data->callchain->ips[callchain_nr - 2] == PERF_CONTEXT_USER_DEFERRED) {
> > + data->deferred_cookie = data->callchain->ips[callchain_nr - 1];
> > + data->deferred_callchain = true;
> > + }
> > OVERFLOW_CHECK(array, sz, max_size);
> > array = (void *)array + sz;
> > }
> > diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
> > index b5dd42588c916d91..841b711d970e9457 100644
> > --- a/tools/perf/util/machine.c
> > +++ b/tools/perf/util/machine.c
> > @@ -2124,6 +2124,7 @@ static int add_callchain_ip(struct thread *thread,
> > *cpumode = PERF_RECORD_MISC_KERNEL;
> > break;
> > case PERF_CONTEXT_USER:
> > + case PERF_CONTEXT_USER_DEFERRED:
> > *cpumode = PERF_RECORD_MISC_USER;
> > break;
> > default:
> > diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c
> > index 66b666d9ce649dd7..741c3d657a8b6ae7 100644
> > --- a/tools/perf/util/perf_event_attr_fprintf.c
> > +++ b/tools/perf/util/perf_event_attr_fprintf.c
> > @@ -343,6 +343,8 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
> > PRINT_ATTRf(inherit_thread, p_unsigned);
> > PRINT_ATTRf(remove_on_exec, p_unsigned);
> > PRINT_ATTRf(sigtrap, p_unsigned);
> > + PRINT_ATTRf(defer_callchain, p_unsigned);
> > + PRINT_ATTRf(defer_output, p_unsigned);
> >
> > PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned, false);
> > PRINT_ATTRf(bp_type, p_unsigned);
> > diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h
> > index fae834144ef42105..a8307b20a9ea8066 100644
> > --- a/tools/perf/util/sample.h
> > +++ b/tools/perf/util/sample.h
> > @@ -107,6 +107,8 @@ struct perf_sample {
> > /** @weight3: On x86 holds retire_lat, on powerpc holds p_stage_cyc. */
> > u16 weight3;
> > bool no_hw_idx; /* No hw_idx collected in branch_stack */
> > + bool deferred_callchain; /* Has deferred user callchains */
> > + u64 deferred_cookie;
> > char insn[MAX_INSN];
> > void *raw_data;
> > struct ip_callchain *callchain;
> > diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> > index 4b0236b2df2913e1..361e15c1f26a96d0 100644
> > --- a/tools/perf/util/session.c
> > +++ b/tools/perf/util/session.c
> > @@ -720,6 +720,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
> > [PERF_RECORD_CGROUP] = perf_event__cgroup_swap,
> > [PERF_RECORD_TEXT_POKE] = perf_event__text_poke_swap,
> > [PERF_RECORD_AUX_OUTPUT_HW_ID] = perf_event__all64_swap,
> > + [PERF_RECORD_CALLCHAIN_DEFERRED] = perf_event__all64_swap,
> > [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap,
> > [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap,
> > [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
> > @@ -854,6 +855,9 @@ static void callchain__printf(struct evsel *evsel,
> > for (i = 0; i < callchain->nr; i++)
> > printf("..... %2d: %016" PRIx64 "\n",
> > i, callchain->ips[i]);
> > +
> > + if (sample->deferred_callchain)
> > + printf("...... (deferred)\n");
> > }
> >
> > static void branch_stack__printf(struct perf_sample *sample,
> > @@ -1123,6 +1127,19 @@ static void dump_sample(struct evsel *evsel, union perf_event *event,
> > sample_read__printf(sample, evsel->core.attr.read_format);
> > }
> >
> > +static void dump_deferred_callchain(struct evsel *evsel, union perf_event *event,
> > + struct perf_sample *sample)
> > +{
> > + if (!dump_trace)
> > + return;
> > +
> > + printf("(IP, 0x%x): %d/%d: %#" PRIx64 "\n",
> > + event->header.misc, sample->pid, sample->tid, sample->deferred_cookie);
> > +
> > + if (evsel__has_callchain(evsel))
> > + callchain__printf(evsel, sample);
> > +}
> > +
> > static void dump_read(struct evsel *evsel, union perf_event *event)
> > {
> > struct perf_record_read *read_event = &event->read;
> > @@ -1353,6 +1370,9 @@ static int machines__deliver_event(struct machines *machines,
> > return tool->text_poke(tool, event, sample, machine);
> > case PERF_RECORD_AUX_OUTPUT_HW_ID:
> > return tool->aux_output_hw_id(tool, event, sample, machine);
> > + case PERF_RECORD_CALLCHAIN_DEFERRED:
> > + dump_deferred_callchain(evsel, event, sample);
> > + return tool->callchain_deferred(tool, event, sample, evsel, machine);
> > default:
> > ++evlist->stats.nr_unknown_events;
> > return -1;
> > diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c
> > index 22a8a4ffe05f778e..f732d33e7f895ed4 100644
> > --- a/tools/perf/util/tool.c
> > +++ b/tools/perf/util/tool.c
> > @@ -287,6 +287,7 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events)
> > tool->read = process_event_sample_stub;
> > tool->throttle = process_event_stub;
> > tool->unthrottle = process_event_stub;
> > + tool->callchain_deferred = process_event_sample_stub;
>
> nit: there's a similar change needed to delegate_tool__init, that code
> is currently unused.
Will update in v2.
>
> Reviewed-by: Ian Rogers <irogers@google.com>
Thanks for the review!
Namhyung
>
> > tool->attr = process_event_synth_attr_stub;
> > tool->event_update = process_event_synth_event_update_stub;
> > tool->tracing_data = process_event_synth_tracing_data_stub;
> > diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
> > index 88337cee1e3e2be3..9b9f0a8cbf3de4b5 100644
> > --- a/tools/perf/util/tool.h
> > +++ b/tools/perf/util/tool.h
> > @@ -44,7 +44,8 @@ enum show_feature_header {
> >
> > struct perf_tool {
> > event_sample sample,
> > - read;
> > + read,
> > + callchain_deferred;
> > event_op mmap,
> > mmap2,
> > comm,
> > --
> > 2.52.0.rc1.455.g30608eb744-goog
> >
^ permalink raw reply
* Re: [PATCH v4 5/5] perf tools: Merge deferred user callchains
From: Namhyung Kim @ 2025-11-20 1:07 UTC (permalink / raw)
To: Ian Rogers
Cc: Arnaldo Carvalho de Melo, James Clark, Jiri Olsa, Adrian Hunter,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users,
Steven Rostedt, Josh Poimboeuf, Indu Bhagat, Jens Remus,
Mathieu Desnoyers, linux-trace-kernel, bpf
In-Reply-To: <CAP-5=fVWVHzJ6uzfPpM37p6ZNbFC2D51nmdJzmnyKXy4COwB_Q@mail.gmail.com>
On Wed, Nov 19, 2025 at 08:54:26AM -0800, Ian Rogers wrote:
> On Sat, Nov 15, 2025 at 3:41 PM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > Save samples with deferred callchains in a separate list and deliver
> > them after merging the user callchains. If users don't want to merge
> > they can set tool->merge_deferred_callchains to false to prevent the
> > behavior.
> >
> > With previous result, now perf script will show the merged callchains.
> >
> > $ perf script
> > ...
> > pwd 2312 121.163435: 249113 cpu/cycles/P:
> > ffffffff845b78d8 __build_id_parse.isra.0+0x218 ([kernel.kallsyms])
> > ffffffff83bb5bf6 perf_event_mmap+0x2e6 ([kernel.kallsyms])
> > ffffffff83c31959 mprotect_fixup+0x1e9 ([kernel.kallsyms])
> > ffffffff83c31dc5 do_mprotect_pkey+0x2b5 ([kernel.kallsyms])
> > ffffffff83c3206f __x64_sys_mprotect+0x1f ([kernel.kallsyms])
> > ffffffff845e6692 do_syscall_64+0x62 ([kernel.kallsyms])
> > ffffffff8360012f entry_SYSCALL_64_after_hwframe+0x76 ([kernel.kallsyms])
> > 7f18fe337fa7 mprotect+0x7 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
> > 7f18fe330e0f _dl_sysdep_start+0x7f (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
> > 7f18fe331448 _dl_start_user+0x0 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
> > ...
> >
> > The old output can be get using --no-merge-callchain option.
> > Also perf report can get the user callchain entry at the end.
> >
> > $ perf report --no-children --stdio -q -S __build_id_parse.isra.0
> > # symbol: __build_id_parse.isra.0
> > 8.40% pwd [kernel.kallsyms]
> > |
> > ---__build_id_parse.isra.0
> > perf_event_mmap
> > mprotect_fixup
> > do_mprotect_pkey
> > __x64_sys_mprotect
> > do_syscall_64
> > entry_SYSCALL_64_after_hwframe
> > mprotect
> > _dl_sysdep_start
> > _dl_start_user
> >
> > Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> > ---
> > tools/perf/Documentation/perf-script.txt | 5 ++
> > tools/perf/builtin-inject.c | 1 +
> > tools/perf/builtin-report.c | 1 +
> > tools/perf/builtin-script.c | 4 ++
> > tools/perf/util/callchain.c | 29 ++++++++++
> > tools/perf/util/callchain.h | 3 ++
> > tools/perf/util/evlist.c | 1 +
> > tools/perf/util/evlist.h | 2 +
> > tools/perf/util/session.c | 67 +++++++++++++++++++++++-
> > tools/perf/util/tool.c | 1 +
> > tools/perf/util/tool.h | 1 +
> > 11 files changed, 114 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
> > index 28bec7e78bc858ba..03d1129606328d6d 100644
> > --- a/tools/perf/Documentation/perf-script.txt
> > +++ b/tools/perf/Documentation/perf-script.txt
> > @@ -527,6 +527,11 @@ include::itrace.txt[]
> > The known limitations include exception handing such as
> > setjmp/longjmp will have calls/returns not match.
> >
> > +--merge-callchains::
> > + Enable merging deferred user callchains if available. This is the
> > + default behavior. If you want to see separate CALLCHAIN_DEFERRED
> > + records for some reason, use --no-merge-callchains explicitly.
> > +
> > :GMEXAMPLECMD: script
> > :GMEXAMPLESUBCMD:
> > include::guest-files.txt[]
> > diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
> > index bd9245d2dd41aa48..51d2721b6db9dccb 100644
> > --- a/tools/perf/builtin-inject.c
> > +++ b/tools/perf/builtin-inject.c
> > @@ -2527,6 +2527,7 @@ int cmd_inject(int argc, const char **argv)
> > inject.tool.auxtrace = perf_event__repipe_auxtrace;
> > inject.tool.bpf_metadata = perf_event__repipe_op2_synth;
> > inject.tool.dont_split_sample_group = true;
> > + inject.tool.merge_deferred_callchains = false;
> > inject.session = __perf_session__new(&data, &inject.tool,
> > /*trace_event_repipe=*/inject.output.is_pipe,
> > /*host_env=*/NULL);
> > diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
> > index 2bc269f5fcef8023..add6b1c2aaf04270 100644
> > --- a/tools/perf/builtin-report.c
> > +++ b/tools/perf/builtin-report.c
> > @@ -1614,6 +1614,7 @@ int cmd_report(int argc, const char **argv)
> > report.tool.event_update = perf_event__process_event_update;
> > report.tool.feature = process_feature_event;
> > report.tool.ordering_requires_timestamps = true;
> > + report.tool.merge_deferred_callchains = !dump_trace;
> >
> > session = perf_session__new(&data, &report.tool);
> > if (IS_ERR(session)) {
> > diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
> > index 85b42205a71b3993..62e43d3c5ad731a0 100644
> > --- a/tools/perf/builtin-script.c
> > +++ b/tools/perf/builtin-script.c
> > @@ -4009,6 +4009,7 @@ int cmd_script(int argc, const char **argv)
> > bool header_only = false;
> > bool script_started = false;
> > bool unsorted_dump = false;
> > + bool merge_deferred_callchains = true;
> > char *rec_script_path = NULL;
> > char *rep_script_path = NULL;
> > struct perf_session *session;
> > @@ -4162,6 +4163,8 @@ int cmd_script(int argc, const char **argv)
> > "Guest code can be found in hypervisor process"),
> > OPT_BOOLEAN('\0', "stitch-lbr", &script.stitch_lbr,
> > "Enable LBR callgraph stitching approach"),
> > + OPT_BOOLEAN('\0', "merge-callchains", &merge_deferred_callchains,
> > + "Enable merge deferred user callchains"),
> > OPTS_EVSWITCH(&script.evswitch),
> > OPT_END()
> > };
> > @@ -4418,6 +4421,7 @@ int cmd_script(int argc, const char **argv)
> > script.tool.throttle = process_throttle_event;
> > script.tool.unthrottle = process_throttle_event;
> > script.tool.ordering_requires_timestamps = true;
> > + script.tool.merge_deferred_callchains = merge_deferred_callchains;
> > session = perf_session__new(&data, &script.tool);
> > if (IS_ERR(session))
> > return PTR_ERR(session);
> > diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
> > index 2884187ccbbecfdc..71dc5a070065dd2a 100644
> > --- a/tools/perf/util/callchain.c
> > +++ b/tools/perf/util/callchain.c
> > @@ -1838,3 +1838,32 @@ int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel,
> > }
> > return 0;
> > }
> > +
> > +int sample__merge_deferred_callchain(struct perf_sample *sample_orig,
> > + struct perf_sample *sample_callchain)
> > +{
> > + u64 nr_orig = sample_orig->callchain->nr - 1;
> > + u64 nr_deferred = sample_callchain->callchain->nr;
> > + struct ip_callchain *callchain;
> > +
> > + if (sample_orig->callchain->nr < 2) {
> > + sample_orig->deferred_callchain = false;
> > + return -EINVAL;
> > + }
> > +
> > + callchain = calloc(1 + nr_orig + nr_deferred, sizeof(u64));
> > + if (callchain == NULL) {
> > + sample_orig->deferred_callchain = false;
> > + return -ENOMEM;
> > + }
> > +
> > + callchain->nr = nr_orig + nr_deferred;
> > + /* copy original including PERF_CONTEXT_USER_DEFERRED (but the cookie) */
> > + memcpy(callchain->ips, sample_orig->callchain->ips, nr_orig * sizeof(u64));
> > + /* copy deferred user callchains */
> > + memcpy(&callchain->ips[nr_orig], sample_callchain->callchain->ips,
> > + nr_deferred * sizeof(u64));
> > +
> > + sample_orig->callchain = callchain;
> > + return 0;
> > +}
> > diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
> > index d5ae4fbb7ce5fa44..2a52af8c80ace33c 100644
> > --- a/tools/perf/util/callchain.h
> > +++ b/tools/perf/util/callchain.h
> > @@ -318,4 +318,7 @@ int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel,
> > struct perf_sample *sample, int max_stack,
> > bool symbols, callchain_iter_fn cb, void *data);
> >
> > +int sample__merge_deferred_callchain(struct perf_sample *sample_orig,
> > + struct perf_sample *sample_callchain);
> > +
> > #endif /* __PERF_CALLCHAIN_H */
> > diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
> > index e8217efdda5323c6..03674d2cbd015e4f 100644
> > --- a/tools/perf/util/evlist.c
> > +++ b/tools/perf/util/evlist.c
> > @@ -85,6 +85,7 @@ void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus,
> > evlist->ctl_fd.pos = -1;
> > evlist->nr_br_cntr = -1;
> > metricgroup__rblist_init(&evlist->metric_events);
> > + INIT_LIST_HEAD(&evlist->deferred_samples);
> > }
> >
> > struct evlist *evlist__new(void)
> > diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
> > index 5e71e3dc60423079..911834ae7c2a6f76 100644
> > --- a/tools/perf/util/evlist.h
> > +++ b/tools/perf/util/evlist.h
> > @@ -92,6 +92,8 @@ struct evlist {
> > * of struct metric_expr.
> > */
> > struct rblist metric_events;
> > + /* samples with deferred_callchain would wait here. */
> > + struct list_head deferred_samples;
> > };
> >
> > struct evsel_str_handler {
> > diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> > index 361e15c1f26a96d0..2e777fd1bcf6707b 100644
> > --- a/tools/perf/util/session.c
> > +++ b/tools/perf/util/session.c
> > @@ -1285,6 +1285,60 @@ static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool
> > per_thread);
> > }
> >
> > +struct deferred_event {
> > + struct list_head list;
> > + union perf_event *event;
>
> What's going on with the memory here? Doesn't the event need to be a
> copy to avoid the memory going away in between processing events? It
> is definitely worth commenting. Note, I don't see a copy being made
> for the event here in machines__deliver_event.
It doesn't allocate memory for the event. It rather points to the ring
buffer. But there are places we need to copy. Will update!
Thanks,
Namhyung
>
> > +};
> > +
> > +static int evlist__deliver_deferred_samples(struct evlist *evlist,
> > + const struct perf_tool *tool,
> > + union perf_event *event,
> > + struct perf_sample *sample,
> > + struct machine *machine)
> > +{
> > + struct deferred_event *de, *tmp;
> > + struct evsel *evsel;
> > + int ret = 0;
> > +
> > + if (!tool->merge_deferred_callchains) {
> > + evsel = evlist__id2evsel(evlist, sample->id);
> > + return tool->callchain_deferred(tool, event, sample,
> > + evsel, machine);
> > + }
> > +
> > + list_for_each_entry_safe(de, tmp, &evlist->deferred_samples, list) {
> > + struct perf_sample orig_sample;
> > +
> > + ret = evlist__parse_sample(evlist, de->event, &orig_sample);
> > + if (ret < 0) {
> > + pr_err("failed to parse original sample\n");
> > + break;
> > + }
> > +
> > + if (sample->tid != orig_sample.tid)
> > + continue;
> > +
> > + if (event->callchain_deferred.cookie == orig_sample.deferred_cookie)
> > + sample__merge_deferred_callchain(&orig_sample, sample);
> > + else
> > + orig_sample.deferred_callchain = false;
> > +
> > + evsel = evlist__id2evsel(evlist, orig_sample.id);
> > + ret = evlist__deliver_sample(evlist, tool, de->event,
> > + &orig_sample, evsel, machine);
> > +
> > + if (orig_sample.deferred_callchain)
> > + free(orig_sample.callchain);
> > +
> > + list_del(&de->list);
> > + free(de);
> > +
> > + if (ret)
> > + break;
> > + }
> > + return ret;
> > +}
> > +
> > static int machines__deliver_event(struct machines *machines,
> > struct evlist *evlist,
> > union perf_event *event,
> > @@ -1313,6 +1367,16 @@ static int machines__deliver_event(struct machines *machines,
> > return 0;
> > }
> > dump_sample(evsel, event, sample, perf_env__arch(machine->env));
> > + if (sample->deferred_callchain && tool->merge_deferred_callchains) {
> > + struct deferred_event *de = malloc(sizeof(*de));
> > +
> > + if (de == NULL)
> > + return -ENOMEM;
> > +
> > + de->event = event;
> > + list_add_tail(&de->list, &evlist->deferred_samples);
> > + return 0;
> > + }
> > return evlist__deliver_sample(evlist, tool, event, sample, evsel, machine);
> > case PERF_RECORD_MMAP:
> > return tool->mmap(tool, event, sample, machine);
> > @@ -1372,7 +1436,8 @@ static int machines__deliver_event(struct machines *machines,
> > return tool->aux_output_hw_id(tool, event, sample, machine);
> > case PERF_RECORD_CALLCHAIN_DEFERRED:
> > dump_deferred_callchain(evsel, event, sample);
> > - return tool->callchain_deferred(tool, event, sample, evsel, machine);
> > + return evlist__deliver_deferred_samples(evlist, tool, event,
> > + sample, machine);
> > default:
> > ++evlist->stats.nr_unknown_events;
> > return -1;
> > diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c
> > index f732d33e7f895ed4..c5d3b464b2a433b3 100644
> > --- a/tools/perf/util/tool.c
> > +++ b/tools/perf/util/tool.c
> > @@ -266,6 +266,7 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events)
> > tool->cgroup_events = false;
> > tool->no_warn = false;
> > tool->show_feat_hdr = SHOW_FEAT_NO_HEADER;
> > + tool->merge_deferred_callchains = true;
> >
> > tool->sample = process_event_sample_stub;
> > tool->mmap = process_event_stub;
> > diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
> > index 9b9f0a8cbf3de4b5..e96b69d25a5b737d 100644
> > --- a/tools/perf/util/tool.h
> > +++ b/tools/perf/util/tool.h
> > @@ -90,6 +90,7 @@ struct perf_tool {
> > bool cgroup_events;
> > bool no_warn;
> > bool dont_split_sample_group;
> > + bool merge_deferred_callchains;
> > enum show_feature_header show_feat_hdr;
> > };
> >
> > --
> > 2.52.0.rc1.455.g30608eb744-goog
> >
^ permalink raw reply
* Re: [PATCH] selftests: tracing: Add tprobe enable/disable testcase
From: Masami Hiramatsu @ 2025-11-20 1:25 UTC (permalink / raw)
To: Shuah Khan
Cc: Steven Rostedt, Masami Hiramatsu (Google), Shuah Khan,
Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest
In-Reply-To: <b61a339e-f80f-4ecd-861e-e9bb834d5101@linuxfoundation.org>
On Wed, 19 Nov 2025 15:56:57 -0700
Shuah Khan <skhan@linuxfoundation.org> wrote:
> On 11/19/25 15:06, Steven Rostedt wrote:
> > On Wed, 19 Nov 2025 14:44:22 -0700
> > Shuah Khan <skhan@linuxfoundation.org> wrote:
> >
> >> On 11/7/25 07:35, Masami Hiramatsu (Google) wrote:
> >>> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> >>>
> >>> Commit 2867495dea86 ("tracing: tprobe-events: Register tracepoint when
> >>> enable tprobe event") caused regression bug and tprobe did not work.
> >>> To prevent similar problems, add a testcase which enables/disables a
> >>> tprobe and check the results.
> >>>
> >>> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> >>
> >> Steve, do you want me to take this through my tree?
> >
> > Yes please. Masami's an official maintainer and mostly handles all things
> > "probe" related. This is his domain ;-)
> >
> > Thanks,
> >
> > -- Steve
> Applied to linux-kselftest next for Linux 6.19-rc1.
Thanks Shuah! This and other regression fixes is better to go
through selftests tree because those are checking existing
features. Maybe better to add [PATCH -selftests] or something
like that?
Thank you,
>
> thanks,
> -- Shuah
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [PATCH 00/44] Change a lot of min_t() that might mask high bits
From: Jakub Kicinski @ 2025-11-20 1:47 UTC (permalink / raw)
To: david.laight.linux
Cc: linux-kernel, Alan Stern, Alexander Viro, Alexei Starovoitov,
Andi Shyti, Andreas Dilger, Andrew Lunn, Andrew Morton,
Andrii Nakryiko, Andy Shevchenko, Ard Biesheuvel,
Arnaldo Carvalho de Melo, Bjorn Helgaas, Borislav Petkov,
Christian Brauner, Christian König, Christoph Hellwig,
Daniel Borkmann, Dan Williams, Dave Hansen, Dave Jiang,
David Ahern, David Hildenbrand, Davidlohr Bueso, David S. Miller,
Dennis Zhou, Eric Dumazet, Greg Kroah-Hartman, Herbert Xu,
Ingo Molnar, Jakub Sitnicki, James E.J. Bottomley,
Jarkko Sakkinen, Jason A. Donenfeld, Jens Axboe, Jiri Slaby,
Johannes Weiner, John Allen, Jonathan Cameron, Juergen Gross,
Kees Cook, KP Singh, Linus Walleij, Martin K. Petersen,
Matthew Wilcox (Oracle), Mika Westerberg, Mike Rapoport,
Miklos Szeredi, Namhyung Kim, Neal Cardwell, nic_swsd,
OGAWA Hirofumi, Olivia Mackall, Paolo Abeni, Paolo Bonzini,
Peter Huewe, Peter Zijlstra, Rafael J. Wysocki,
Sean Christopherson, Srinivas Kandagatla, Stefano Stabellini,
Steven Rostedt, Tejun Heo, Theodore Ts'o, Thomas Gleixner,
Tom Lendacky, Willem de Bruijn, x86, Yury Norov, amd-gfx, bpf,
cgroups, dri-devel, io-uring, kvm, linux-acpi, linux-block,
linux-crypto, linux-cxl, linux-efi, linux-ext4, linux-fsdevel,
linux-gpio, linux-i2c, linux-integrity, linux-mm, linux-nvme,
linux-pci, linux-perf-users, linux-scsi, linux-serial,
linux-trace-kernel, linux-usb, mptcp, netdev, usb-storage
In-Reply-To: <20251119224140.8616-1-david.laight.linux@gmail.com>
On Wed, 19 Nov 2025 22:40:56 +0000 david.laight.linux@gmail.com wrote:
> I've had to trim the 124 maintainers/lists that get_maintainer.pl finds
> from 124 to under 100 to be able to send the cover letter.
> The individual patches only go to the addresses found for the associated files.
> That reduces the number of emails to a less unsane number.
Please split the networking (9?) patches out to a separate series.
It will help you with the CC list, and help us to get this applied..
^ permalink raw reply
* Re: [PATCH bpf-next v3 0/6] bpf trampoline support "jmp" mode
From: Leon Hwang @ 2025-11-20 2:07 UTC (permalink / raw)
To: Xu Kuohai, Menglong Dong, Menglong Dong, Alexei Starovoitov
Cc: Alexei Starovoitov, Steven Rostedt, Daniel Borkmann,
John Fastabend, Andrii Nakryiko, Martin KaFai Lau, Eduard,
Song Liu, Yonghong Song, KP Singh, Stanislav Fomichev, Hao Luo,
Jiri Olsa, Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers,
jiang.biao, bpf, LKML, linux-trace-kernel
In-Reply-To: <5f4d0bf9-9c74-44ce-8f29-c43fa5e8810a@huaweicloud.com>
On 19/11/25 20:36, Xu Kuohai wrote:
> On 11/19/2025 10:55 AM, Leon Hwang wrote:
>>
>>
>> On 19/11/25 10:47, Menglong Dong wrote:
>>> On 2025/11/19 08:28, Alexei Starovoitov wrote:
>>>> On Tue, Nov 18, 2025 at 4:36 AM Menglong Dong
>>>> <menglong8.dong@gmail.com> wrote:
>>>>>
>>>>> As we can see above, the performance of fexit increase from
>>>>> 80.544M/s to
>>>>> 136.540M/s, and the "fmodret" increase from 78.301M/s to 159.248M/s.
>>>>
>>>> Nice! Now we're talking.
>>>>
>>>> I think arm64 CPUs have a similar RSB-like return address predictor.
>>>> Do we need to do something similar there?
>>>> The question is not targeted to you, Menglong,
>>>> just wondering.
>>>
>>> I did some research before, and I find that most arch
>>> have such RSB-like stuff. I'll have a look at the loongarch
>>> later(maybe after the LPC, as I'm forcing on the English practice),
>>> and Leon is following the arm64.
>>
>> Yep, happy to take this on.
>>
>> I'm reviewing the arm64 JIT code now and will experiment with possible
>> approaches to handle this as well.
>>
>
> Unfortunately, the arm64 trampoline uses a tricky approach to bypass BTI
> by using ret instruction to invoke the patched function. This conflicts
> with the current approach, and seems there is no straightforward solution.
>
Hi Kuohai,
Thanks for the explanation.
Do you recall the original reason for using a ret instruction to bypass
BTI in the arm64 trampoline? I'm trying to understand whether that
constraint is fundamental or historical.
I'm wondering if we could structure the control flow like this:
foo "bl" bar -> bar:
bar "br" trampoline -> trampoline:
trampoline "bl" -> bar func body:
bar func body "ret" -> trampoline
trampoline "ret" -> foo
This would introduce two "bl"s and two "ret"s, keeping the RAS balanced
in a way similar to the x86 approach.
With this structure, we could also shrink the frame layout:
* SP + retaddr_off [ self ip ]
* [ FP ]
And then store the "self" return address elsewhere on the stack.
Do you think something along these lines could work?
Thanks,
Leon
^ permalink raw reply
* [PATCHSET v5 0/6] perf tools: Add deferred callchain support
From: Namhyung Kim @ 2025-11-20 2:10 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ian Rogers, James Clark
Cc: Jiri Olsa, Adrian Hunter, Peter Zijlstra, Ingo Molnar, LKML,
linux-perf-users, Steven Rostedt, Josh Poimboeuf, Indu Bhagat,
Jens Remus, Mathieu Desnoyers, linux-trace-kernel, bpf
Hello,
This is a new version of deferred callchain support as the kernel part
is merged to the tip tree. Actually this is based on Steve's work (v16).
https://lore.kernel.org/r/20250908175319.841517121@kernel.org
v5 changes)
* update delegate tools (Ian)
* copy and flush remaining samples (Ian)
* add Ian's Reviewed-by tags
v4: https://lore.kernel.org/r/20251115234106.348571-1-namhyung@kernel.org
* add --call-graph fp,defer option (Ian, Steve)
* add more comment on the cookie (Ian)
* display cookie part in the deferred callchain (Ian)
v3: https://lore.kernel.org/r/20251114070018.160330-1-namhyung@kernel.org
* handle new attr.defer_output to generate deferred callchains
* fix crash when cookies don't match (Steven)
* disable merging for perf inject
* fix missing feature detection bug
* symbolize merged callchains properly
Here's an example session.
$ perf record --call-graph fp,defer pwd
/home/namhyung/project/linux
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.010 MB perf.data (29 samples) ]
$ perf evlist -v
cpu/cycles/P: type: 0 (PERF_TYPE_HARDWARE), size: 136, config: 0 (PERF_COUNT_HW_CPU_CYCLES),
{ sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|CALLCHAIN|PERIOD,
read_format: ID|LOST, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1,
task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1, build_id: 1,
defer_callchain: 1, defer_output: 1
$ perf script
...
pwd 2312 121.163435: 249113 cpu/cycles/P:
ffffffff845b78d8 __build_id_parse.isra.0+0x218 ([kernel.kallsyms])
ffffffff83bb5bf6 perf_event_mmap+0x2e6 ([kernel.kallsyms])
ffffffff83c31959 mprotect_fixup+0x1e9 ([kernel.kallsyms])
ffffffff83c31dc5 do_mprotect_pkey+0x2b5 ([kernel.kallsyms])
ffffffff83c3206f __x64_sys_mprotect+0x1f ([kernel.kallsyms])
ffffffff845e6692 do_syscall_64+0x62 ([kernel.kallsyms])
ffffffff8360012f entry_SYSCALL_64_after_hwframe+0x76 ([kernel.kallsyms])
7f18fe337fa7 mprotect+0x7 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
7f18fe330e0f _dl_sysdep_start+0x7f (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
7f18fe331448 _dl_start_user+0x0 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
...
$ perf script --no-merge-callchains
...
pwd 2312 121.163435: 249113 cpu/cycles/P:
ffffffff845b78d8 __build_id_parse.isra.0+0x218 ([kernel.kallsyms])
ffffffff83bb5bf6 perf_event_mmap+0x2e6 ([kernel.kallsyms])
ffffffff83c31959 mprotect_fixup+0x1e9 ([kernel.kallsyms])
ffffffff83c31dc5 do_mprotect_pkey+0x2b5 ([kernel.kallsyms])
ffffffff83c3206f __x64_sys_mprotect+0x1f ([kernel.kallsyms])
ffffffff845e6692 do_syscall_64+0x62 ([kernel.kallsyms])
ffffffff8360012f entry_SYSCALL_64_after_hwframe+0x76 ([kernel.kallsyms])
b00000006 (cookie) ([unknown])
pwd 2312 121.163447: DEFERRED CALLCHAIN [cookie: b00000006]
7f18fe337fa7 mprotect+0x7 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
7f18fe330e0f _dl_sysdep_start+0x7f (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
7f18fe331448 _dl_start_user+0x0 (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
...
The code is available at 'perf/defer-callchain-v5' branch in
git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git
Thanks,
Namhyung
Namhyung Kim (6):
tools headers UAPI: Sync linux/perf_event.h for deferred callchains
perf tools: Minimal DEFERRED_CALLCHAIN support
perf record: Add --call-graph fp,defer option for deferred callchains
perf script: Display PERF_RECORD_CALLCHAIN_DEFERRED
perf tools: Merge deferred user callchains
perf tools: Flush remaining samples w/o deferred callchains
tools/include/uapi/linux/perf_event.h | 21 ++-
tools/lib/perf/include/perf/event.h | 13 ++
tools/perf/Documentation/perf-config.txt | 3 +
tools/perf/Documentation/perf-record.txt | 4 +
tools/perf/Documentation/perf-script.txt | 5 +
tools/perf/builtin-inject.c | 1 +
tools/perf/builtin-report.c | 1 +
tools/perf/builtin-script.c | 93 +++++++++++
tools/perf/util/callchain.c | 45 +++++-
tools/perf/util/callchain.h | 4 +
tools/perf/util/event.c | 1 +
tools/perf/util/evlist.c | 1 +
tools/perf/util/evlist.h | 2 +
tools/perf/util/evsel.c | 50 +++++-
tools/perf/util/evsel.h | 1 +
tools/perf/util/evsel_fprintf.c | 5 +-
tools/perf/util/machine.c | 1 +
tools/perf/util/perf_event_attr_fprintf.c | 2 +
tools/perf/util/sample.h | 2 +
tools/perf/util/session.c | 183 ++++++++++++++++++++++
tools/perf/util/tool.c | 5 +
tools/perf/util/tool.h | 4 +-
22 files changed, 439 insertions(+), 8 deletions(-)
--
2.52.0.rc1.455.g30608eb744-goog
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox