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 <[email protected]>
---
 lib/Kconfig                 |  10 ++++
 lib/tests/Makefile          |   1 +
 lib/tests/ref_trace_kunit.c | 141 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 152 insertions(+)

diff --git a/lib/Kconfig b/lib/Kconfig
index 00a9509636c1..7e2746be6d16 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 && TRACEPOINTS
+       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..8f896adc96d1
--- /dev/null
+++ b/lib/tests/ref_trace_kunit.c
@@ -0,0 +1,141 @@
+// 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 */                                 
\
+               int notimeout = wait_var_event_timeout(                         
\
+                       &capture.count,                                         
\
+                       atomic_read_acquire(&capture.count),                    
\
+                       10                                                      
\
+               );                                                              
\
+                                                                               
\
+               KUNIT_ASSERT_TRUE(test, notimeout);                             
\
+                                                                               
\
+               unregister_trace_ref_trace_final_put(probe, NULL);              
\
+               tracepoint_synchronize_unregister();                            
\
+                                                                               
\
+               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 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 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 void test_percpu_ref_put_many(struct kunit *test)
+{
+       struct percpu_ref obj;
+
+       test_init();
+
+       KUNIT_ASSERT_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 <[email protected]>");
+MODULE_DESCRIPTION("KUnit test for ref_trace");
+MODULE_LICENSE("GPL");

-- 
2.51.2


Reply via email to