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
