AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/06 07:15 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a new tracepoint (ref_trace_final_put) to reference counting functions and a corresponding KUnit test. It does not alter the functional execution flow of the kernel.",
  "WorthFuzzing": false
}

1/1 2026/07/06 07:15 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 50c4a92fb63d4d32408e07a5b8de145db003c5d8\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Jul 6 07:15:13 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 10e8253181d321..7c387ed746ee51 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -4210,7 +4210,10 @@ S:\tMaintained\n F:\tDocumentation/atomic_*.txt\n F:\tarch/*/include/asm/atomic*.h\n F:\tinclude/*/atomic*.h\n+F:\tinclude/linux/ref_trace.h\n F:\tinclude/linux/refcount.h\n+F:\tlib/ref_trace.h\n+F:\tlib/tests/ref_trace_kunit.c\n F:\tscripts/atomic/\n F:\trust/kernel/sync/atomic.rs\n F:\trust/kernel/sync/atomic/\ndiff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h\nindex d73a1c08c3e3c0..55543ecdf60cfb 100644\n--- a/include/linux/percpu-refcount.h\n+++ b/include/linux/percpu-refcount.h\n@@ -55,6 +55,7 @@\n #include \u003clinux/rcupdate.h\u003e\n #include \u003clinux/types.h\u003e\n #include \u003clinux/gfp.h\u003e\n+#include \u003clinux/ref_trace.h\u003e\n \n struct percpu_ref;\n typedef void (percpu_ref_func_t)(struct percpu_ref *);\n@@ -331,8 +332,10 @@ static inline void percpu_ref_put_many(struct percpu_ref *ref, unsigned long nr)\n \n \tif (__ref_is_percpu(ref, \u0026percpu_count))\n \t\tthis_cpu_sub(*percpu_count, nr);\n-\telse if (unlikely(atomic_long_sub_and_test(nr, \u0026ref-\u003edata-\u003ecount)))\n+\telse if (unlikely(atomic_long_sub_and_test(nr, \u0026ref-\u003edata-\u003ecount))) {\n+\t\ttrace_ref_final_put(ref);\n \t\tref-\u003edata-\u003erelease(ref);\n+\t}\n \n \trcu_read_unlock();\n }\ndiff --git a/include/linux/ref_trace.h b/include/linux/ref_trace.h\nnew file mode 100644\nindex 00000000000000..54e9fba9e3e9f4\n--- /dev/null\n+++ b/include/linux/ref_trace.h\n@@ -0,0 +1,26 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#ifndef _LINUX_REF_TRACE_H\n+#define _LINUX_REF_TRACE_H\n+\n+#include \u003clinux/tracepoint-defs.h\u003e\n+#include \u003clinux/instruction_pointer.h\u003e\n+\n+/* Declare the tracepoint so tracepoint_enabled() can be used */\n+DECLARE_TRACEPOINT(ref_trace_final_put);\n+\n+#ifdef CONFIG_TRACEPOINTS\n+/* Wrapper function implemented in lib/ref_trace.c */\n+extern void do_ref_trace_final_put(unsigned long caller, const char *fn, const void *obj);\n+\n+#define trace_ref_final_put(obj)\t\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\t\\\n+\t\tif (tracepoint_enabled(ref_trace_final_put))\t\t\t\\\n+\t\t\tdo_ref_trace_final_put(_RET_IP_, __func__, obj);\t\\\n+\t} while (0)\n+\n+#else /* !CONFIG_TRACEPOINTS */\n+static inline void do_ref_trace_final_put(unsigned long caller, const char *fn, const void *obj) { }\n+#define trace_ref_final_put(obj) do { } while (0)\n+#endif\n+\n+#endif /* _LINUX_REF_TRACE_H */\ndiff --git a/include/linux/refcount.h b/include/linux/refcount.h\nindex ba7657ced2817f..7b1fbf326a21c6 100644\n--- a/include/linux/refcount.h\n+++ b/include/linux/refcount.h\n@@ -107,6 +107,7 @@\n #include \u003clinux/limits.h\u003e\n #include \u003clinux/refcount_types.h\u003e\n #include \u003clinux/spinlock_types.h\u003e\n+#include \u003clinux/ref_trace.h\u003e\n \n struct mutex;\n \n@@ -393,6 +394,7 @@ bool __refcount_sub_and_test(int i, refcount_t *r, int *oldp)\n \n \tif (old \u003e 0 \u0026\u0026 old == i) {\n \t\tsmp_acquire__after_ctrl_dep();\n+\t\ttrace_ref_final_put(r);\n \t\treturn true;\n \t}\n \ndiff --git a/include/trace/events/ref_trace.h b/include/trace/events/ref_trace.h\nnew file mode 100644\nindex 00000000000000..e6037a325be262\n--- /dev/null\n+++ b/include/trace/events/ref_trace.h\n@@ -0,0 +1,46 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#undef TRACE_SYSTEM\n+#define TRACE_SYSTEM ref_trace\n+\n+#if !defined(_TRACE_REF_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)\n+#define _TRACE_REF_TRACE_H\n+\n+#include \u003clinux/tracepoint.h\u003e\n+\n+/**\n+ * ref_trace_final_put - trace when a reference count reaches zero\n+ * @caller: function that called the refcounting\n+ * function(refcount_sub_and_test, percpu_ref_put_many)\n+ * @fn: refcounting function(eg refcount_sub_and_test)\n+ * @obj: refcount object(struct percpu_ref, refcount_t)\n+ *\n+ * Tracepoint instrumentation can be added using the ref_trace_final_put\n+ * macro defined in include/linux/ref_trace.h\n+ * which uses _RET_IP_ and __func__ for caller and fn arguments respectively,\n+ * thus only requiring obj arg to be supplied\n+ */\n+TRACE_EVENT(ref_trace_final_put,\n+\n+\tTP_PROTO(unsigned long caller, const char *fn, const void *obj),\n+\n+\tTP_ARGS(caller, fn, obj),\n+\n+\tTP_STRUCT__entry(\n+\t__field(unsigned long, caller)\n+\t__string(fn, fn)\n+\t__field(const void *, obj)\n+\t),\n+\n+\tTP_fast_assign(\n+\t__entry-\u003ecaller = caller;\n+\t__assign_str(fn);\n+\t__entry-\u003eobj = obj;\n+\t),\n+\n+\tTP_printk(\"caller=%pS fn=%s obj=%p\", (void *)__entry-\u003ecaller, __get_str(fn), __entry-\u003eobj)\n+);\n+\n+#endif /* _TRACE_REF_TRACE_H */\n+\n+/* This part must be outside protection */\n+#include \u003ctrace/define_trace.h\u003e\ndiff --git a/lib/Kconfig b/lib/Kconfig\nindex 00a9509636c187..c29b2ccb3c3117 100644\n--- a/lib/Kconfig\n+++ b/lib/Kconfig\n@@ -52,6 +52,16 @@ config PACKING_KUNIT_TEST\n \n \t  When in doubt, say N.\n \n+config REF_TRACE_KUNIT_TEST\n+\tbool \"ref_trace kunit test\" if !KUNIT_ALL_TESTS\n+\tdepends on KUNIT \u0026\u0026 FTRACE\n+\tdefault KUNIT_ALL_TESTS\n+\thelp\n+\t  This option enables the KUnit test suite for the ref_trace_final_put\n+\t  tracepoint.\n+\n+\t  If unsure, say N\n+\n config BITREVERSE\n \ttristate\n \ndiff --git a/lib/Makefile b/lib/Makefile\nindex f33a24bf1c19ad..41737090a95db9 100644\n--- a/lib/Makefile\n+++ b/lib/Makefile\n@@ -335,3 +335,5 @@ CONTEXT_ANALYSIS_test_context-analysis.o := y\n obj-$(CONFIG_CONTEXT_ANALYSIS_TEST) += test_context-analysis.o\n \n subdir-$(CONFIG_FORTIFY_SOURCE) += test_fortify\n+\n+obj-$(CONFIG_TRACEPOINTS) += ref_trace.o\ndiff --git a/lib/ref_trace.c b/lib/ref_trace.c\nnew file mode 100644\nindex 00000000000000..25f7c17c7c4758\n--- /dev/null\n+++ b/lib/ref_trace.c\n@@ -0,0 +1,12 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#define CREATE_TRACE_POINTS\n+#include \u003ctrace/events/ref_trace.h\u003e\n+\n+//Wrapper function for functions defined entirely in header files\n+void do_ref_trace_final_put(unsigned long caller, const char *fn, const void *obj)\n+{\n+\ttrace_ref_trace_final_put(caller, fn, obj);\n+}\n+EXPORT_SYMBOL_GPL(do_ref_trace_final_put);\n+\n+EXPORT_TRACEPOINT_SYMBOL_GPL(ref_trace_final_put);\ndiff --git a/lib/tests/Makefile b/lib/tests/Makefile\nindex 7e9c2fa52e35a4..828a030ad8c710 100644\n--- a/lib/tests/Makefile\n+++ b/lib/tests/Makefile\n@@ -57,5 +57,6 @@ obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o\n obj-$(CONFIG_UTIL_MACROS_KUNIT) += util_macros_kunit.o\n obj-$(CONFIG_RATELIMIT_KUNIT_TEST) += test_ratelimit.o\n obj-$(CONFIG_UUID_KUNIT_TEST) += uuid_kunit.o\n+obj-$(CONFIG_REF_TRACE_KUNIT_TEST) += ref_trace_kunit.o\n \n obj-$(CONFIG_TEST_RUNTIME_MODULE)\t\t+= module/\ndiff --git a/lib/tests/ref_trace_kunit.c b/lib/tests/ref_trace_kunit.c\nnew file mode 100644\nindex 00000000000000..2ece6c840fe0ad\n--- /dev/null\n+++ b/lib/tests/ref_trace_kunit.c\n@@ -0,0 +1,138 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#include \u003clinux/kernel.h\u003e\n+#include \u003ckunit/test.h\u003e\n+#include \u003clinux/wait_bit.h\u003e\n+#include \u003clinux/instruction_pointer.h\u003e\n+#include \u003clinux/kallsyms.h\u003e\n+#include \u003clinux/percpu-refcount.h\u003e\n+#include \u003clinux/refcount.h\u003e\n+#include \u003ctrace/events/ref_trace.h\u003e\n+\n+struct data {\n+\tunsigned long caller;\n+\tconst char *fn;\n+\tconst void *obj;\n+\tint *flag;\n+\tstruct kunit *test;\n+};\n+\n+struct data refc_chk;\n+struct data pcpu_chk;\n+\n+//called when tracepoint fires\n+static void probe(\n+\t  void *ignore,\n+\t  unsigned long caller,\n+\t  const char *fn,\n+\t  const void *obj)\n+{\n+\tstruct data *chk_val;\n+\n+\tchar func_name[KSYM_SYMBOL_LEN];\n+\n+\tsprint_symbol_no_offset(func_name, caller);\n+\n+\tif (!strcmp(func_name, \"test_refcount\")) {\n+\t\tchk_val = \u0026refc_chk;\n+\t\tKUNIT_EXPECT_FALSE(chk_val-\u003etest, memcmp(obj, chk_val-\u003eobj, sizeof(refcount_t)));\n+\t} else if (!strcmp(func_name, \"test_percpu\")) {\n+\t\tchk_val = \u0026pcpu_chk;\n+\t\tKUNIT_EXPECT_FALSE(chk_val-\u003etest, memcmp(\n+\t\t\tobj, chk_val-\u003eobj, sizeof(struct percpu_ref)));\n+\t} else {\n+\t\t//non test function origin trace events\n+\t\treturn;\n+\t}\n+\n+\tstruct kunit *test = chk_val-\u003etest;\n+\tint *flag = chk_val-\u003eflag;\n+\n+\t//ensure past flag writes are done before reading\n+\tKUNIT_EXPECT_EQ(test, 1, smp_load_acquire(flag));\n+\tKUNIT_EXPECT_EQ(test, caller, chk_val-\u003ecaller);\n+\n+\tsmp_store_release(flag, 0); //signal probe completion\n+}\n+\n+\n+\n+static void test_refcount(struct kunit *test)\n+{\n+\trefcount_t refc;\n+\tint flag_addr = 0;\n+\n+\trefc_chk.caller = (unsigned long)\u0026test_refcount;\n+\trefc_chk.fn = \"__refcount_sub_and_test\";\n+\trefc_chk.obj = \u0026refc;\n+\trefc_chk.flag = \u0026flag_addr;\n+\trefc_chk.test = test;\n+\n+\tint *flag = refc_chk.flag;\n+\n+\tKUNIT_EXPECT_FALSE(test, register_trace_ref_trace_final_put(probe, NULL));\n+\n+\trefcount_set(\u0026refc, 2);\n+\n+\tKUNIT_EXPECT_FALSE(test, refcount_dec_and_test(\u0026refc));\n+\n+\tsmp_store_release(flag, 1); //signal final put can happen\n+\n+\tKUNIT_EXPECT_TRUE(test, refcount_dec_and_test(\u0026refc));\n+\n+\twait_var_event(flag, smp_load_acquire(flag)); //wait for probe completion\n+\tunregister_trace_ref_trace_final_put(probe, NULL);\n+}\n+\n+static void dummy_release(struct percpu_ref *ref) {}\n+\n+static void test_percpu(struct kunit *test)\n+{\n+\tstruct percpu_ref pcpu_ref;\n+\n+\tint flag_addr = 1;\n+\n+\tpcpu_chk.caller = (unsigned long)\u0026test_percpu;\n+\tpcpu_chk.fn = \"percpu_ref_put_many\";\n+\tpcpu_chk.obj = \u0026pcpu_ref;\n+\tpcpu_chk.flag = \u0026flag_addr;\n+\tpcpu_chk.test = test;\n+\n+\tint *flag = pcpu_chk.flag;\n+\n+\tKUNIT_EXPECT_FALSE(test, register_trace_ref_trace_final_put(probe, NULL));\n+\n+\tKUNIT_EXPECT_FALSE(test, percpu_ref_init(\u0026pcpu_ref, dummy_release, 0, GFP_KERNEL));\n+\n+\tpercpu_ref_get(\u0026pcpu_ref);\n+\tpercpu_ref_get(\u0026pcpu_ref);\n+\n+\tpercpu_ref_put(\u0026pcpu_ref);\n+\tpercpu_ref_put(\u0026pcpu_ref);\n+\n+\tpercpu_ref_switch_to_atomic_sync(\u0026pcpu_ref);\n+\n+\tsmp_store_release(flag, 1); //signal final put can happen\n+\n+\tpercpu_ref_put(\u0026pcpu_ref);\n+\n+\twait_var_event(flag, smp_load_acquire(flag)); //wait for probe completion\n+\tunregister_trace_ref_trace_final_put(probe, NULL);\n+\tpercpu_ref_exit(\u0026pcpu_ref);\n+}\n+\n+static struct kunit_case __refdata ref_trace_test_cases[] = {\n+\tKUNIT_CASE(test_refcount),\n+\tKUNIT_CASE(test_percpu),\n+\t{}\n+};\n+\n+static struct kunit_suite ref_trace_test_suite = {\n+\t.name = \"ref-trace\",\n+\t.test_cases = ref_trace_test_cases,\n+};\n+\n+kunit_test_suites(\u0026ref_trace_test_suite);\n+\n+MODULE_AUTHOR(\"Eugene Mavick \u003cm@mavick.dev\u003e\");\n+MODULE_DESCRIPTION(\"KUnit test for ref_trace\");\n+MODULE_LICENSE(\"GPL\");\n"
}

