* [PATCH] tracing: Fix syscall events activation by ensuring refcount hits zero
From: Huiwen He @ 2026-02-24 2:35 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu
Cc: Mathieu Desnoyers, linux-trace-kernel, linux-kernel, Huiwen He
When multiple syscall events are specified in the kernel command line
(e.g., trace_event=syscalls:sys_enter_openat,syscalls:sys_enter_close),
they are often not captured after boot, even though they appear enabled
in the tracing/set_event file.
The issue stems from how syscall events are initialized. Syscall
tracepoints require the global reference count (sys_tracepoint_refcount)
to transition from 0 to 1 to trigger the registration of the syscall
work (TIF_SYSCALL_TRACEPOINT) for tasks, including the init process (pid 1).
The current implementation of early_enable_events() with disable_first=true
used an interleaved sequence of "Disable A -> Enable A -> Disable B -> Enable B".
If multiple syscalls are enabled, the refcount never drops to zero,
preventing the 0->1 transition that triggers actual registration.
Fix this by splitting early_enable_events() into two distinct phases:
1. Disable all events specified in the buffer.
2. Enable all events specified in the buffer.
This ensures the refcount hits zero before re-enabling, allowing syscall
events to be properly activated during early boot.
The code is also refactored to use a helper function to avoid logic
duplication between the disable and enable phases.
Fixes: ce1039bd3a89 ("tracing: Fix enabling of syscall events on the command line")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
---
kernel/trace/trace_events.c | 52 ++++++++++++++++++++++++++-----------
1 file changed, 37 insertions(+), 15 deletions(-)
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 137b4d9bb116..879ed8b0cc78 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -4514,26 +4514,22 @@ static __init int event_trace_memsetup(void)
return 0;
}
-__init void
-early_enable_events(struct trace_array *tr, char *buf, bool disable_first)
+/*
+ * Helper function to enable or disable a comma-separated list of events
+ * from the bootup buffer.
+ */
+static __init void __early_set_events(struct trace_array *tr, char *buf, bool enable)
{
char *token;
- int ret;
-
- while (true) {
- token = strsep(&buf, ",");
-
- if (!token)
- break;
+ while ((token = strsep(&buf, ","))) {
if (*token) {
- /* Restarting syscalls requires that we stop them first */
- if (disable_first)
+ if (enable) {
+ if (ftrace_set_clr_event(tr, token, 1))
+ pr_warn("Failed to enable trace event: %s\n", token);
+ } else {
ftrace_set_clr_event(tr, token, 0);
-
- ret = ftrace_set_clr_event(tr, token, 1);
- if (ret)
- pr_warn("Failed to enable trace event: %s\n", token);
+ }
}
/* Put back the comma to allow this to be called again */
@@ -4542,6 +4538,32 @@ early_enable_events(struct trace_array *tr, char *buf, bool disable_first)
}
}
+/**
+ * early_enable_events - enable events from the bootup buffer
+ * @tr: The trace array to enable the events in
+ * @buf: The buffer containing the comma separated list of events
+ * @disable_first: If true, disable all events in @buf before enabling them
+ *
+ * This function enables events from the bootup buffer. If @disable_first
+ * is true, it will first disable all events in the buffer before enabling
+ * them.
+ *
+ * For syscall events, which rely on a global refcount to register the
+ * SYSCALL_WORK_SYSCALL_TRACEPOINT flag (especially for pid 1), we must
+ * ensure the refcount hits zero before re-enabling them. A simple
+ * "disable then enable" per-event is not enough if multiple syscalls are
+ * used, as the refcount will stay above zero. Thus, we need a two-phase
+ * approach: disable all, then enable all.
+ */
+__init void
+early_enable_events(struct trace_array *tr, char *buf, bool disable_first)
+{
+ if (disable_first)
+ __early_set_events(tr, buf, false);
+
+ __early_set_events(tr, buf, true);
+}
+
static __init int event_trace_enable(void)
{
struct trace_array *tr = top_trace_array();
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v7 3/3] mm: vmscan: add PIDs to vmscan tracepoints
From: Masami Hiramatsu @ 2026-02-23 23:30 UTC (permalink / raw)
To: Thomas Ballasi
Cc: akpm, axelrasmussen, david, hannes, linux-mm, linux-trace-kernel,
lorenzo.stoakes, mhiramat, mhocko, rostedt, shakeel.butt, weixugc,
yuanchu, zhengqi.arch
In-Reply-To: <20260223171544.4750-4-tballasi@linux.microsoft.com>
Hi,
On Mon, 23 Feb 2026 09:15:44 -0800
Thomas Ballasi <tballasi@linux.microsoft.com> wrote:
> The changes aims at adding additionnal tracepoints variables to help
> debuggers attribute them to specific processes.
>
> The PID field uses in_task() to reliably detect when we're in process
> context and can safely access current->pid. When not in process
> context (such as in interrupt or in an asynchronous RCU context), the
> field is set to -1 as a sentinel value.
>
> Signed-off-by: Thomas Ballasi <tballasi@linux.microsoft.com>
> ---
> include/trace/events/vmscan.h | 34 +++++++++++++++++++++++++---------
> 1 file changed, 25 insertions(+), 9 deletions(-)
>
> diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
> index 1212f6a7c223e..15b31281f0955 100644
> --- a/include/trace/events/vmscan.h
> +++ b/include/trace/events/vmscan.h
> @@ -122,18 +122,22 @@ DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_begin_template,
> __field( unsigned long, gfp_flags )
> __field( u64, memcg_id )
> __field( int, order )
> + __field( int, pid )
> ),
>
> TP_fast_assign(
> __entry->gfp_flags = (__force unsigned long)gfp_flags;
> __entry->order = order;
> + __entry->pid = current->pid;
All entries saves current->pid in common_pid field. Can you use
this common field?
# cat events/vmscan/mm_vmscan_reclaim_pages/format
name: mm_vmscan_reclaim_pages
ID: 590
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1; ## <------------here
field:int nid; offset:8; size:4; signed:1;
field:unsigned long nr_scanned; offset:16; size:8; signed:0;
field:unsigned long nr_reclaimed; offset:24; size:8; signed:0;
field:unsigned long nr_dirty; offset:32; size:8; signed:0;
field:unsigned long nr_writeback; offset:40; size:8; signed:0;
field:unsigned long nr_congested; offset:48; size:8; signed:0;
field:unsigned long nr_immediate; offset:56; size:8; signed:0;
field:unsigned int nr_activate0; offset:64; size:4; signed:0;
field:unsigned int nr_activate1; offset:68; size:4; signed:0;
field:unsigned long nr_ref_keep; offset:72; size:8; signed:0;
field:unsigned long nr_unmap_fail; offset:80; size:8; signed:0;
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [PATCH 2/4] mm: convert zone lock users to wrappers
From: Shakeel Butt @ 2026-02-23 23:28 UTC (permalink / raw)
To: Dmitry Ilvokhin
Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Brendan Jackman,
Johannes Weiner, Zi Yan, Oscar Salvador, Qi Zheng, Axel Rasmussen,
Yuanchu Xie, Wei Xu, linux-kernel, linux-mm, linux-trace-kernel,
linux-cxl, kernel-team
In-Reply-To: <7d1ee95201a8870445556e61e47161f46ade8b3b.1770821420.git.d@ilvokhin.com>
On Wed, Feb 11, 2026 at 03:22:14PM +0000, Dmitry Ilvokhin wrote:
> Replace direct zone lock acquire/release operations with the
> newly introduced wrappers.
>
> The changes are purely mechanical substitutions. No functional change
> intended. Locking semantics and ordering remain unchanged.
>
> The compaction path is left unchanged for now and will be
> handled separately in the following patch due to additional
> non-trivial modifications.
>
> Signed-off-by: Dmitry Ilvokhin <d@ilvokhin.com>
Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
^ permalink raw reply
* Re: [PATCH 1/4] mm: introduce zone lock wrappers
From: Shakeel Butt @ 2026-02-23 22:36 UTC (permalink / raw)
To: Dmitry Ilvokhin
Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Brendan Jackman,
Johannes Weiner, Zi Yan, Oscar Salvador, Qi Zheng, Axel Rasmussen,
Yuanchu Xie, Wei Xu, linux-kernel, linux-mm, linux-trace-kernel,
linux-cxl, kernel-team
In-Reply-To: <3826dd6dc55a9c5721ec3de85f019764a6cf3222.1770821420.git.d@ilvokhin.com>
On Wed, Feb 11, 2026 at 03:22:13PM +0000, Dmitry Ilvokhin wrote:
> Add thin wrappers around zone lock acquire/release operations. This
> prepares the code for future tracepoint instrumentation without
> modifying individual call sites.
>
> Centralizing zone lock operations behind wrappers allows future
> instrumentation or debugging hooks to be added without touching
> all users.
>
> No functional change intended. The wrappers are introduced in
> preparation for subsequent patches and are not yet used.
>
> Signed-off-by: Dmitry Ilvokhin <d@ilvokhin.com>
> ---
> MAINTAINERS | 1 +
> include/linux/zone_lock.h | 38 ++++++++++++++++++++++++++++++++++++++
> 2 files changed, 39 insertions(+)
> create mode 100644 include/linux/zone_lock.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index b4088f7290be..680c9ae02d7e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -16498,6 +16498,7 @@ F: include/linux/pgtable.h
> F: include/linux/ptdump.h
> F: include/linux/vmpressure.h
> F: include/linux/vmstat.h
> +F: include/linux/zone_lock.h
> F: kernel/fork.c
> F: mm/Kconfig
> F: mm/debug.c
> diff --git a/include/linux/zone_lock.h b/include/linux/zone_lock.h
> new file mode 100644
> index 000000000000..c531e26280e6
> --- /dev/null
> +++ b/include/linux/zone_lock.h
> @@ -0,0 +1,38 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _LINUX_ZONE_LOCK_H
> +#define _LINUX_ZONE_LOCK_H
> +
> +#include <linux/mmzone.h>
> +#include <linux/spinlock.h>
> +
> +static inline void zone_lock_init(struct zone *zone)
> +{
> + spin_lock_init(&zone->lock);
> +}
> +
> +#define zone_lock_irqsave(zone, flags) \
> +do { \
> + spin_lock_irqsave(&(zone)->lock, flags); \
> +} while (0)
> +
> +#define zone_trylock_irqsave(zone, flags) \
> +({ \
> + spin_trylock_irqsave(&(zone)->lock, flags); \
> +})
Any reason you used macros for above two and inlined functions for remaining?
> +
> +static inline void zone_unlock_irqrestore(struct zone *zone, unsigned long flags)
> +{
> + spin_unlock_irqrestore(&zone->lock, flags);
> +}
> +
> +static inline void zone_lock_irq(struct zone *zone)
> +{
> + spin_lock_irq(&zone->lock);
> +}
> +
> +static inline void zone_unlock_irq(struct zone *zone)
> +{
> + spin_unlock_irq(&zone->lock);
> +}
> +
> +#endif /* _LINUX_ZONE_LOCK_H */
> --
> 2.47.3
>
^ permalink raw reply
* [RFC PATCH bpf-next 2/3] ftrace: Use kallsyms binary search for single-symbol lookup
From: Andrey Grodzovsky @ 2026-02-23 21:51 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, jolsa, rostedt, linux-trace-kernel,
linux-open-source
In-Reply-To: <20260223215113.924599-1-andrey.grodzovsky@crowdstrike.com>
When ftrace_lookup_symbols() is called with a single symbol (cnt == 1),
use kallsyms_lookup_name() for O(log N) binary search instead of the
full linear scan via kallsyms_on_each_symbol().
ftrace_lookup_symbols() was designed for batch resolution of many
symbols in a single pass. For large cnt this is efficient: a single
O(N) walk over all symbols with O(log cnt) binary search into the
sorted input array. But for cnt == 1 it still decompresses all ~200K
kernel symbols only to match one.
kallsyms_lookup_name() uses the sorted kallsyms index and needs only
~17 decompressions for a single lookup.
This is the common path for kprobe.session with exact function names,
where libbpf sends one symbol per BPF_LINK_CREATE syscall.
If binary lookup fails (duplicate symbol names where the first match
is not ftrace-instrumented, or module symbols), the function falls
through to the existing linear scan path.
Before (cnt=1, 50 kprobe.session programs):
Attach: 858 ms (kallsyms_expand_symbol 25% of CPU)
After:
Attach: 52 ms (16x faster)
Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@crowdstrike.com>
---
kernel/trace/ftrace.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 827fb9a0bf0d..bfd7670669c2 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -9263,6 +9263,19 @@ static int kallsyms_callback(void *data, const char *name, unsigned long addr)
* @addrs array, which needs to be big enough to store at least @cnt
* addresses.
*
+ * For a single symbol (cnt == 1), uses kallsyms_lookup_name() which
+ * performs an O(log N) binary search via the sorted kallsyms index.
+ * This avoids the full O(N) linear scan over all kernel symbols that
+ * the multi-symbol path requires.
+ *
+ * For multiple symbols, uses a single-pass linear scan via
+ * kallsyms_on_each_symbol() with binary search into the sorted input
+ * array. While individual lookups are O(log N), doing K lookups
+ * totals O(K * log N) which loses to a single sequential O(N) pass
+ * at scale due to cache-friendly memory access patterns of the linear
+ * walk. Empirical testing shows the linear scan is faster for batch
+ * lookups even well below 10K symbols.
+ *
* Returns: 0 if all provided symbols are found, -ESRCH otherwise.
*/
int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *addrs)
@@ -9270,6 +9283,21 @@ int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *a
struct kallsyms_data args;
int found_all;
+ /* Fast path: single symbol uses O(log N) binary search */
+ if (cnt == 1) {
+ addrs[0] = kallsyms_lookup_name(sorted_syms[0]);
+ if (addrs[0])
+ addrs[0] = ftrace_location(addrs[0]);
+ if (addrs[0])
+ return 0;
+ /*
+ * Binary lookup can fail for duplicate symbol names
+ * where the first match is not ftrace-instrumented,
+ * or for module symbols. Retry with linear scan.
+ */
+ }
+
+ /* Batch path: single-pass O(N) linear scan */
memset(addrs, 0, sizeof(*addrs) * cnt);
args.addrs = addrs;
args.syms = sorted_syms;
--
2.34.1
^ permalink raw reply related
* [RFC PATCH bpf-next 3/3] selftests/bpf: add tests for kprobe.session optimization
From: Andrey Grodzovsky @ 2026-02-23 21:51 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, jolsa, rostedt, linux-trace-kernel,
linux-open-source
In-Reply-To: <20260223215113.924599-1-andrey.grodzovsky@crowdstrike.com>
Add two new subtests to kprobe_multi_test to validate the
kprobe.session exact function name optimization:
test_session_syms: Attaches a kprobe.session program to an exact
function name (bpf_fentry_test1) to verify the fast syms[] path
works correctly. Validates that both entry and return probes fire.
test_session_errors: Verifies error code consistency between the
wildcard pattern path (slow, parses kallsyms) and the exact function
name path (fast, uses syms[] array). Both paths must return -ENOENT
for non-existent functions, protecting against future changes that
could break API consistency.
Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@crowdstrike.com>
---
.../bpf/prog_tests/kprobe_multi_test.c | 76 +++++++++++++++++++
.../bpf/progs/kprobe_multi_session_errors.c | 27 +++++++
.../bpf/progs/kprobe_multi_session_syms.c | 45 +++++++++++
3 files changed, 148 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/kprobe_multi_session_errors.c
create mode 100644 tools/testing/selftests/bpf/progs/kprobe_multi_session_syms.c
diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c
index 9caef222e528..62f7959858a9 100644
--- a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c
@@ -8,6 +8,8 @@
#include "kprobe_multi_override.skel.h"
#include "kprobe_multi_session.skel.h"
#include "kprobe_multi_session_cookie.skel.h"
+#include "kprobe_multi_session_syms.skel.h"
+#include "kprobe_multi_session_errors.skel.h"
#include "kprobe_multi_verifier.skel.h"
#include "kprobe_write_ctx.skel.h"
#include "bpf/libbpf_internal.h"
@@ -400,6 +402,76 @@ static void test_session_cookie_skel_api(void)
kprobe_multi_session_cookie__destroy(skel);
}
+static void test_session_syms_skel_api(void)
+{
+ struct kprobe_multi_session_syms *skel = NULL;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+ int err, prog_fd;
+
+ skel = kprobe_multi_session_syms__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "kprobe_multi_session_syms__open_and_load"))
+ return;
+
+ skel->bss->pid = getpid();
+
+ err = kprobe_multi_session_syms__attach(skel);
+ if (!ASSERT_OK(err, "kprobe_multi_session_syms__attach"))
+ goto cleanup;
+
+ prog_fd = bpf_program__fd(skel->progs.trigger);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_run");
+ ASSERT_EQ(topts.retval, 0, "test_run");
+
+ /* Test 1: Both entry and return should fire */
+ ASSERT_EQ(skel->bss->test1_count, 2, "test1_count");
+ ASSERT_TRUE(skel->bss->test1_return, "test1_return");
+
+cleanup:
+ kprobe_multi_session_syms__destroy(skel);
+}
+
+static void test_session_errors(void)
+{
+ struct kprobe_multi_session_errors *skel = NULL;
+ struct bpf_link *link_wildcard = NULL;
+ struct bpf_link *link_exact = NULL;
+ int err_wildcard, err_exact;
+
+ skel = kprobe_multi_session_errors__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "kprobe_multi_session_errors__open_and_load"))
+ return;
+
+ /*
+ * Test error code consistency: both wildcard (slow path) and exact name
+ * (fast path) should return the same error code (ENOENT) for non-existent
+ * functions. This protects against future kernel changes that might alter
+ * error return values.
+ */
+
+ /* Try to attach with non-existent wildcard pattern (slow path) */
+ link_wildcard = bpf_program__attach(skel->progs.test_nonexistent_wildcard);
+ err_wildcard = -errno;
+ ASSERT_ERR_PTR(link_wildcard, "attach_nonexistent_wildcard");
+ ASSERT_EQ(err_wildcard, -ENOENT, "wildcard_error_enoent");
+
+ /* Try to attach with non-existent exact name (fast path) */
+ link_exact = bpf_program__attach(skel->progs.test_nonexistent_exact);
+ err_exact = -errno;
+ ASSERT_ERR_PTR(link_exact, "attach_nonexistent_exact");
+ ASSERT_EQ(err_exact, -ENOENT, "exact_error_enoent");
+
+ /*
+ * Verify both paths return identical error codes - this is critical for
+ * API consistency and prevents user code from breaking when switching
+ * between wildcard patterns and exact function names.
+ */
+ ASSERT_EQ(err_wildcard, err_exact, "error_consistency");
+
+ kprobe_multi_session_errors__destroy(skel);
+}
+
static void test_unique_match(void)
{
LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
@@ -645,6 +717,10 @@ void test_kprobe_multi_test(void)
test_session_skel_api();
if (test__start_subtest("session_cookie"))
test_session_cookie_skel_api();
+ if (test__start_subtest("session_syms"))
+ test_session_syms_skel_api();
+ if (test__start_subtest("session_errors"))
+ test_session_errors();
if (test__start_subtest("unique_match"))
test_unique_match();
if (test__start_subtest("attach_write_ctx"))
diff --git a/tools/testing/selftests/bpf/progs/kprobe_multi_session_errors.c b/tools/testing/selftests/bpf/progs/kprobe_multi_session_errors.c
new file mode 100644
index 000000000000..749d43b35bc2
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kprobe_multi_session_errors.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Test error code consistency between fast and slow paths for non-existent functions */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+/*
+ * Test 1: Non-existent wildcard pattern (slow path)
+ * This uses pattern matching with kallsyms parsing and should fail with ENOENT
+ */
+SEC("kprobe.session/__impossible_test_func_xyz_wildcard_*")
+int test_nonexistent_wildcard(struct pt_regs *ctx)
+{
+ return 0;
+}
+
+/*
+ * Test 2: Non-existent exact function name (fast path)
+ * This uses syms[] array and should fail with ENOENT (normalized from ESRCH)
+ */
+SEC("kprobe.session/__impossible_test_func_xyz_exact_123")
+int test_nonexistent_exact(struct pt_regs *ctx)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/kprobe_multi_session_syms.c b/tools/testing/selftests/bpf/progs/kprobe_multi_session_syms.c
new file mode 100644
index 000000000000..6a4bd57af1fc
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kprobe_multi_session_syms.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Test kprobe.session with exact function names to verify syms[] optimization */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <stdbool.h>
+
+char _license[] SEC("license") = "GPL";
+
+int pid = 0;
+
+/* Results for each function: incremented on entry and return */
+__u64 test1_count = 0;
+
+/* Track entry vs return */
+bool test1_return = false;
+
+/*
+ * No tests in here, just to trigger 'bpf_fentry_test*'
+ * through tracing test_run
+ */
+SEC("fentry/bpf_modify_return_test")
+int BPF_PROG(trigger)
+{
+ return 0;
+}
+
+/*
+ * Test 1: Exact function name (no wildcards) - uses fast syms[] path
+ * This should attach via opts.syms array, bypassing kallsyms parsing
+ */
+SEC("kprobe.session/bpf_fentry_test1")
+int test_kprobe_syms_1(struct pt_regs *ctx)
+{
+ if (bpf_get_current_pid_tgid() >> 32 != pid)
+ return 0;
+
+ test1_count++;
+
+ /* Check if this is return probe */
+ if (bpf_session_is_return(ctx))
+ test1_return = true;
+
+ return 0; /* Always execute return probe */
+}
--
2.34.1
^ permalink raw reply related
* [RFC PATCH bpf-next 1/3] libbpf: Optimize kprobe.session attachment for exact function names
From: Andrey Grodzovsky @ 2026-02-23 21:51 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, jolsa, rostedt, linux-trace-kernel,
linux-open-source
In-Reply-To: <20260223215113.924599-1-andrey.grodzovsky@crowdstrike.com>
Implement dual-path optimization in attach_kprobe_session():
- Fast path: Use syms[] array for exact function names
(no kallsyms parsing)
- Slow path: Use pattern matching with kallsyms only for
wildcards
This avoids expensive kallsyms file parsing (~150ms) when function names
are specified exactly, improving attachment time 50x (~3-5ms).
Error code normalization: The fast path returns ESRCH from kernel's
ftrace_lookup_symbols(), while slow path returns ENOENT from userspace
kallsyms parsing. Convert ESRCH to ENOENT in fast path to maintain API
consistency - both paths now return identical error codes for "symbol
not found".
Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@crowdstrike.com>
---
tools/lib/bpf/libbpf.c | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 0be7017800fe..87a71eab4308 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -12192,7 +12192,7 @@ static int attach_kprobe_session(const struct bpf_program *prog, long cookie,
{
LIBBPF_OPTS(bpf_kprobe_multi_opts, opts, .session = true);
const char *spec;
- char *pattern;
+ char *func_name;
int n;
*link = NULL;
@@ -12202,14 +12202,36 @@ static int attach_kprobe_session(const struct bpf_program *prog, long cookie,
return 0;
spec = prog->sec_name + sizeof("kprobe.session/") - 1;
- n = sscanf(spec, "%m[a-zA-Z0-9_.*?]", &pattern);
+ n = sscanf(spec, "%m[a-zA-Z0-9_.*?]", &func_name);
if (n < 1) {
- pr_warn("kprobe session pattern is invalid: %s\n", spec);
+ pr_warn("kprobe session function name is invalid: %s\n", spec);
return -EINVAL;
}
- *link = bpf_program__attach_kprobe_multi_opts(prog, pattern, &opts);
- free(pattern);
+ /* Check if pattern contains wildcards */
+ if (strpbrk(func_name, "*?")) {
+ /* Wildcard pattern - use pattern matching path with kallsyms parsing */
+ *link = bpf_program__attach_kprobe_multi_opts(prog, func_name, &opts);
+ } else {
+ /* Exact function name - use syms array path (fast, no kallsyms parsing) */
+ const char *syms[1];
+
+ syms[0] = func_name;
+ opts.syms = syms;
+ opts.cnt = 1;
+ *link = bpf_program__attach_kprobe_multi_opts(prog, NULL, &opts);
+ if (!*link && errno == ESRCH) {
+ /*
+ * Normalize error code for API consistency: fast path returns ESRCH
+ * from kernel's ftrace_lookup_symbols(), while slow path returns ENOENT
+ * from userspace kallsyms parsing. Convert ESRCH to ENOENT so both paths
+ * return the same error for "symbol not found".
+ */
+ errno = ENOENT;
+ }
+ }
+
+ free(func_name);
return *link ? 0 : -errno;
}
--
2.34.1
^ permalink raw reply related
* [RFC PATCH bpf-next 0/3] Optimize kprobe.session attachment for exact match
From: Andrey Grodzovsky @ 2026-02-23 21:51 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, jolsa, rostedt, linux-trace-kernel,
linux-open-source
When libbpf attaches kprobe.session programs with exact function names
(the common case: SEC("kprobe.session/vfs_read")), the current code path
has two independent performance bottlenecks:
1. Userspace (libbpf): attach_kprobe_session() always parses
/proc/kallsyms to resolve function names, even when the name is exact
(no wildcards).
2. Kernel (ftrace): ftrace_lookup_symbols() does a full O(N) linear scan
Worse case ~200K kernel symbols via kallsyms_on_each_symbol(), decompressing
every symbol name, even when resolving a single symbol (cnt == 1).
This series optimizes both layers:
Patch 1 adds a dual-path optimization to libbpf's attach_kprobe_session().
When the section name contains no wildcards (* or ?), it passes the
function name via opts.syms[] directly to the kernel, completely skipping
the /proc/kallsyms parse. When wildcards are present, it falls back to
the existing pattern matching path. Error codes are normalized (ESRCH →
ENOENT) so both paths present identical errors for "symbol not found".
Patch 2 adds a cnt == 1 fast path inside ftrace_lookup_symbols(). For a
single symbol, it uses kallsyms_lookup_name() which performs an O(log N)
binary search via the sorted kallsyms index, needing only ~17 symbol
decompressions instead of ~200K. If the binary lookup fails (duplicate
symbol names where the first match is not ftrace-instrumented, or module
symbols), it falls through to the existing linear scan.
The optimization is placed inside ftrace_lookup_symbols() rather than in
its callers because:
- It benefits all callers (bpf_kprobe_multi_link_attach,
register_fprobe_syms) without duplicating logic.
- The cnt == 1 binary search with fallback is purely an internal
optimization detail of ftrace_lookup_symbols()'s contract.
For batch lookups (cnt > 1), the existing single-pass O(N) linear scan
is retained. Empirical profiling with perf and bpftrace on both QEMU
and real hardware showed that the linear scan beats per-symbol
binary search for batch resolution at every measured scale (500, 10K,
41K symbols).
Patch 3 adds selftests covering the optimization: test_session_syms
validates that exact function name attachment works correctly through
the fast path, and test_session_errors verifies that both the wildcard
(slow) and exact (fast) paths return identical -ENOENT errors for
non-existent functions.
Example - (50 kprobe.session programs, each attaching to one exact
function name via separate BPF_LINK_CREATE syscall, 50 distinct
functions):
Configuration Attach Time
-----------------------------------------------+-----------
Before (unpatched libbpf + kernel) 7,488 ms
Patched libbpf only 858 ms
Both patches (libbpf + ftrace) 52 ms
Traditional kprobe pairs (100 progs, reference) 132 ms
Combined improvement: 144x faster. kprobe.session is now 2.5x faster
than the equivalent traditional kprobe entry+return pair.
Background: ftrace_lookup_symbols() was added by "ftrace: Add
ftrace_lookup_symbols function" to batch-resolve thousands of
wildcard-matched symbols in a single linear pass. At the time,
kallsyms_lookup_name() was also a linear scan, so the batch approach
was strictly better. "kallsyms: Improve the performance of
kallsyms_lookup_name()" later added a sorted index making
kallsyms_lookup_name() O(log N), but ftrace_lookup_symbols() was
never updated to take advantage of this for the single-symbol case.
Andrey Grodzovsky (3):
libbpf: Optimize kprobe.session attachment for exact function names
ftrace: Use kallsyms binary search for single-symbol lookup
selftests/bpf: add tests for kprobe.session optimization
kernel/trace/ftrace.c | 28 +++++++
tools/lib/bpf/libbpf.c | 32 ++++++--
.../bpf/prog_tests/kprobe_multi_test.c | 76 +++++++++++++++++++
.../bpf/progs/kprobe_multi_session_errors.c | 27 +++++++
.../bpf/progs/kprobe_multi_session_syms.c | 45 +++++++++++
5 files changed, 203 insertions(+), 5 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/kprobe_multi_session_errors.c
create mode 100644 tools/testing/selftests/bpf/progs/kprobe_multi_session_syms.c
--
2.34.1
^ permalink raw reply
* Re: [PATCH v1] Documentation/rtla: Add hwnoise to main page
From: Jonathan Corbet @ 2026-02-23 21:34 UTC (permalink / raw)
To: Costa Shulyupin, Steven Rostedt, Tomas Glozar, linux-trace-kernel,
linux-kernel, linux-doc
Cc: Costa Shulyupin
In-Reply-To: <20260215131249.33437-1-costa.shul@redhat.com>
Costa Shulyupin <costa.shul@redhat.com> writes:
> Add hwnoise to the command list and SEE ALSO section. The command list
> is ordered from low level to high level.
>
> Signed-off-by: Costa Shulyupin <costa.shul@redhat.com>
> ---
> Documentation/tools/rtla/rtla.rst | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
Applied, thanks.
jon
^ permalink raw reply
* Re: [PATCH v7 3/3] mm: vmscan: add PIDs to vmscan tracepoints
From: Shakeel Butt @ 2026-02-23 19:42 UTC (permalink / raw)
To: Thomas Ballasi
Cc: akpm, axelrasmussen, david, hannes, linux-mm, linux-trace-kernel,
lorenzo.stoakes, mhiramat, mhocko, rostedt, weixugc, yuanchu,
zhengqi.arch
In-Reply-To: <20260223171544.4750-4-tballasi@linux.microsoft.com>
On Mon, Feb 23, 2026 at 09:15:44AM -0800, Thomas Ballasi wrote:
> The changes aims at adding additionnal tracepoints variables to help
> debuggers attribute them to specific processes.
>
> The PID field uses in_task() to reliably detect when we're in process
Where is this in_task() check happening? Also this patch is changing
tracepoints for memory reclaim which never happens in any context other than
process context, so we don't need __event_in_irq() checks for these tracepoints.
^ permalink raw reply
* Re: [PATCH bpf-next 02/17] bpf: Use mutex lock pool for bpf trampolines
From: Alexei Starovoitov @ 2026-02-23 19:35 UTC (permalink / raw)
To: Jiri Olsa
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, bpf,
linux-trace-kernel, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, Menglong Dong, Steven Rostedt
In-Reply-To: <aZsT6_3tkli3SPsI@krava>
On Sun, Feb 22, 2026 at 6:34 AM Jiri Olsa <olsajiri@gmail.com> wrote:
>
> On Fri, Feb 20, 2026 at 11:58:13AM -0800, Alexei Starovoitov wrote:
> > On Fri, Feb 20, 2026 at 2:07 AM Jiri Olsa <jolsa@kernel.org> wrote:
> > >
> > > Adding mutex lock pool that replaces bpf trampolines mutex.
> > >
> > > For tracing_multi link coming in following changes we need to lock all
> > > the involved trampolines during the attachment. This could mean thousands
> > > of mutex locks, which is not convenient.
> > >
> > > As suggested by Andrii we can replace bpf trampolines mutex with mutex
> > > pool, where each trampoline is hash-ed to one of the locks from the pool.
> > >
> > > It's better to lock all the pool mutexes (64 at the moment) than
> > > thousands of them.
> > >
> > > Removing the mutex_is_locked in bpf_trampoline_put, because we removed
> > > the mutex from bpf_trampoline.
> > >
> > > Suggested-by: Andrii Nakryiko <andrii@kernel.org>
> > > Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> > > ---
> > > include/linux/bpf.h | 2 --
> > > kernel/bpf/trampoline.c | 74 +++++++++++++++++++++++++++++++----------
> > > 2 files changed, 56 insertions(+), 20 deletions(-)
> > >
> > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > > index cd9b96434904..46bf3d86bdb2 100644
> > > --- a/include/linux/bpf.h
> > > +++ b/include/linux/bpf.h
> > > @@ -1335,8 +1335,6 @@ struct bpf_trampoline {
> > > /* hlist for trampoline_ip_table */
> > > struct hlist_node hlist_ip;
> > > struct ftrace_ops *fops;
> > > - /* serializes access to fields of this trampoline */
> > > - struct mutex mutex;
> > > refcount_t refcnt;
> > > u32 flags;
> > > u64 key;
> > > diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> > > index 952cd7932461..05dc0358654d 100644
> > > --- a/kernel/bpf/trampoline.c
> > > +++ b/kernel/bpf/trampoline.c
> > > @@ -30,6 +30,45 @@ static struct hlist_head trampoline_ip_table[TRAMPOLINE_TABLE_SIZE];
> > > /* serializes access to trampoline tables */
> > > static DEFINE_MUTEX(trampoline_mutex);
> > >
> > > +#define TRAMPOLINE_LOCKS_BITS 6
> > > +#define TRAMPOLINE_LOCKS_TABLE_SIZE (1 << TRAMPOLINE_LOCKS_BITS)
> > > +
> > > +static struct {
> > > + struct mutex mutex;
> > > + struct lock_class_key key;
> > > +} *trampoline_locks;
> > > +
> > > +static struct mutex *trampoline_locks_lookup(struct bpf_trampoline *tr)
> >
> > select_trampoline_lock() ?
>
> ok
>
> >
> > > +{
> > > + return &trampoline_locks[hash_64((u64) tr, TRAMPOLINE_LOCKS_BITS)].mutex;
> > > +}
> > > +
> > > +static void trampoline_lock(struct bpf_trampoline *tr)
> > > +{
> > > + mutex_lock(trampoline_locks_lookup(tr));
> > > +}
> > > +
> > > +static void trampoline_unlock(struct bpf_trampoline *tr)
> > > +{
> > > + mutex_unlock(trampoline_locks_lookup(tr));
> > > +}
> > > +
> > > +static int __init trampoline_locks_init(void)
> > > +{
> > > + int i;
> > > +
> > > + trampoline_locks = kmalloc_array(TRAMPOLINE_LOCKS_TABLE_SIZE,
> > > + sizeof(trampoline_locks[0]), GFP_KERNEL);
> >
> > why bother with memory allocation? This is just 64 mutexes.
>
> ok, I could probably use __mutex_init directly for static key
>
> about 64.. not sure how I missed that but there's lockdep limit for
> maximum locks depth and it's 48.. so we'll need to use 32 locks,
> which is probably still ok
>
> >
> > > + if (!trampoline_locks)
> > > + return -ENOMEM;
> > > +
> > > + for (i = 0; i < TRAMPOLINE_LOCKS_TABLE_SIZE; i++) {
> > > + lockdep_register_key(&trampoline_locks[i].key);
> >
> > why special key?
>
> if we keep single key we will get lockdep 'recursive locking' warning
> during bpf_trampoline_multi_attach, because lockdep will think we lock
> the same mutex
>
> there's support to annotate nested locking with mutex_lock_nested but
> it allows maximum of 8 nested instances
yeah. subclass limit of 8 is there for a different use case.
I guess you never validated your earlier approach of "let's take
all trampoline mutexes" with lockdep ? ;)
MAX_LOCK_DEPTH is indeed 48.
See fs/configfs/inode.c and default_group_class.
It does:
lockdep_set_class(&inode->i_rwsem,
&default_group_class[depth - 1]);
the idea here is that the number of lockdep keys doesn't have
to be equal to the actual number of mutexes.
I guess we can keep a total of 32 mutexes to avoid making it too fancy.
Please add a comment explaining 32 and why it needs lockdep_key.
I thought declaring all mutexes as static will avoid the need for the key,
but DEFINE_MUTEX doesn't support an array.
So since we need a loop anyway to init mutex and the key,
let's keep kmalloc_array() above. Which is now renamed to kmalloc_objs()
after 7.0-rc1.
^ permalink raw reply
* Re: [PATCH v7 2/3] mm: vmscan: add cgroup IDs to vmscan tracepoints
From: Shakeel Butt @ 2026-02-23 19:34 UTC (permalink / raw)
To: Thomas Ballasi
Cc: akpm, axelrasmussen, david, hannes, linux-mm, linux-trace-kernel,
lorenzo.stoakes, mhiramat, mhocko, rostedt, weixugc, yuanchu,
zhengqi.arch
In-Reply-To: <20260223171544.4750-3-tballasi@linux.microsoft.com>
On Mon, Feb 23, 2026 at 09:15:43AM -0800, Thomas Ballasi wrote:
> Memory reclaim events are currently difficult to attribute to
> specific cgroups, making debugging memory pressure issues
> challenging. This patch adds memory cgroup ID (memcg_id) to key
> vmscan tracepoints to enable better correlation and analysis.
>
> For operations not associated with a specific cgroup, the field
> is defaulted to 0.
>
> Signed-off-by: Thomas Ballasi <tballasi@linux.microsoft.com>
Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
^ permalink raw reply
* Re: [PATCH v7 1/3] tracing: Add __event_in_*irq() helpers
From: Shakeel Butt @ 2026-02-23 19:33 UTC (permalink / raw)
To: Thomas Ballasi
Cc: akpm, axelrasmussen, david, hannes, linux-mm, linux-trace-kernel,
lorenzo.stoakes, mhiramat, mhocko, rostedt, weixugc, yuanchu,
zhengqi.arch
In-Reply-To: <20260223171544.4750-2-tballasi@linux.microsoft.com>
On Mon, Feb 23, 2026 at 09:15:42AM -0800, Thomas Ballasi wrote:
> From: Steven Rostedt <rostedt@goodmis.org>
>
> Some trace events want to expose in their output if they were triggered in
> an interrupt or softirq context. Instead of recording this in the event
> structure itself, as this information is stored in the flags portion of
> the event header, add helper macros that can be used in the print format:
>
> TP_printk("val=%d %s", __entry->val, __event_in_irq() ? "(in-irq)" : "")
>
> This will output "(in-irq)" for the event in the trace data if the event
> was triggered in hard or soft interrupt context.
>
> Link: https://lore.kernel.org/all/20251229132942.31a2b583@gandalf.local.home/
>
> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
> Signed-off-by: Thomas Ballasi <tballasi@linux.microsoft.com>
Reviewed-by: Shakeel Butt <shakeel.butt@linux.dev>
^ permalink raw reply
* Re: [PATCH v4 10/12] riscv: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS
From: Conor Dooley @ 2026-02-23 17:41 UTC (permalink / raw)
To: Puranjay Mohan
Cc: Conor Dooley, Andy Chiu, linux-riscv, alexghiti, palmer,
Björn Töpel, linux-kernel, linux-trace-kernel,
Alexandre Ghiti, Mark Rutland, paul.walmsley, greentime.hu,
nick.hu, nylon.chen, eric.lin, vicent.chen, zong.li,
yongxuan.wang, samuel.holland, olivia.chu, c2232430, arnd,
Sami Tolvanen, Kees Cook, Nathan Chancellor, llvm, pjw
In-Reply-To: <CANk7y0iokvWz==XQ67v3aztQ268WVmud22Y4gp-dPd4SrFhzMQ@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 413 bytes --]
On Mon, Feb 23, 2026 at 05:36:24PM +0000, Puranjay Mohan wrote:
> > Ye, this is what Nathan and I both did locally, give or take. I just
> > wasn't sure if this was actually correct to do or if it was just
> > papering over an issue with our CFI support. Do you want to send this as
> > a patch?
>
> Yes, I will send a patch with fixes tag.
Great, thanks! Add a cc: stable too, while you're at it ;)
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v4 10/12] riscv: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS
From: Puranjay Mohan @ 2026-02-23 17:36 UTC (permalink / raw)
To: Conor Dooley
Cc: Conor Dooley, Andy Chiu, linux-riscv, alexghiti, palmer,
Björn Töpel, linux-kernel, linux-trace-kernel,
Alexandre Ghiti, Mark Rutland, paul.walmsley, greentime.hu,
nick.hu, nylon.chen, eric.lin, vicent.chen, zong.li,
yongxuan.wang, samuel.holland, olivia.chu, c2232430, arnd,
Sami Tolvanen, Kees Cook, Nathan Chancellor, llvm, pjw
In-Reply-To: <20260223-garlic-sulphuric-0c1574608d09@spud>
On Mon, Feb 23, 2026 at 4:29 PM Conor Dooley <conor@kernel.org> wrote:
>
> On Mon, Feb 23, 2026 at 03:41:26PM +0000, Puranjay Mohan wrote:
> > On Mon, Feb 23, 2026 at 3:28 PM Conor Dooley <conor.dooley@microchip.com> wrote:
> > >
> > > On Mon, Feb 23, 2026 at 03:18:17PM +0000, Puranjay Mohan wrote:
> > > > On Sat, Feb 21, 2026 at 12:15 PM Conor Dooley <conor@kernel.org> wrote:
> > > > >
> > > > > Hey,
> > > > >
> > > > > On Tue, Apr 08, 2025 at 02:08:34AM +0800, Andy Chiu wrote:
> > > > > > From: Puranjay Mohan <puranjay12@gmail.com>
> > > > > >
> > > > > > This patch enables support for DYNAMIC_FTRACE_WITH_CALL_OPS on RISC-V.
> > > > > > This allows each ftrace callsite to provide an ftrace_ops to the common
> > > > > > ftrace trampoline, allowing each callsite to invoke distinct tracer
> > > > > > functions without the need to fall back to list processing or to
> > > > > > allocate custom trampolines for each callsite. This significantly speeds
> > > > > > up cases where multiple distinct trace functions are used and callsites
> > > > > > are mostly traced by a single tracer.
>
> > > > > >
> > > > > > Signed-off-by: Puranjay Mohan <puranjay12@gmail.com>
> > > > > >
> > > > > > [update kconfig, asm, refactor]
> > > > > >
> > > > > > Signed-off-by: Andy Chiu <andybnac@gmail.com>
> > > > > > Tested-by: Björn Töpel <bjorn@rivosinc.com>
> > > > >
> > > > > I bisected a boot failure to this commit [c217157bcd1df ("riscv:
> > > > > Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")] yesterday, that appears
> > > > > to be affecting all LLVM versions that I currently have installed. From
> > > > > some initial testing of Kconfig options, it looks like the issue is
> > > > > CFI_CLANG related because when I disable CFI_CLANG things work once
> > > > > more. Since this option depends on !CFI_CLANG, but is def_bool y, I
> > > > > modified Kconfig to force disable it at all times and tested
> > > > > !DYNAMIC_FTRACE_WITH_CALL_OPS && !CFG_CLANG, which did boot.
> > > > >
> > > > > I dunno anything about what's going on in this patch, but so little in
> > > > > it relates to having DYNAMIC_FTRACE_WITH_CALL_OPS, that I was able to
> > > > > figure out that the problem is -fpatchable-function-entry=8,4
> > > > >
> > > >
> > > > DYNAMIC_FTRACE_WITH_CALL_OPS can't work together with CFI_CLANG.
> > > >
> > > > arm64 has:
> > > >
> > > > select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS \
> > > > if (DYNAMIC_FTRACE_WITH_ARGS && !CFI && \
> > > > (CC_IS_CLANG || !CC_OPTIMIZE_FOR_SIZE))
> > > >
> > > > would need something similar for riscv if not already done.
> > >
> > >
> > > I think you've misunderstood my email. We already have:
> > >
> > > select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS if (DYNAMIC_FTRACE_WITH_ARGS && !CFI)
> > >
> > > The problem is that the patch broke using CFI_CLANG, due to the
> > > fpatchable-function-entry change.
> >
> >
> > Yeah, sorry I did not see the patch,
> > the original one I sent had:
> >
> > +ifeq ($(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS), y)
> > +ifeq ($(CONFIG_RISCV_ISA_C),y)
> > + CC_FLAGS_FTRACE := -fpatchable-function-entry=8,4
> > +else
> > + CC_FLAGS_FTRACE := -fpatchable-function-entry=4,2
> > +endif
> > +else
> >
> >
> > The basic Idea is that we can't put nops before the function entry
> > when using CFI_CLANG, because they both interfere with each other.
> >
> > the fix should be something like:
>
> Ye, this is what Nathan and I both did locally, give or take. I just
> wasn't sure if this was actually correct to do or if it was just
> papering over an issue with our CFI support. Do you want to send this as
> a patch?
Yes, I will send a patch with fixes tag.
Thanks,
Puranjay
^ permalink raw reply
* Re: [PATCH 0/4] mm: zone lock tracepoint instrumentation
From: Cheatham, Benjamin @ 2026-02-23 17:17 UTC (permalink / raw)
To: Dmitry Ilvokhin
Cc: linux-kernel, linux-mm, linux-trace-kernel, linux-cxl,
kernel-team, Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Brendan Jackman,
Johannes Weiner, Zi Yan, Oscar Salvador, Qi Zheng, Shakeel Butt,
Axel Rasmussen, Yuanchu Xie, Wei Xu
In-Reply-To: <aZyEctoThn0anlz8@shell.ilvokhin.com>
On 2/23/2026 10:46 AM, Dmitry Ilvokhin wrote:
> [You don't often get email from d@ilvokhin.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> On Fri, Feb 20, 2026 at 01:09:59PM -0600, Cheatham, Benjamin wrote:
>> On 2/11/2026 9:22 AM, Dmitry Ilvokhin wrote:
>>> Zone lock contention can significantly impact allocation and
>>> reclaim latency, as it is a central synchronization point in
>>> the page allocator and reclaim paths. Improved visibility into
>>> its behavior is therefore important for diagnosing performance
>>> issues in memory-intensive workloads.
>>>
>>> On some production workloads at Meta, we have observed noticeable
>>> zone lock contention. Deeper analysis of lock holders and waiters
>>> is currently difficult with existing instrumentation.
>>>
>>> While generic lock contention_begin/contention_end tracepoints
>>> cover the slow path, they do not provide sufficient visibility
>>> into lock hold times. In particular, the lack of a release-side
>>> event makes it difficult to identify long lock holders and
>>> correlate them with waiters. As a result, distinguishing between
>>> short bursts of contention and pathological long hold times
>>> requires additional instrumentation.
>>>
>>> This patch series adds dedicated tracepoint instrumentation to
>>> zone lock, following the existing mmap_lock tracing model.
>>>
>>> The goal is to enable detailed holder/waiter analysis and lock
>>> hold time measurements without affecting the fast path when
>>> tracing is disabled.
>>>
>>> The series is structured as follows:
>>>
>>> 1. Introduce zone lock wrappers.
>>> 2. Mechanically convert zone lock users to the wrappers.
>>> 3. Convert compaction to use the wrappers (requires minor
>>> restructuring of compact_lock_irqsave()).
>>> 4. Add zone lock tracepoints.
>>
>> I think you can improve the flow of this series if reorder as follows:
>> 1. Introduce zone lock wrappers
>> 4. Add zone lock tracepoints
>> 2. Mechanically convert zone lock users to the wrappers
>> 3. Convert compaction to use the wrappers...
>>
>> and possibly squash 1 & 4 (though that might be too big of a patch). It's better to introduce the
>> wrappers and their tracepoints together before the reviewer (i.e. me) forgets what was added in
>> patch 1 by the time they get to patch 4.
>
> Hi Ben,
>
> Thanks for the suggestion.
>
> I structured the series intentionally to keep all behavior-preserving
> refactoring separate from the actual instrumentation change.
>
> In particular, I had to split the conversion into two patches to
> separate the purely mechanical changes from the compaction
> restructuring. With the current order, tracepoints addition remains a
> single, atomic functional change on top of a fully converted tree. This
> keeps the instrumentation isolated from the refactoring and with an
> intention to make bisection and review of the behavioral change easier.
>
> Reordering as suggested would mix instrumentation with intermediate
> refactoring states, which I'd prefer to avoid.
>
> I hope this reasoning makes sense, but I'm happy to discuss if there are
> strong objections.
No that's fine, I figured as much. I just wasn't sure that was more important
to you than what (I thought) was a better reading order for the series.
Thanks,
Ben
>
>>
>> Thanks,
>> Ben
^ permalink raw reply
* [PATCH v7 3/3] mm: vmscan: add PIDs to vmscan tracepoints
From: Thomas Ballasi @ 2026-02-23 17:15 UTC (permalink / raw)
To: tballasi
Cc: akpm, axelrasmussen, david, hannes, linux-mm, linux-trace-kernel,
lorenzo.stoakes, mhiramat, mhocko, rostedt, shakeel.butt, weixugc,
yuanchu, zhengqi.arch
In-Reply-To: <20260223171544.4750-1-tballasi@linux.microsoft.com>
The changes aims at adding additionnal tracepoints variables to help
debuggers attribute them to specific processes.
The PID field uses in_task() to reliably detect when we're in process
context and can safely access current->pid. When not in process
context (such as in interrupt or in an asynchronous RCU context), the
field is set to -1 as a sentinel value.
Signed-off-by: Thomas Ballasi <tballasi@linux.microsoft.com>
---
include/trace/events/vmscan.h | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index 1212f6a7c223e..15b31281f0955 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -122,18 +122,22 @@ DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_begin_template,
__field( unsigned long, gfp_flags )
__field( u64, memcg_id )
__field( int, order )
+ __field( int, pid )
),
TP_fast_assign(
__entry->gfp_flags = (__force unsigned long)gfp_flags;
__entry->order = order;
+ __entry->pid = current->pid;
__entry->memcg_id = mem_cgroup_id(memcg);
),
- TP_printk("order=%d gfp_flags=%s memcg_id=%llu",
+ TP_printk("order=%d gfp_flags=%s pid=%d memcg_id=%llu %s",
__entry->order,
show_gfp_flags(__entry->gfp_flags),
- __entry->memcg_id)
+ __entry->pid,
+ __entry->memcg_id,
+ __event_in_irq() ? "(in-irq)" : "")
);
DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_direct_reclaim_begin,
@@ -168,16 +172,20 @@ DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_end_template,
TP_STRUCT__entry(
__field( unsigned long, nr_reclaimed )
__field( u64, memcg_id )
+ __field( int, pid )
),
TP_fast_assign(
__entry->nr_reclaimed = nr_reclaimed;
__entry->memcg_id = mem_cgroup_id(memcg);
+ __entry->pid = current->pid;
),
- TP_printk("nr_reclaimed=%lu memcg_id=%llu",
+ TP_printk("nr_reclaimed=%lu pid=%d memcg_id=%llu %s",
__entry->nr_reclaimed,
- __entry->memcg_id)
+ __entry->pid,
+ __entry->memcg_id,
+ __event_in_irq() ? "(in-irq)" : "")
);
DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_direct_reclaim_end,
@@ -220,9 +228,10 @@ TRACE_EVENT(mm_shrink_slab_start,
__field(unsigned long, cache_items)
__field(unsigned long long, delta)
__field(unsigned long, total_scan)
+ __field(u64, memcg_id)
__field(int, priority)
__field(int, nid)
- __field(u64, memcg_id)
+ __field(int, pid)
),
TP_fast_assign(
@@ -236,19 +245,22 @@ TRACE_EVENT(mm_shrink_slab_start,
__entry->priority = priority;
__entry->nid = sc->nid;
__entry->memcg_id = mem_cgroup_id(memcg);
+ __entry->pid = current->pid;
),
- TP_printk("%pS %p: nid: %d memcg_id: %llu objects to shrink %ld gfp_flags %s cache items %ld delta %lld total_scan %ld priority %d",
+ TP_printk("%pS %p: nid: %d pid: %d memcg_id: %llu objects to shrink %ld gfp_flags %s cache items %ld delta %lld total_scan %ld priority %d %s",
__entry->shrink,
__entry->shr,
__entry->nid,
+ __entry->pid,
__entry->memcg_id,
__entry->nr_objects_to_shrink,
show_gfp_flags(__entry->gfp_flags),
__entry->cache_items,
__entry->delta,
__entry->total_scan,
- __entry->priority)
+ __entry->priority,
+ __event_in_irq() ? "(in-irq)" : "")
);
TRACE_EVENT(mm_shrink_slab_end,
@@ -266,6 +278,7 @@ TRACE_EVENT(mm_shrink_slab_end,
__field(long, total_scan)
__field(int, nid)
__field(int, retval)
+ __field(int, pid)
__field(u64, memcg_id)
),
@@ -277,18 +290,21 @@ TRACE_EVENT(mm_shrink_slab_end,
__entry->total_scan = total_scan;
__entry->nid = nid;
__entry->retval = shrinker_retval;
+ __entry->pid = current->pid;
__entry->memcg_id = mem_cgroup_id(memcg);
),
- TP_printk("%pS %p: nid: %d memcg_id: %llu unused scan count %ld new scan count %ld total_scan %ld last shrinker return val %d",
+ TP_printk("%pS %p: nid: %d pid: %d memcg_id: %llu unused scan count %ld new scan count %ld total_scan %ld last shrinker return val %d %s",
__entry->shrink,
__entry->shr,
__entry->nid,
+ __entry->pid,
__entry->memcg_id,
__entry->unused_scan,
__entry->new_scan,
__entry->total_scan,
- __entry->retval)
+ __entry->retval,
+ __event_in_irq() ? "(in-irq)" : "")
);
TRACE_EVENT(mm_vmscan_lru_isolate,
--
2.33.8
^ permalink raw reply related
* [PATCH v7 2/3] mm: vmscan: add cgroup IDs to vmscan tracepoints
From: Thomas Ballasi @ 2026-02-23 17:15 UTC (permalink / raw)
To: tballasi
Cc: akpm, axelrasmussen, david, hannes, linux-mm, linux-trace-kernel,
lorenzo.stoakes, mhiramat, mhocko, rostedt, shakeel.butt, weixugc,
yuanchu, zhengqi.arch
In-Reply-To: <20260223171544.4750-1-tballasi@linux.microsoft.com>
Memory reclaim events are currently difficult to attribute to
specific cgroups, making debugging memory pressure issues
challenging. This patch adds memory cgroup ID (memcg_id) to key
vmscan tracepoints to enable better correlation and analysis.
For operations not associated with a specific cgroup, the field
is defaulted to 0.
Signed-off-by: Thomas Ballasi <tballasi@linux.microsoft.com>
---
include/trace/events/vmscan.h | 83 ++++++++++++++++++++---------------
mm/shrinker.c | 6 ++-
mm/vmscan.c | 17 +++----
3 files changed, 61 insertions(+), 45 deletions(-)
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index 490958fa10dee..1212f6a7c223e 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -114,85 +114,92 @@ TRACE_EVENT(mm_vmscan_wakeup_kswapd,
DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_begin_template,
- TP_PROTO(int order, gfp_t gfp_flags),
+ TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg),
- TP_ARGS(order, gfp_flags),
+ TP_ARGS(gfp_flags, order, memcg),
TP_STRUCT__entry(
- __field( int, order )
__field( unsigned long, gfp_flags )
+ __field( u64, memcg_id )
+ __field( int, order )
),
TP_fast_assign(
- __entry->order = order;
__entry->gfp_flags = (__force unsigned long)gfp_flags;
+ __entry->order = order;
+ __entry->memcg_id = mem_cgroup_id(memcg);
),
- TP_printk("order=%d gfp_flags=%s",
+ TP_printk("order=%d gfp_flags=%s memcg_id=%llu",
__entry->order,
- show_gfp_flags(__entry->gfp_flags))
+ show_gfp_flags(__entry->gfp_flags),
+ __entry->memcg_id)
);
DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_direct_reclaim_begin,
- TP_PROTO(int order, gfp_t gfp_flags),
+ TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg),
- TP_ARGS(order, gfp_flags)
+ TP_ARGS(gfp_flags, order, memcg)
);
#ifdef CONFIG_MEMCG
DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_memcg_reclaim_begin,
- TP_PROTO(int order, gfp_t gfp_flags),
+ TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg),
- TP_ARGS(order, gfp_flags)
+ TP_ARGS(gfp_flags, order, memcg)
);
DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_memcg_softlimit_reclaim_begin,
- TP_PROTO(int order, gfp_t gfp_flags),
+ TP_PROTO(gfp_t gfp_flags, int order, struct mem_cgroup *memcg),
- TP_ARGS(order, gfp_flags)
+ TP_ARGS(gfp_flags, order, memcg)
);
#endif /* CONFIG_MEMCG */
DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_end_template,
- TP_PROTO(unsigned long nr_reclaimed),
+ TP_PROTO(unsigned long nr_reclaimed, struct mem_cgroup *memcg),
- TP_ARGS(nr_reclaimed),
+ TP_ARGS(nr_reclaimed, memcg),
TP_STRUCT__entry(
__field( unsigned long, nr_reclaimed )
+ __field( u64, memcg_id )
),
TP_fast_assign(
__entry->nr_reclaimed = nr_reclaimed;
+ __entry->memcg_id = mem_cgroup_id(memcg);
),
- TP_printk("nr_reclaimed=%lu", __entry->nr_reclaimed)
+ TP_printk("nr_reclaimed=%lu memcg_id=%llu",
+ __entry->nr_reclaimed,
+ __entry->memcg_id)
);
DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_direct_reclaim_end,
- TP_PROTO(unsigned long nr_reclaimed),
+ TP_PROTO(unsigned long nr_reclaimed, struct mem_cgroup *memcg),
- TP_ARGS(nr_reclaimed)
+ TP_ARGS(nr_reclaimed, memcg)
);
#ifdef CONFIG_MEMCG
DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_memcg_reclaim_end,
- TP_PROTO(unsigned long nr_reclaimed),
+ TP_PROTO(unsigned long nr_reclaimed, struct mem_cgroup *memcg),
- TP_ARGS(nr_reclaimed)
+ TP_ARGS(nr_reclaimed, memcg)
);
DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_memcg_softlimit_reclaim_end,
- TP_PROTO(unsigned long nr_reclaimed),
+ TP_PROTO(unsigned long nr_reclaimed, struct mem_cgroup *memcg),
- TP_ARGS(nr_reclaimed)
+ TP_ARGS(nr_reclaimed, memcg)
);
#endif /* CONFIG_MEMCG */
@@ -200,39 +207,42 @@ TRACE_EVENT(mm_shrink_slab_start,
TP_PROTO(struct shrinker *shr, struct shrink_control *sc,
long nr_objects_to_shrink, unsigned long cache_items,
unsigned long long delta, unsigned long total_scan,
- int priority),
+ int priority, struct mem_cgroup *memcg),
TP_ARGS(shr, sc, nr_objects_to_shrink, cache_items, delta, total_scan,
- priority),
+ priority, memcg),
TP_STRUCT__entry(
__field(struct shrinker *, shr)
__field(void *, shrink)
- __field(int, nid)
__field(long, nr_objects_to_shrink)
__field(unsigned long, gfp_flags)
__field(unsigned long, cache_items)
__field(unsigned long long, delta)
__field(unsigned long, total_scan)
__field(int, priority)
+ __field(int, nid)
+ __field(u64, memcg_id)
),
TP_fast_assign(
__entry->shr = shr;
__entry->shrink = shr->scan_objects;
- __entry->nid = sc->nid;
__entry->nr_objects_to_shrink = nr_objects_to_shrink;
__entry->gfp_flags = (__force unsigned long)sc->gfp_mask;
__entry->cache_items = cache_items;
__entry->delta = delta;
__entry->total_scan = total_scan;
__entry->priority = priority;
+ __entry->nid = sc->nid;
+ __entry->memcg_id = mem_cgroup_id(memcg);
),
- TP_printk("%pS %p: nid: %d objects to shrink %ld gfp_flags %s cache items %ld delta %lld total_scan %ld priority %d",
+ TP_printk("%pS %p: nid: %d memcg_id: %llu objects to shrink %ld gfp_flags %s cache items %ld delta %lld total_scan %ld priority %d",
__entry->shrink,
__entry->shr,
__entry->nid,
+ __entry->memcg_id,
__entry->nr_objects_to_shrink,
show_gfp_flags(__entry->gfp_flags),
__entry->cache_items,
@@ -243,35 +253,38 @@ TRACE_EVENT(mm_shrink_slab_start,
TRACE_EVENT(mm_shrink_slab_end,
TP_PROTO(struct shrinker *shr, int nid, int shrinker_retval,
- long unused_scan_cnt, long new_scan_cnt, long total_scan),
+ long unused_scan_cnt, long new_scan_cnt, long total_scan, struct mem_cgroup *memcg),
TP_ARGS(shr, nid, shrinker_retval, unused_scan_cnt, new_scan_cnt,
- total_scan),
+ total_scan, memcg),
TP_STRUCT__entry(
__field(struct shrinker *, shr)
- __field(int, nid)
__field(void *, shrink)
__field(long, unused_scan)
__field(long, new_scan)
- __field(int, retval)
__field(long, total_scan)
+ __field(int, nid)
+ __field(int, retval)
+ __field(u64, memcg_id)
),
TP_fast_assign(
__entry->shr = shr;
- __entry->nid = nid;
__entry->shrink = shr->scan_objects;
__entry->unused_scan = unused_scan_cnt;
__entry->new_scan = new_scan_cnt;
- __entry->retval = shrinker_retval;
__entry->total_scan = total_scan;
+ __entry->nid = nid;
+ __entry->retval = shrinker_retval;
+ __entry->memcg_id = mem_cgroup_id(memcg);
),
- TP_printk("%pS %p: nid: %d unused scan count %ld new scan count %ld total_scan %ld last shrinker return val %d",
+ TP_printk("%pS %p: nid: %d memcg_id: %llu unused scan count %ld new scan count %ld total_scan %ld last shrinker return val %d",
__entry->shrink,
__entry->shr,
__entry->nid,
+ __entry->memcg_id,
__entry->unused_scan,
__entry->new_scan,
__entry->total_scan,
@@ -504,9 +517,9 @@ TRACE_EVENT(mm_vmscan_node_reclaim_begin,
DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_node_reclaim_end,
- TP_PROTO(unsigned long nr_reclaimed),
+ TP_PROTO(unsigned long nr_reclaimed, struct mem_cgroup *memcg),
- TP_ARGS(nr_reclaimed)
+ TP_ARGS(nr_reclaimed, memcg)
);
TRACE_EVENT(mm_vmscan_throttled,
diff --git a/mm/shrinker.c b/mm/shrinker.c
index 4a93fd433689a..ddf784f996a59 100644
--- a/mm/shrinker.c
+++ b/mm/shrinker.c
@@ -410,7 +410,8 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl,
total_scan = min(total_scan, (2 * freeable));
trace_mm_shrink_slab_start(shrinker, shrinkctl, nr,
- freeable, delta, total_scan, priority);
+ freeable, delta, total_scan, priority,
+ shrinkctl->memcg);
/*
* Normally, we should not scan less than batch_size objects in one
@@ -461,7 +462,8 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl,
*/
new_nr = add_nr_deferred(next_deferred, shrinker, shrinkctl);
- trace_mm_shrink_slab_end(shrinker, shrinkctl->nid, freed, nr, new_nr, total_scan);
+ trace_mm_shrink_slab_end(shrinker, shrinkctl->nid, freed, nr, new_nr, total_scan,
+ shrinkctl->memcg);
return freed;
}
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 614ccf39fe3fa..9d512fb354fcd 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -6603,11 +6603,11 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
return 1;
set_task_reclaim_state(current, &sc.reclaim_state);
- trace_mm_vmscan_direct_reclaim_begin(order, sc.gfp_mask);
+ trace_mm_vmscan_direct_reclaim_begin(sc.gfp_mask, order, 0);
nr_reclaimed = do_try_to_free_pages(zonelist, &sc);
- trace_mm_vmscan_direct_reclaim_end(nr_reclaimed);
+ trace_mm_vmscan_direct_reclaim_end(nr_reclaimed, 0);
set_task_reclaim_state(current, NULL);
return nr_reclaimed;
@@ -6636,8 +6636,9 @@ unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg,
sc.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
(GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK);
- trace_mm_vmscan_memcg_softlimit_reclaim_begin(sc.order,
- sc.gfp_mask);
+ trace_mm_vmscan_memcg_softlimit_reclaim_begin(sc.gfp_mask,
+ sc.order,
+ memcg);
/*
* NOTE: Although we can get the priority field, using it
@@ -6648,7 +6649,7 @@ unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg,
*/
shrink_lruvec(lruvec, &sc);
- trace_mm_vmscan_memcg_softlimit_reclaim_end(sc.nr_reclaimed);
+ trace_mm_vmscan_memcg_softlimit_reclaim_end(sc.nr_reclaimed, memcg);
*nr_scanned = sc.nr_scanned;
@@ -6684,13 +6685,13 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
struct zonelist *zonelist = node_zonelist(numa_node_id(), sc.gfp_mask);
set_task_reclaim_state(current, &sc.reclaim_state);
- trace_mm_vmscan_memcg_reclaim_begin(0, sc.gfp_mask);
+ trace_mm_vmscan_memcg_reclaim_begin(sc.gfp_mask, 0, memcg);
noreclaim_flag = memalloc_noreclaim_save();
nr_reclaimed = do_try_to_free_pages(zonelist, &sc);
memalloc_noreclaim_restore(noreclaim_flag);
- trace_mm_vmscan_memcg_reclaim_end(nr_reclaimed);
+ trace_mm_vmscan_memcg_reclaim_end(nr_reclaimed, memcg);
set_task_reclaim_state(current, NULL);
return nr_reclaimed;
@@ -7642,7 +7643,7 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask,
delayacct_freepages_end();
psi_memstall_leave(&pflags);
- trace_mm_vmscan_node_reclaim_end(sc->nr_reclaimed);
+ trace_mm_vmscan_node_reclaim_end(sc->nr_reclaimed, 0);
return sc->nr_reclaimed;
}
--
2.33.8
^ permalink raw reply related
* [PATCH v7 1/3] tracing: Add __event_in_*irq() helpers
From: Thomas Ballasi @ 2026-02-23 17:15 UTC (permalink / raw)
To: tballasi
Cc: akpm, axelrasmussen, david, hannes, linux-mm, linux-trace-kernel,
lorenzo.stoakes, mhiramat, mhocko, rostedt, shakeel.butt, weixugc,
yuanchu, zhengqi.arch
In-Reply-To: <20260223171544.4750-1-tballasi@linux.microsoft.com>
From: Steven Rostedt <rostedt@goodmis.org>
Some trace events want to expose in their output if they were triggered in
an interrupt or softirq context. Instead of recording this in the event
structure itself, as this information is stored in the flags portion of
the event header, add helper macros that can be used in the print format:
TP_printk("val=%d %s", __entry->val, __event_in_irq() ? "(in-irq)" : "")
This will output "(in-irq)" for the event in the trace data if the event
was triggered in hard or soft interrupt context.
Link: https://lore.kernel.org/all/20251229132942.31a2b583@gandalf.local.home/
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: Thomas Ballasi <tballasi@linux.microsoft.com>
---
include/trace/stages/stage3_trace_output.h | 8 ++++++++
include/trace/stages/stage7_class_define.h | 19 +++++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/include/trace/stages/stage3_trace_output.h b/include/trace/stages/stage3_trace_output.h
index 1e7b0bef95f52..53a23988a3b8a 100644
--- a/include/trace/stages/stage3_trace_output.h
+++ b/include/trace/stages/stage3_trace_output.h
@@ -150,3 +150,11 @@
#undef __get_buf
#define __get_buf(len) trace_seq_acquire(p, (len))
+
+#undef __event_in_hardirq
+#undef __event_in_softirq
+#undef __event_in_irq
+
+#define __event_in_hardirq() (__entry->ent.flags & TRACE_FLAG_HARDIRQ)
+#define __event_in_softirq() (__entry->ent.flags & TRACE_FLAG_SOFTIRQ)
+#define __event_in_irq() (__entry->ent.flags & (TRACE_FLAG_HARDIRQ | TRACE_FLAG_SOFTIRQ))
diff --git a/include/trace/stages/stage7_class_define.h b/include/trace/stages/stage7_class_define.h
index fcd564a590f43..47008897a7956 100644
--- a/include/trace/stages/stage7_class_define.h
+++ b/include/trace/stages/stage7_class_define.h
@@ -26,6 +26,25 @@
#undef __print_hex_dump
#undef __get_buf
+#undef __event_in_hardirq
+#undef __event_in_softirq
+#undef __event_in_irq
+
+/*
+ * The TRACE_FLAG_* are enums. Instead of using TRACE_DEFINE_ENUM(),
+ * use their hardcoded values. These values are parsed by user space
+ * tooling elsewhere so they will never change.
+ *
+ * See "enum trace_flag_type" in linux/trace_events.h:
+ * TRACE_FLAG_HARDIRQ
+ * TRACE_FLAG_SOFTIRQ
+ */
+
+/* This is what is displayed in the format files */
+#define __event_in_hardirq() (REC->common_flags & 0x8)
+#define __event_in_softirq() (REC->common_flags & 0x10)
+#define __event_in_irq() (REC->common_flags & 0x18)
+
/*
* The below is not executed in the kernel. It is only what is
* displayed in the print format for userspace to parse.
--
2.33.8
^ permalink raw reply related
* [PATCH v7 0/3] mm: vmscan: add PID and cgroup ID to vmscan tracepoints
From: Thomas Ballasi @ 2026-02-23 17:15 UTC (permalink / raw)
To: tballasi
Cc: akpm, axelrasmussen, david, hannes, linux-mm, linux-trace-kernel,
lorenzo.stoakes, mhiramat, mhocko, rostedt, shakeel.butt, weixugc,
yuanchu, zhengqi.arch
In-Reply-To: <20260213181537.54350-1-tballasi@linux.microsoft.com>
Changes in v6:
- Edited __entry_in_irq() to __event_in_irq() in corresponding commit
message
- Restore an entry that was removed inadvertently
Link to v6:
https://lore.kernel.org/linux-trace-kernel/20260213181537.54350-1-tballasi@linux.microsoft.com/
Signed-off-by: Thomas Ballasi <tballasi@linux.microsoft.com>
Steven Rostedt (1):
tracing: Add __event_in_*irq() helpers
Thomas Ballasi (2):
mm: vmscan: add cgroup IDs to vmscan tracepoints
mm: vmscan: add PIDs to vmscan tracepoints
include/trace/events/vmscan.h | 103 +++++++++++++--------
include/trace/stages/stage3_trace_output.h | 8 ++
include/trace/stages/stage7_class_define.h | 19 ++++
mm/shrinker.c | 6 +-
mm/vmscan.c | 17 ++--
5 files changed, 106 insertions(+), 47 deletions(-)
--
2.33.8
^ permalink raw reply
* Re: [PATCH 0/4] mm: zone lock tracepoint instrumentation
From: Dmitry Ilvokhin @ 2026-02-23 16:46 UTC (permalink / raw)
To: Cheatham, Benjamin
Cc: linux-kernel, linux-mm, linux-trace-kernel, linux-cxl,
kernel-team, Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Brendan Jackman,
Johannes Weiner, Zi Yan, Oscar Salvador, Qi Zheng, Shakeel Butt,
Axel Rasmussen, Yuanchu Xie, Wei Xu
In-Reply-To: <06b2a2b6-d5c8-4522-8e22-10616f887846@amd.com>
On Fri, Feb 20, 2026 at 01:09:59PM -0600, Cheatham, Benjamin wrote:
> On 2/11/2026 9:22 AM, Dmitry Ilvokhin wrote:
> > Zone lock contention can significantly impact allocation and
> > reclaim latency, as it is a central synchronization point in
> > the page allocator and reclaim paths. Improved visibility into
> > its behavior is therefore important for diagnosing performance
> > issues in memory-intensive workloads.
> >
> > On some production workloads at Meta, we have observed noticeable
> > zone lock contention. Deeper analysis of lock holders and waiters
> > is currently difficult with existing instrumentation.
> >
> > While generic lock contention_begin/contention_end tracepoints
> > cover the slow path, they do not provide sufficient visibility
> > into lock hold times. In particular, the lack of a release-side
> > event makes it difficult to identify long lock holders and
> > correlate them with waiters. As a result, distinguishing between
> > short bursts of contention and pathological long hold times
> > requires additional instrumentation.
> >
> > This patch series adds dedicated tracepoint instrumentation to
> > zone lock, following the existing mmap_lock tracing model.
> >
> > The goal is to enable detailed holder/waiter analysis and lock
> > hold time measurements without affecting the fast path when
> > tracing is disabled.
> >
> > The series is structured as follows:
> >
> > 1. Introduce zone lock wrappers.
> > 2. Mechanically convert zone lock users to the wrappers.
> > 3. Convert compaction to use the wrappers (requires minor
> > restructuring of compact_lock_irqsave()).
> > 4. Add zone lock tracepoints.
>
> I think you can improve the flow of this series if reorder as follows:
> 1. Introduce zone lock wrappers
> 4. Add zone lock tracepoints
> 2. Mechanically convert zone lock users to the wrappers
> 3. Convert compaction to use the wrappers...
>
> and possibly squash 1 & 4 (though that might be too big of a patch). It's better to introduce the
> wrappers and their tracepoints together before the reviewer (i.e. me) forgets what was added in
> patch 1 by the time they get to patch 4.
Hi Ben,
Thanks for the suggestion.
I structured the series intentionally to keep all behavior-preserving
refactoring separate from the actual instrumentation change.
In particular, I had to split the conversion into two patches to
separate the purely mechanical changes from the compaction
restructuring. With the current order, tracepoints addition remains a
single, atomic functional change on top of a fully converted tree. This
keeps the instrumentation isolated from the refactoring and with an
intention to make bisection and review of the behavioral change easier.
Reordering as suggested would mix instrumentation with intermediate
refactoring states, which I'd prefer to avoid.
I hope this reasoning makes sense, but I'm happy to discuss if there are
strong objections.
>
> Thanks,
> Ben
^ permalink raw reply
* [PATCH v3 19/19] rv/rvgen: fix _fill_states() return type annotation
From: Wander Lairson Costa @ 2026-02-23 16:18 UTC (permalink / raw)
To: Steven Rostedt, Gabriele Monaco, Nam Cao, Wander Lairson Costa,
open list:RUNTIME VERIFICATION (RV), open list
In-Reply-To: <20260223162407.147003-1-wander@redhat.com>
The _fill_states() method returns a list of strings, but the type
annotation incorrectly specified str. Update the annotation to
list[str] to match the actual return value.
Signed-off-by: Wander Lairson Costa <wander@redhat.com>
Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
---
tools/verification/rvgen/rvgen/ltl2k.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/verification/rvgen/rvgen/ltl2k.py b/tools/verification/rvgen/rvgen/ltl2k.py
index b8ac584fe2504..81fd1f5ea5ea2 100644
--- a/tools/verification/rvgen/rvgen/ltl2k.py
+++ b/tools/verification/rvgen/rvgen/ltl2k.py
@@ -75,7 +75,7 @@ class ltl2k(generator.Monitor):
if not self.name:
self.name = Path(file_path).stem
- def _fill_states(self) -> str:
+ def _fill_states(self) -> list[str]:
buf = [
"enum ltl_buchi_state {",
]
--
2.53.0
^ permalink raw reply related
* [PATCH v3 18/19] rv/rvgen: fix unbound loop variable warning
From: Wander Lairson Costa @ 2026-02-23 16:18 UTC (permalink / raw)
To: Steven Rostedt, Gabriele Monaco, Nam Cao, Wander Lairson Costa,
open list:RUNTIME VERIFICATION (RV), open list
In-Reply-To: <20260223162407.147003-1-wander@redhat.com>
Pyright static analysis reports a "possibly unbound variable" warning
for the loop variable `i` in the `abbreviate_atoms` function. The
variable is accessed after the inner loop terminates to slice the atom
string. While the loop logic currently ensures execution, the analyzer
flags the reliance on the loop variable persisting outside its scope.
Refactor the prefix length calculation into a nested `find_share_length`
helper function. This encapsulates the search logic and uses explicit
return statements, ensuring the length value is strictly defined. This
satisfies the type checker and improves code readability without
altering the runtime behavior.
Signed-off-by: Wander Lairson Costa <wander@redhat.com>
Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
---
tools/verification/rvgen/rvgen/ltl2k.py | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/tools/verification/rvgen/rvgen/ltl2k.py b/tools/verification/rvgen/rvgen/ltl2k.py
index b6300c38154dc..b8ac584fe2504 100644
--- a/tools/verification/rvgen/rvgen/ltl2k.py
+++ b/tools/verification/rvgen/rvgen/ltl2k.py
@@ -44,13 +44,17 @@ def abbreviate_atoms(atoms: list[str]) -> list[str]:
skip = ["is", "by", "or", "and"]
return '_'.join([x[:2] for x in s.lower().split('_') if x not in skip])
- abbrs = []
- for atom in atoms:
+ def find_share_length(atom: str) -> int:
for i in range(len(atom), -1, -1):
if sum(a.startswith(atom[:i]) for a in atoms) > 1:
- break
- share = atom[:i]
- unique = atom[i:]
+ return i
+ return 0
+
+ abbrs = []
+ for atom in atoms:
+ share_len = find_share_length(atom)
+ share = atom[:share_len]
+ unique = atom[share_len:]
abbrs.append((shorten(share) + shorten(unique)))
return abbrs
--
2.53.0
^ permalink raw reply related
* [PATCH v3 17/19] rv/rvgen: enforce presence of initial state
From: Wander Lairson Costa @ 2026-02-23 16:18 UTC (permalink / raw)
To: Steven Rostedt, Gabriele Monaco, Nam Cao, Wander Lairson Costa,
open list:RUNTIME VERIFICATION (RV), open list
In-Reply-To: <20260223162407.147003-1-wander@redhat.com>
The __get_state_variables() method parses DOT files to identify the
automaton's initial state. If the input file lacks a node with the
required initialization prefix, the initial_state variable is referenced
before assignment, causing an UnboundLocalError or a generic error
during the state removal step.
Initialize the variable explicitly and validate that a start node was
found after parsing. Raise a descriptive AutomataError if the definition
is missing to improve debugging and ensure the automaton is valid.
Signed-off-by: Wander Lairson Costa <wander@redhat.com>
Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
---
tools/verification/rvgen/rvgen/automata.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/tools/verification/rvgen/rvgen/automata.py b/tools/verification/rvgen/rvgen/automata.py
index fd2a4a0c3f6eb..ac765c9b2dece 100644
--- a/tools/verification/rvgen/rvgen/automata.py
+++ b/tools/verification/rvgen/rvgen/automata.py
@@ -145,6 +145,7 @@ class Automata:
# wait for node declaration
states = []
final_states = []
+ initial_state = ""
has_final_states = False
cursor = self.__get_cursor_begin_states()
@@ -171,6 +172,9 @@ class Automata:
final_states.append(state)
has_final_states = True
+ if not initial_state:
+ raise AutomataError("The automaton doesn't have an initial state")
+
states = sorted(set(states))
states.remove(initial_state)
--
2.53.0
^ permalink raw reply related
* [PATCH v3 16/19] rv/rvgen: extract node marker string to class constant
From: Wander Lairson Costa @ 2026-02-23 16:17 UTC (permalink / raw)
To: Steven Rostedt, Gabriele Monaco, Nam Cao, Wander Lairson Costa,
open list:RUNTIME VERIFICATION (RV), open list
In-Reply-To: <20260223162407.147003-1-wander@redhat.com>
Add a node_marker class constant to the Automata class to replace the
hardcoded "{node" string literal used throughout the DOT file parsing
logic. This follows the existing pattern established by the init_marker
and invalid_state_str class constants in the same class.
The "{node" string is used as a marker to identify node declaration
lines in DOT files during state variable extraction and cursor
positioning. Extracting it to a named constant improves code
maintainability and makes the marker's purpose explicit.
Signed-off-by: Wander Lairson Costa <wander@redhat.com>
Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
---
tools/verification/rvgen/rvgen/automata.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/tools/verification/rvgen/rvgen/automata.py b/tools/verification/rvgen/rvgen/automata.py
index 6613a9d6159a9..fd2a4a0c3f6eb 100644
--- a/tools/verification/rvgen/rvgen/automata.py
+++ b/tools/verification/rvgen/rvgen/automata.py
@@ -44,6 +44,7 @@ class Automata:
invalid_state_str = "INVALID_STATE"
init_marker = "__init_"
+ node_marker = "{node"
# val can be numerical, uppercase (constant or macro), lowercase (parameter or function)
# only numerical values should have units
constraint_rule = re.compile(r"""
@@ -112,7 +113,7 @@ class Automata:
for cursor, line in enumerate(self.__dot_lines):
split_line = line.split()
- if len(split_line) and split_line[0] == "{node":
+ if len(split_line) and split_line[0] == self.node_marker:
return cursor
raise AutomataError("Could not find a beginning state")
@@ -127,9 +128,9 @@ class Automata:
continue
if state == 0:
- if line[0] == "{node":
+ if line[0] == self.node_marker:
state = 1
- elif line[0] != "{node":
+ elif line[0] != self.node_marker:
break
else:
raise AutomataError("Could not find beginning event")
@@ -151,7 +152,7 @@ class Automata:
# process nodes
for line in islice(self.__dot_lines, cursor, None):
split_line = line.split()
- if not split_line or split_line[0] != "{node":
+ if not split_line or split_line[0] != self.node_marker:
break
raw_state = split_line[-1]
--
2.53.0
^ permalink raw reply related
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