aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/kernel/trace/rv/rv_monitors_test.c
blob: 3ad11195e664a62180c3bffae8fea0853cfd8433 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// 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");