2/1 2026/07/06 07:15 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a new tracepoint (ref_trace_final_put) to reference counting functions and a corresponding KUnit test. It does not alter the functional execution flow of the kernel.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 50c4a92fb63d4d32408e07a5b8de145db003c5d8
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Jul 6 07:15:13 2026 +0000

    syz-cluster: applied patch under review

diff --git a/MAINTAINERS b/MAINTAINERS
index 10e8253181d321..7c387ed746ee51 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.h
+F:	lib/tests/ref_trace_kunit.c
 F:	scripts/atomic/
 F:	rust/kernel/sync/atomic.rs
 F:	rust/kernel/sync/atomic/
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h
index d73a1c08c3e3c0..55543ecdf60cfb 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))) {
+		trace_ref_final_put(ref);
 		ref->data->release(ref);
+	}
 
 	rcu_read_unlock();
 }
diff --git a/include/linux/ref_trace.h b/include/linux/ref_trace.h
new file mode 100644
index 00000000000000..54e9fba9e3e9f4
--- /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 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 trace_ref_final_put(obj) do { } while (0)
+#endif
+
+#endif /* _LINUX_REF_TRACE_H */
diff --git a/include/linux/refcount.h b/include/linux/refcount.h
index ba7657ced2817f..7b1fbf326a21c6 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();
+		trace_ref_final_put(r);
 		return true;
 	}
 
