| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (C) 2026-2029 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com> | |
| * | |
| * RV monitor kunit tests: | |
| * Tests the RV monitors by triggering fake events to verify monitor | |
| * behavior and reactions. Tests start from the first defined event and | |
| * trigger events in order to verify error detection. | |
| */ | |
| #include <rv/kunit.h> | |
| #include <kunit/test-bug.h> | |
| #include <linux/kernel.h> | |
| #include <linux/rv.h> | |
| #include "rv.h" | |
| /* | |
| * An easy way to pass the context is to use kunit_get_current_test()->priv, | |
| * but this doesn't always work (e.g. a reactor running from another context | |
| * like softirq). Store the current value here whenever a test is running. | |
| */ | |
| static struct rv_kunit_ctx *active_ctx; | |
| __printf(1, 0) | |
| static void rv_kunit_mock_react(const char *msg, va_list args) | |
| { | |
| if (active_ctx) | |
| ++active_ctx->reactions; | |
| } | |
| /* | |
| * teardown_test - Disable the monitor for a kunit test | |
| * | |
| * Since per-task monitors are special, make sure we reset all the ones we | |
| * started manually here, if required. | |
| */ | |
| void teardown_test(void *arg) | |
| { | |
| const struct rv_kunit_mon *mon = arg; | |
| struct kunit *test = kunit_get_current_test(); | |
| if (test) { | |
| struct rv_kunit_ctx *ctx = test->priv; | |
| RV_KUNIT_EXPECT_NO_REACTION(test, ctx); | |
| if (mon->is_per_task && mon->task_reset) { | |
| for (int i = 0; i < ctx->mock_task_count; i++) | |
| mon->task_reset(ctx->mock_tasks[i]); | |
| synchronize_rcu(); | |
| } | |
| } | |
| mon->rv_this->enabled = 0; | |
| if (mon->rv_this->reactor) | |
| mon->rv_this->react = mon->rv_this->reactor->react; | |
| else | |
| mon->rv_this->react = NULL; | |
| active_ctx = NULL; | |
| rv_mock_current(NULL); | |
| if (mon->is_per_task) | |
| *mon->task_slot = RV_PER_TASK_MONITOR_INIT; | |
| else | |
| mon->monitor_destroy(); | |
| } | |
| /* | |
| * prepare_test - Enable the monitor for a kunit test | |
| * | |
| * Do the bare minimum to set up the monitor, per-task monitors are special as | |
| * "real" initialisation/destruction iterates over real tasks, and may register | |
| * handlers. All we need is to select the right slot in the task_struct. | |
| */ | |
| void prepare_test(struct kunit *test, const struct rv_kunit_mon *mon) | |
| { | |
| KUNIT_ASSERT_FALSE(test, mon->rv_this->enabled); | |
| active_ctx = test->priv; | |
| mon->rv_this->react = rv_kunit_mock_react; | |
| if (mon->is_per_task) | |
| *mon->task_slot = 0; | |
| else | |
| KUNIT_ASSERT_EQ(test, mon->monitor_init(), 0); | |
| mon->rv_this->enabled = 1; | |
| KUNIT_ASSERT_EQ(test, 0, | |
| kunit_add_action_or_reset(test, teardown_test, (void *)mon)); | |
| } | |
| struct task_struct *rv_kunit_alloc_mock_task(struct kunit *test) | |
| { | |
| struct rv_kunit_ctx *ctx = test->priv; | |
| struct task_struct *tsk; | |
| KUNIT_ASSERT_LT(test, ctx->mock_task_count, RV_KUNIT_MAX_MOCK_TASKS); | |
| tsk = kunit_kzalloc(test, sizeof(struct task_struct), GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_NULL(test, tsk); | |
| if (!IS_ENABLED(CONFIG_THREAD_INFO_IN_TASK)) { | |
| tsk->stack = kunit_kzalloc(test, sizeof(struct thread_info), GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_NULL(test, tsk->stack); | |
| } | |
| ctx->mock_tasks[ctx->mock_task_count++] = tsk; | |
| return tsk; | |
| } | |
| static int rv_mon_test_init(struct kunit *test) | |
| { | |
| struct rv_kunit_ctx *ctx; | |
| ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); | |
| test->priv = ctx; | |
| return 0; | |
| } | |
| static void __maybe_unused rv_test_stub(struct kunit *test) | |
| { | |
| kunit_skip(test, "Monitor not enabled\n"); | |
| } | |
| /* | |
| * rv_test_dummy - test reactions work as expected | |
| */ | |
| static void rv_test_dummy(struct kunit *test) | |
| { | |
| struct rv_kunit_ctx *ctx = test->priv; | |
| static struct rv_monitor dummy_monitor = { | |
| .name = "dummy", | |
| .react = rv_kunit_mock_react, | |
| }; | |
| active_ctx = ctx; | |
| RV_KUNIT_EXPECT_REACTION_HERE(test, ctx) | |
| rv_react(&dummy_monitor, "dummy"); | |
| RV_KUNIT_EXPECT_NO_REACTION(test, ctx); | |
| active_ctx = NULL; | |
| } | |
| #include "monitors/sco/sco_kunit.c" | |
| #include "monitors/sssw/sssw_kunit.c" | |
| #include "monitors/sts/sts_kunit.c" | |
| #include "monitors/opid/opid_kunit.c" | |
| #include "monitors/nomiss/nomiss_kunit.c" | |
| #include "monitors/pagefault/pagefault_kunit.c" | |
| #include "monitors/sleep/sleep_kunit.c" | |
| static struct kunit_case rv_mon_test_cases[] = { | |
| KUNIT_CASE(rv_test_dummy), | |
| KUNIT_CASE(rv_test_sco), | |
| KUNIT_CASE(rv_test_sssw), | |
| KUNIT_CASE(rv_test_sts), | |
| KUNIT_CASE(rv_test_opid), | |
| KUNIT_CASE(rv_test_nomiss), | |
| KUNIT_CASE(rv_test_pagefault), | |
| KUNIT_CASE(rv_test_sleep), | |
| {} | |
| }; | |
| static struct kunit_suite rv_mon_test_suite = { | |
| .name = "rv_mon", | |
| .suite_init = rv_set_testing, | |
| .suite_exit = rv_clear_testing, | |
| .init = rv_mon_test_init, | |
| .test_cases = rv_mon_test_cases, | |
| }; | |
| kunit_test_suites(&rv_mon_test_suite); | |
| MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>"); | |
| MODULE_DESCRIPTION("RV monitor kunit tests: test monitors by triggering reactions"); | |
| MODULE_LICENSE("GPL"); | |
| MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); |