mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
Validate the functionality of LTL monitors by injecting events in a controlled environment (KUnit) and expecting reactions, just like it is done in DA monitors. Reviewed-by: Nam Cao <namcao@linutronix.de> Link: https://lore.kernel.org/r/20260723074534.43521-15-gmonaco@redhat.com Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
183 lines
4.6 KiB
C
183 lines
4.6 KiB
C
// 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");
|