diff --git a/include/trace/events/ref_trace.h b/include/trace/events/ref_trace.h
new file mode 100644
index 00000000000000..e6037a325be262
--- /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/Kconfig b/lib/Kconfig
index 00a9509636c187..c29b2ccb3c3117 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/Makefile b/lib/Makefile
index f33a24bf1c19ad..41737090a95db9 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 00000000000000..25f7c17c7c4758
--- /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_ref_trace_final_put(caller, fn, obj);
+}
+EXPORT_SYMBOL_GPL(do_ref_trace_final_put);
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(ref_trace_final_put);
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index 7e9c2fa52e35a4..828a030ad8c710 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 00000000000000..2ece6c840fe0ad
--- /dev/null
+++ b/lib/tests/ref_trace_kunit.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <kunit/test.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 <trace/events/ref_trace.h>
+
+struct data {
+	unsigned long caller;
+	const char *fn;
+	const void *obj;
+	int *flag;
+	struct kunit *test;
+};
+
+struct data refc_chk;
+struct data pcpu_chk;
+
+//called when tracepoint fires
+static void probe(
+	  void *ignore,
+	  unsigned long caller,
+	  const char *fn,
+	  const void *obj)
+{
+	struct data *chk_val;
+
+	char func_name[KSYM_SYMBOL_LEN];
+
+	sprint_symbol_no_offset(func_name, caller);
+
+	if (!strcmp(func_name, "test_refcount")) {
+		chk_val = &refc_chk;
+		KUNIT_EXPECT_FALSE(chk_val->test, memcmp(obj, chk_val->obj, sizeof(refcount_t)));
+	} else if (!strcmp(func_name, "test_percpu")) {
+		chk_val = &pcpu_chk;
+		KUNIT_EXPECT_FALSE(chk_val->test, memcmp(
+			obj, chk_val->obj, sizeof(struct percpu_ref)));
+	} else {
+		//non test function origin trace events
+		return;
+	}
+
+	struct kunit *test = chk_val->test;
+	int *flag = chk_val->flag;
+
+	//ensure past flag writes are done before reading
+	KUNIT_EXPECT_EQ(test, 1, smp_load_acquire(flag));
+	KUNIT_EXPECT_EQ(test, caller, chk_val->caller);
+
+	smp_store_release(flag, 0); //signal probe completion
+}
+
+
+
+static void test_refcount(struct kunit *test)
+{
+	refcount_t refc;
+	int flag_addr = 0;
+
+	refc_chk.caller = (unsigned long)&test_refcount;
+	refc_chk.fn = "__refcount_sub_and_test";
+	refc_chk.obj = &refc;
+	refc_chk.flag = &flag_addr;
+	refc_chk.test = test;
+
+	int *flag = refc_chk.flag;
+
+	KUNIT_EXPECT_FALSE(test, register_trace_ref_trace_final_put(probe, NULL));
+
+	refcount_set(&refc, 2);
+
+	KUNIT_EXPECT_FALSE(test, refcount_dec_and_test(&refc));
+
+	smp_store_release(flag, 1); //signal final put can happen
+
+	KUNIT_EXPECT_TRUE(test, refcount_dec_and_test(&refc));
+
+	wait_var_event(flag, smp_load_acquire(flag)); //wait for probe completion
+	unregister_trace_ref_trace_final_put(probe, NULL);
+}
+
+static void dummy_release(struct percpu_ref *ref) {}
+
+static void test_percpu(struct kunit *test)
+{
+	struct percpu_ref pcpu_ref;
+
+	int flag_addr = 1;
+
+	pcpu_chk.caller = (unsigned long)&test_percpu;
+	pcpu_chk.fn = "percpu_ref_put_many";
+	pcpu_chk.obj = &pcpu_ref;
+	pcpu_chk.flag = &flag_addr;
+	pcpu_chk.test = test;
+
+	int *flag = pcpu_chk.flag;
+
+	KUNIT_EXPECT_FALSE(test, register_trace_ref_trace_final_put(probe, NULL));
+
+	KUNIT_EXPECT_FALSE(test, percpu_ref_init(&pcpu_ref, dummy_release, 0, GFP_KERNEL));
+
+	percpu_ref_get(&pcpu_ref);
+	percpu_ref_get(&pcpu_ref);
+
+	percpu_ref_put(&pcpu_ref);
+	percpu_ref_put(&pcpu_ref);
+
+	percpu_ref_switch_to_atomic_sync(&pcpu_ref);
+
+	smp_store_release(flag, 1); //signal final put can happen
+
+	percpu_ref_put(&pcpu_ref);
+
+	wait_var_event(flag, smp_load_acquire(flag)); //wait for probe completion
+	unregister_trace_ref_trace_final_put(probe, NULL);
+	percpu_ref_exit(&pcpu_ref);
+}
+
+static struct kunit_case __refdata ref_trace_test_cases[] = {
+	KUNIT_CASE(test_refcount),
+	KUNIT_CASE(test_percpu),
+	{}
+};
+
+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");

