* [PATCH v2 1/5] tracing: add ref_trace_final_put tracepoint
2026-07-10 5:30 [PATCH v2 0/5] tracing: add ref_trace_final_put tracing Eugene Mavick via B4 Relay
@ 2026-07-10 5:30 ` Eugene Mavick via B4 Relay
2026-07-10 5:30 ` [PATCH v2 2/5] refcount: " Eugene Mavick via B4 Relay
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
From: Eugene Mavick <m@mavick.dev>
Add ref_trace_final_put tracepoint and related core infrastructure
ref_trace_final_put fires when a reference
count reaches zero and the object enters its final release path.
The tracepoint records three fields:
- caller: function that called the refcounting
function(refcount_sub_and_test, percpu_ref_put_many)
- fn: refcounting function(eg refcount_sub_and_test)
- obj: refcount object(struct percpu_ref, refcount_t)
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
v2:
-change trace_ref_final_put macro name to do_trace_ref_final_put to
avoid overlap with tracepoint names(trace_*)
-change, in above mentioned macro, from trace_ref_trace_final_put( to
trace_call__ref_trace_final_put( to avoid double check
-changes suggested by Steven Rostedt
-v1:https://lore.kernel.org/all/20260705-refcount-final-put-trace-v1-1-cdd0014626a9@mavick.dev/
---
include/linux/ref_trace.h | 26 +++++++++++++++++++++++
include/trace/events/ref_trace.h | 46 ++++++++++++++++++++++++++++++++++++++++
lib/Makefile | 2 ++
lib/ref_trace.c | 12 +++++++++++
4 files changed, 86 insertions(+)
diff --git a/include/linux/ref_trace.h b/include/linux/ref_trace.h
new file mode 100644
index 000000000000..6c6600938a47
--- /dev/null
+++ b/include/linux/ref_trace.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_REF_TRACE_H
+#define _LINUX_REF_TRACE_H
+
+#include <linux/tracepoint-defs.h>
+#include <linux/instruction_pointer.h>
+
+/* Declare the tracepoint so tracepoint_enabled() can be used */
+DECLARE_TRACEPOINT(ref_trace_final_put);
+
+#ifdef CONFIG_TRACEPOINTS
+/* Wrapper function implemented in lib/ref_trace.c */
+extern void do_ref_trace_final_put(unsigned long caller, const char *fn, const void *obj);
+
+#define do_trace_ref_final_put(obj) \
+ do { \
+ if (tracepoint_enabled(ref_trace_final_put)) \
+ do_ref_trace_final_put(_RET_IP_, __func__, obj); \
+ } while (0)
+
+#else /* !CONFIG_TRACEPOINTS */
+static inline void do_ref_trace_final_put(unsigned long caller, const char *fn, const void *obj) { }
+#define do_trace_ref_final_put(obj) do { } while (0)
+#endif
+
+#endif /* _LINUX_REF_TRACE_H */
diff --git a/include/trace/events/ref_trace.h b/include/trace/events/ref_trace.h
new file mode 100644
index 000000000000..e6037a325be2
--- /dev/null
+++ b/include/trace/events/ref_trace.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ref_trace
+
+#if !defined(_TRACE_REF_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_REF_TRACE_H
+
+#include <linux/tracepoint.h>
+
+/**
+ * ref_trace_final_put - trace when a reference count reaches zero
+ * @caller: function that called the refcounting
+ * function(refcount_sub_and_test, percpu_ref_put_many)
+ * @fn: refcounting function(eg refcount_sub_and_test)
+ * @obj: refcount object(struct percpu_ref, refcount_t)
+ *
+ * Tracepoint instrumentation can be added using the ref_trace_final_put
+ * macro defined in include/linux/ref_trace.h
+ * which uses _RET_IP_ and __func__ for caller and fn arguments respectively,
+ * thus only requiring obj arg to be supplied
+ */
+TRACE_EVENT(ref_trace_final_put,
+
+ TP_PROTO(unsigned long caller, const char *fn, const void *obj),
+
+ TP_ARGS(caller, fn, obj),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, caller)
+ __string(fn, fn)
+ __field(const void *, obj)
+ ),
+
+ TP_fast_assign(
+ __entry->caller = caller;
+ __assign_str(fn);
+ __entry->obj = obj;
+ ),
+
+ TP_printk("caller=%pS fn=%s obj=%p", (void *)__entry->caller, __get_str(fn), __entry->obj)
+);
+
+#endif /* _TRACE_REF_TRACE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/lib/Makefile b/lib/Makefile
index f33a24bf1c19..41737090a95d 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -335,3 +335,5 @@ CONTEXT_ANALYSIS_test_context-analysis.o := y
obj-$(CONFIG_CONTEXT_ANALYSIS_TEST) += test_context-analysis.o
subdir-$(CONFIG_FORTIFY_SOURCE) += test_fortify
+
+obj-$(CONFIG_TRACEPOINTS) += ref_trace.o
diff --git a/lib/ref_trace.c b/lib/ref_trace.c
new file mode 100644
index 000000000000..9102dc7117db
--- /dev/null
+++ b/lib/ref_trace.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+#define CREATE_TRACE_POINTS
+#include <trace/events/ref_trace.h>
+
+//Wrapper function for functions defined entirely in header files
+void do_ref_trace_final_put(unsigned long caller, const char *fn, const void *obj)
+{
+ trace_call__ref_trace_final_put(caller, fn, obj);
+}
+EXPORT_SYMBOL_GPL(do_ref_trace_final_put);
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(ref_trace_final_put);
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v2 2/5] refcount: add ref_trace_final_put tracepoint
2026-07-10 5:30 [PATCH v2 0/5] tracing: add ref_trace_final_put tracing Eugene Mavick via B4 Relay
2026-07-10 5:30 ` [PATCH v2 1/5] tracing: add ref_trace_final_put tracepoint Eugene Mavick via B4 Relay
@ 2026-07-10 5:30 ` Eugene Mavick via B4 Relay
2026-07-10 5:30 ` [PATCH v2 3/5] percpu-refcount: add ref_trace_final_put trace Eugene Mavick via B4 Relay
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
From: Eugene Mavick <m@mavick.dev>
Add the ref_trace_final_put tracepoint to __refcount_sub_and_test() and
refcount_dec_if_one()
This tracepoint fires when a refcount_t reaches zero, capturing the caller
address, the function name, and the refcount_t address.
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
v2:
-update commit message
-add tracepoint to refcount_dec_if_one(), suggested by Usuma Arif
-move MAINTAINERS file entries to new MAINTAINERS commit
-v1:https://lore.kernel.org/all/20260705-refcount-final-put-trace-v1-2-cdd0014626a9@mavick.dev/
---
include/linux/refcount.h | 2 ++
lib/refcount.c | 8 +++++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/include/linux/refcount.h b/include/linux/refcount.h
index ba7657ced281..70d07a462da1 100644
--- a/include/linux/refcount.h
+++ b/include/linux/refcount.h
@@ -107,6 +107,7 @@
#include <linux/limits.h>
#include <linux/refcount_types.h>
#include <linux/spinlock_types.h>
+#include <linux/ref_trace.h>
struct mutex;
@@ -393,6 +394,7 @@ bool __refcount_sub_and_test(int i, refcount_t *r, int *oldp)
if (old > 0 && old == i) {
smp_acquire__after_ctrl_dep();
+ do_trace_ref_final_put(r);
return true;
}
diff --git a/lib/refcount.c b/lib/refcount.c
index a207a8f22b3c..cd7e32df3919 100644
--- a/lib/refcount.c
+++ b/lib/refcount.c
@@ -7,6 +7,7 @@
#include <linux/refcount.h>
#include <linux/spinlock.h>
#include <linux/bug.h>
+#include <linux/ref_trace.h>
#define REFCOUNT_WARN(str) WARN_ONCE(1, "refcount_t: " str ".\n")
@@ -56,7 +57,12 @@ bool refcount_dec_if_one(refcount_t *r)
{
int val = 1;
- return atomic_try_cmpxchg_release(&r->refs, &val, 0);
+ bool ret = atomic_try_cmpxchg_release(&r->refs, &val, 0);
+
+ if (ret)
+ do_trace_ref_final_put(r);
+
+ return ret;
}
EXPORT_SYMBOL(refcount_dec_if_one);
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v2 3/5] percpu-refcount: add ref_trace_final_put trace
2026-07-10 5:30 [PATCH v2 0/5] tracing: add ref_trace_final_put tracing Eugene Mavick via B4 Relay
2026-07-10 5:30 ` [PATCH v2 1/5] tracing: add ref_trace_final_put tracepoint Eugene Mavick via B4 Relay
2026-07-10 5:30 ` [PATCH v2 2/5] refcount: " Eugene Mavick via B4 Relay
@ 2026-07-10 5:30 ` Eugene Mavick via B4 Relay
2026-07-10 5:30 ` [PATCH v2 4/5] kunit: add test for ref_trace_final_put Eugene Mavick via B4 Relay
2026-07-10 5:30 ` [PATCH v2 5/5] MAINTAINERS: add entries " Eugene Mavick via B4 Relay
4 siblings, 0 replies; 6+ messages in thread
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
From: Eugene Mavick <m@mavick.dev>
Add the ref_trace_final_put tracepoint to percpu_ref_put_many().
The tracepoint fires when the atomic counter reaches zero in the
atomic fallback path (after percpu_ref_kill() has been called).
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
include/linux/percpu-refcount.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h
index d73a1c08c3e3..f870ac0e8c06 100644
--- a/include/linux/percpu-refcount.h
+++ b/include/linux/percpu-refcount.h
@@ -55,6 +55,7 @@
#include <linux/rcupdate.h>
#include <linux/types.h>
#include <linux/gfp.h>
+#include <linux/ref_trace.h>
struct percpu_ref;
typedef void (percpu_ref_func_t)(struct percpu_ref *);
@@ -331,8 +332,10 @@ static inline void percpu_ref_put_many(struct percpu_ref *ref, unsigned long nr)
if (__ref_is_percpu(ref, &percpu_count))
this_cpu_sub(*percpu_count, nr);
- else if (unlikely(atomic_long_sub_and_test(nr, &ref->data->count)))
+ else if (unlikely(atomic_long_sub_and_test(nr, &ref->data->count))) {
+ do_trace_ref_final_put(ref);
ref->data->release(ref);
+ }
rcu_read_unlock();
}
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v2 4/5] kunit: add test for ref_trace_final_put
2026-07-10 5:30 [PATCH v2 0/5] tracing: add ref_trace_final_put tracing Eugene Mavick via B4 Relay
` (2 preceding siblings ...)
2026-07-10 5:30 ` [PATCH v2 3/5] percpu-refcount: add ref_trace_final_put trace Eugene Mavick via B4 Relay
@ 2026-07-10 5:30 ` Eugene Mavick via B4 Relay
2026-07-10 5:30 ` [PATCH v2 5/5] MAINTAINERS: add entries " Eugene Mavick via B4 Relay
4 siblings, 0 replies; 6+ messages in thread
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
From: Eugene Mavick <m@mavick.dev>
Add a KUnit test suite for the ref_trace_final_put tracepoint.
The test registers a probe function and triggers both refcount_t and
percpu_ref final put paths, verifying that the tracepoint fires
correctly and that the recorded fields match expected values.
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
v2:
-fix v1 bug where test gave consistent false pass
-make probe minimal, and only check obj and store the values
-add test_init macro to register and reset count
-move value checking to new test_exit macro
-move MAINTAINERS file entries to new MAINTAINERS commit
-v1:https://lore.kernel.org/all/20260705-refcount-final-put-trace-v1-4-cdd0014626a9@mavick.dev/
---
lib/Kconfig | 10 ++++
lib/tests/Makefile | 1 +
lib/tests/ref_trace_kunit.c | 136 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 147 insertions(+)
diff --git a/lib/Kconfig b/lib/Kconfig
index 00a9509636c1..c29b2ccb3c31 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -52,6 +52,16 @@ config PACKING_KUNIT_TEST
When in doubt, say N.
+config REF_TRACE_KUNIT_TEST
+ bool "ref_trace kunit test" if !KUNIT_ALL_TESTS
+ depends on KUNIT && FTRACE
+ default KUNIT_ALL_TESTS
+ help
+ This option enables the KUnit test suite for the ref_trace_final_put
+ tracepoint.
+
+ If unsure, say N
+
config BITREVERSE
tristate
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index 7e9c2fa52e35..828a030ad8c7 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -57,5 +57,6 @@ obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o
obj-$(CONFIG_UTIL_MACROS_KUNIT) += util_macros_kunit.o
obj-$(CONFIG_RATELIMIT_KUNIT_TEST) += test_ratelimit.o
obj-$(CONFIG_UUID_KUNIT_TEST) += uuid_kunit.o
+obj-$(CONFIG_REF_TRACE_KUNIT_TEST) += ref_trace_kunit.o
obj-$(CONFIG_TEST_RUNTIME_MODULE) += module/
diff --git a/lib/tests/ref_trace_kunit.c b/lib/tests/ref_trace_kunit.c
new file mode 100644
index 000000000000..5c7e9d29a030
--- /dev/null
+++ b/lib/tests/ref_trace_kunit.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <kunit/test.h>
+#include <linux/compiler_attributes.h>
+#include <linux/wait_bit.h>
+#include <linux/instruction_pointer.h>
+#include <linux/kallsyms.h>
+#include <linux/percpu-refcount.h>
+#include <linux/refcount.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <trace/events/ref_trace.h>
+
+struct data {
+ unsigned long caller;
+ const char *fn;
+ const void *obj;
+ atomic_t count;
+};
+
+struct data capture;
+
+const void *chk_obj;
+
+#define test_init() \
+ do { \
+ KUNIT_EXPECT_FALSE( \
+ test, register_trace_ref_trace_final_put(probe, NULL)); \
+ \
+ atomic_set_release(&capture.count, 0); \
+ \
+ chk_obj = &obj; \
+ } while (0)
+
+
+#define test_exit(func_name) \
+ do { \
+ /* wait for probe completion */ \
+ wait_var_event( \
+ &capture.count, \
+ atomic_read_acquire(&capture.count) != 0); \
+ \
+ unregister_trace_ref_trace_final_put(probe, NULL); \
+ \
+ KUNIT_EXPECT_EQ(test, atomic_read_acquire(&capture.count), 1); \
+ /* \
+ * caller testing may be flaky \
+ * due to compile optimisations so omit \
+ */ \
+ KUNIT_EXPECT_STREQ(test, capture.fn, #func_name); \
+ KUNIT_EXPECT_PTR_EQ(test, capture.obj, &obj); \
+ } while (0)
+
+static void probe(
+ void *ignore,
+ unsigned long caller,
+ const char *fn,
+ const void *obj)
+{
+ //prevent non test func final_puts from changing captured values
+ if (chk_obj != obj)
+ return;
+
+ capture.caller = caller;
+ capture.fn = fn;
+ capture.obj = obj;
+
+ atomic_inc_return_release(&capture.count); //increase count
+}
+
+static noinline void test_refcount_sub_and_test(struct kunit *test)
+{
+ refcount_t obj;
+
+ test_init();
+ refcount_set(&obj, 2);
+
+ KUNIT_EXPECT_FALSE(test, refcount_dec_and_test(&obj));
+ KUNIT_EXPECT_TRUE(test, refcount_dec_and_test(&obj));
+
+ test_exit(__refcount_sub_and_test);
+}
+
+static __always_inline void test_refcount_dec_if_one(struct kunit *test)
+{
+ refcount_t obj;
+
+ test_init();
+ refcount_set(&obj, 2);
+
+ KUNIT_EXPECT_FALSE(test, refcount_dec_and_test(&obj));
+ KUNIT_EXPECT_TRUE(test, refcount_dec_if_one(&obj));
+
+ test_exit(refcount_dec_if_one);
+}
+static void dummy_release(struct percpu_ref *ref) {}
+
+static noinline void test_percpu_ref_put_many(struct kunit *test)
+{
+ struct percpu_ref obj;
+
+ test_init();
+
+ KUNIT_EXPECT_FALSE(test, percpu_ref_init(&obj, dummy_release, 0, GFP_KERNEL));
+
+ percpu_ref_get(&obj);
+ percpu_ref_get(&obj);
+
+ percpu_ref_put(&obj);
+ percpu_ref_put(&obj);
+
+ percpu_ref_switch_to_atomic_sync(&obj);
+
+ percpu_ref_put(&obj);
+
+ test_exit(percpu_ref_put_many);
+ percpu_ref_exit(&obj);
+}
+
+static struct kunit_case __refdata ref_trace_test_cases[] = {
+ KUNIT_CASE(test_refcount_sub_and_test),
+ KUNIT_CASE(test_refcount_dec_if_one),
+ KUNIT_CASE(test_percpu_ref_put_many),
+ {}
+};
+
+static struct kunit_suite ref_trace_test_suite = {
+ .name = "ref-trace",
+ .test_cases = ref_trace_test_cases
+};
+
+kunit_test_suites(&ref_trace_test_suite);
+
+MODULE_AUTHOR("Eugene Mavick <m@mavick.dev>");
+MODULE_DESCRIPTION("KUnit test for ref_trace");
+MODULE_LICENSE("GPL");
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v2 5/5] MAINTAINERS: add entries for ref_trace_final_put
2026-07-10 5:30 [PATCH v2 0/5] tracing: add ref_trace_final_put tracing Eugene Mavick via B4 Relay
` (3 preceding siblings ...)
2026-07-10 5:30 ` [PATCH v2 4/5] kunit: add test for ref_trace_final_put Eugene Mavick via B4 Relay
@ 2026-07-10 5:30 ` Eugene Mavick via B4 Relay
4 siblings, 0 replies; 6+ messages in thread
From: Eugene Mavick via B4 Relay @ 2026-07-10 5:30 UTC (permalink / raw)
To: Will Deacon, Peter Zijlstra, Boqun Feng, Mark Rutland, Gary Guo,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter
Cc: linux-kernel, linux-trace-kernel, linux-mm, Eugene Mavick
From: Eugene Mavick <m@mavick.dev>
Add new files added in the patch series to MAINTAINERS
Signed-off-by: Eugene Mavick <m@mavick.dev>
---
v2:
This patch was added in v2
---
MAINTAINERS | 3 +++
1 file changed, 3 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 10e8253181d3..2dbb7441906e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4210,7 +4210,10 @@ S: Maintained
F: Documentation/atomic_*.txt
F: arch/*/include/asm/atomic*.h
F: include/*/atomic*.h
+F: include/linux/ref_trace.h
F: include/linux/refcount.h
+F: lib/ref_trace.c
+F: lib/tests/ref_trace_kunit.c
F: scripts/atomic/
F: rust/kernel/sync/atomic.rs
F: rust/kernel/sync/atomic/
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread