On Tue, Jun 16, 2026 at 12:44:49AM +0800, [email protected] wrote:
> From: Wen Yang <[email protected]>
>
> Add KUnit tests for the printk reactor covering:
> - Reactor registration and unregistration lifecycle
> - React callback invocation via rv_react()
> - Double registration rejection
> - Multiple register/unregister cycles
>
> The mock callback calls vprintk_deferred() — the same path as the real
> reactor — then busy-waits to simulate I/O back-pressure, exercising the
> LD_WAIT_FREE constraint of rv_react() under load.
>
> Signed-off-by: Wen Yang <[email protected]>
> ---
> kernel/trace/rv/Kconfig | 10 ++
> kernel/trace/rv/Makefile | 1 +
> kernel/trace/rv/reactor_printk_kunit.c | 123 +++++++++++++++++++++++++
> 3 files changed, 134 insertions(+)
> create mode 100644 kernel/trace/rv/reactor_printk_kunit.c
>
> diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
> index 3884b14df375..ff47895c897f 100644
> --- a/kernel/trace/rv/Kconfig
> +++ b/kernel/trace/rv/Kconfig
> @@ -104,6 +104,16 @@ config RV_REACT_PRINTK
> Enables the printk reactor. The printk reactor emits a printk()
> message if an exception is found.
>
> +config RV_REACT_PRINTK_KUNIT
> + bool "KUnit tests for reactor_printk" if !KUNIT_ALL_TESTS
It would be nice if this was a tristate symbol.
Otherwise the test is completely unusable with CONFIG_KUNIT=m.
Maybe use EXPORT_SYMBOL_FOR_MODULES() for the few needed symbols.
> + depends on RV_REACT_PRINTK && KUNIT
The dependency on RV_REACT_PRINTK is not actually necessary.
Nit: I would split this into two 'depends on'.
> + default KUNIT_ALL_TESTS
> + help
> + This builds KUnit tests for the printk reactor. These are only
> + for development and testing, not for regular kernel use cases.
> +
> + If unsure, say N.
> +
> config RV_REACT_PANIC
> bool "Panic reactor"
> depends on RV_REACTORS
> diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile
> index 94498da35b37..ef0a2dcb927c 100644
> --- a/kernel/trace/rv/Makefile
> +++ b/kernel/trace/rv/Makefile
> @@ -23,4 +23,5 @@ obj-$(CONFIG_RV_MON_NOMISS) += monitors/nomiss/nomiss.o
> # Add new monitors here
> obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
> obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
> +obj-$(CONFIG_RV_REACT_PRINTK_KUNIT) += reactor_printk_kunit.o
> obj-$(CONFIG_RV_REACT_PANIC) += reactor_panic.o
> diff --git a/kernel/trace/rv/reactor_printk_kunit.c
> b/kernel/trace/rv/reactor_printk_kunit.c
> new file mode 100644
> index 000000000000..933aa5602226
> --- /dev/null
> +++ b/kernel/trace/rv/reactor_printk_kunit.c
> @@ -0,0 +1,123 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * KUnit tests for reactor_printk
> + *
> + */
> +
> +#include <kunit/test.h>
> +#include <linux/rv.h>
> +#include <linux/printk.h>
> +#include <linux/sched/clock.h>
> +#include <linux/processor.h>
> +
> +/*
> + * Simulated execution time for mock_printk_react (sched_clock units,
> + * nanoseconds). Models the time a real printk reactor callback may consume
> + * under I/O pressure, exercising the LD_WAIT_FREE constraint of rv_react().
> + */
> +#define MOCK_REACT_DURATION_NS 5000000ULL
> +
> +/*
> + * Mock react callback mirroring rv_printk_reaction().
> + *
> + * Calls vprintk_deferred() — the same path as the real reactor — then holds
> + * the CPU for MOCK_REACT_DURATION_NS via a sched_clock() timed busy-loop,
> + * simulating a callback that is slow due to I/O back-pressure.
> + * sched_clock() is notrace and lock-free; no sleep or lock acquisition is
> + * performed, satisfying the LD_WAIT_FREE constraint of rv_react().
> + */
> +__printf(1, 0) static void mock_printk_react(const char *msg, va_list args)
> +{
> + u64 start = sched_clock();
> +
> + vprintk_deferred(msg, args);
This can get out of sync with the real reactor.
> +
> + while (sched_clock() - start < MOCK_REACT_DURATION_NS)
> + cpu_relax();
> +}
> +
> +static struct rv_reactor mock_printk_reactor = {
> + .name = "test_printk",
> + .description = "test printk reactor",
> + .react = mock_printk_react,
> +};
> +
> +/* Test 1: register and unregister reactor */
Not a fan of the test numbers in the comment.
They will become stale fast.
> +static void test_printk_register_unregister(struct kunit *test)
> +{
> + int ret;
> +
> + ret = rv_register_reactor(&mock_printk_reactor);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> + KUNIT_EXPECT_STREQ(test, mock_printk_reactor.name, "test_printk");
This doesn't really test anything.
> +
> + rv_unregister_reactor(&mock_printk_reactor);
> +}
> +
> +/* Test 2: react callback is invoked via rv_react() */
> +static void test_printk_react_called(struct kunit *test)
> +{
> + struct rv_reactor reactor = {
> + .name = "printk_cb_test",
> + .react = mock_printk_react,
> + };
> + struct rv_monitor monitor = {
> + .name = "test_monitor",
> + .reactor = &reactor,
> + .react = mock_printk_react,
> + };
> +
> + rv_react(&monitor, "printk violation message");
The invocation is not actually tested.
> +}
> +
> +/* Test 3: double registration should fail */
> +static void test_printk_double_register(struct kunit *test)
> +{
> + int ret;
> +
> + ret = rv_register_reactor(&mock_printk_reactor);
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + ret = rv_register_reactor(&mock_printk_reactor);
> + KUNIT_EXPECT_NE(test, ret, 0);
This could test the specific return value.
> +
> + rv_unregister_reactor(&mock_printk_reactor);
> +}
> +
> +/* Test 4: register/unregister cycle */
> +static void test_printk_register_cycle(struct kunit *test)
> +{
> + int ret, i;
> +
> + for (i = 0; i < 5; i++) {
> + ret = rv_register_reactor(&mock_printk_reactor);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +
> + rv_unregister_reactor(&mock_printk_reactor);
> + }
> +}
> +
> +/* Test 5: react callback is not NULL (printk reactors must provide react) */
> +static void test_printk_react_not_null(struct kunit *test)
> +{
> + KUNIT_EXPECT_NOT_NULL(test, mock_printk_reactor.react);
This doesn't really test anything.
> +}
> +
> +static struct kunit_case reactor_printk_kunit_cases[] = {
> + KUNIT_CASE(test_printk_register_unregister),
> + KUNIT_CASE(test_printk_react_called),
> + KUNIT_CASE(test_printk_double_register),
> + KUNIT_CASE(test_printk_register_cycle),
> + KUNIT_CASE(test_printk_react_not_null),
Most tests are not related to the printk reactor at all.
Maybe add a generic "rv_reactor" suite.
> + {}
> +};
> +
> +static struct kunit_suite reactor_printk_kunit_suite = {
> + .name = "rv_reactor_printk",
> + .test_cases = reactor_printk_kunit_cases,
> +};
> +
> +kunit_test_suite(reactor_printk_kunit_suite);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("KUnit tests for reactor_printk");
> --
> 2.25.1
>