3/2 2026/07/06 07:15 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5812
							output: 71
							thoughts: 670
						

Thoughts:
**Analyzing the Patch for Fuzzing Eligibility**

Okay, so I've got this patch here adding a new tracepoint, `ref_trace_final_put`, specifically within `percpu_ref_put_many` and `__refcount_sub_and_test`. It also includes a KUnit test for this new tracepoint. I see the diff spans quite a few files: header files for the new tracepoint, the tracepoint itself, a new `.c` file for the implementation, and updates to `Kconfig` and `Makefile` for the new code and test.

Now, the question is, does this constitute a *functional* change that warrants fuzzing? Let's break it down: The patch fundamentally adds instrumentation. It inserts a tracepoint which, from my understanding, provides observability but doesn't fundamentally alter the behavior of the kernel core.

The prompt explicitly states, that modifications involving tracepoints are "purely decorative changes" and not worth fuzzing. It's really that simple. KUnit tests, by their nature, are also not part of any fuzzing campaign. They verify functionality, but are not intended to be fuzzed as part of a fuzzing campaign. So, under the "purely decorative changes" umbrella, adding tracepoints definitely falls into that category.

Let's quickly confirm. Looking closely at the diff, in `percpu_ref_put_many` and `__refcount_sub_and_test`, the code is only adding the `trace_ref_final_put()` calls. No core logic, no new data structures, nothing that would impact execution flow in a way that fuzzing would typically uncover. It's solely for adding the tracepoint.

Therefore, my conclusion is clear: **WorthFuzzing=false**. This is purely a tracepoint addition and a KUnit test. I'm going to set that and move on.



4/2 2026/07/06 07:15 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a new tracepoint (ref_trace_final_put) to reference counting functions and a corresponding KUnit test. It does not alter the functional execution flow of the kernel.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a new tracepoint (ref_trace_final_put) to reference counting functions and a corresponding KUnit test. It does not alter the functional execution flow of the kernel.",
  "WorthFuzzing": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)