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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit test for suppressing warning tracebacks.
*
* Copyright (C) 2024, Guenter Roeck
* Author: Guenter Roeck <linux@roeck-us.net>
*/
#include <kunit/test.h>
#include <linux/bug.h>
#include <linux/completion.h>
#include <linux/kthread.h>
static void backtrace_suppression_test_warn_direct(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
kunit_warning_suppress(test) {
WARN(1, "This backtrace should be suppressed");
/*
* Count must be checked inside the scope; the handle
* is not accessible after the block exits.
*/
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
}
KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
}
static noinline void trigger_backtrace_warn(void)
{
WARN(1, "This backtrace should be suppressed");
}
static void backtrace_suppression_test_warn_indirect(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
kunit_warning_suppress(test) {
trigger_backtrace_warn();
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
}
}
static void backtrace_suppression_test_warn_multi(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
kunit_warning_suppress(test) {
WARN(1, "This backtrace should be suppressed");
trigger_backtrace_warn();
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
}
}
static void backtrace_suppression_test_warn_on_direct(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE) && !IS_ENABLED(CONFIG_KALLSYMS))
kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE or CONFIG_KALLSYMS");
kunit_warning_suppress(test) {
WARN_ON(1);
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
}
}
static noinline void trigger_backtrace_warn_on(void)
{
WARN_ON(1);
}
static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
kunit_warning_suppress(test) {
trigger_backtrace_warn_on();
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
}
}
static void backtrace_suppression_test_count(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
kunit_warning_suppress(test) {
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 0);
WARN(1, "suppressed");
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
WARN(1, "suppressed again");
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
}
}
static void backtrace_suppression_test_active_state(struct kunit *test)
{
KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
kunit_warning_suppress(test) {
KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
}
KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
kunit_warning_suppress(test) {
KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
}
KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
}
static void backtrace_suppression_test_multi_scope(struct kunit *test)
{
struct kunit_suppressed_warning *sw1, *sw2;
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
sw1 = kunit_start_suppress_warning(test);
trigger_backtrace_warn_on();
WARN(1, "suppressed by sw1");
kunit_end_suppress_warning(test, sw1);
sw2 = kunit_start_suppress_warning(test);
WARN(1, "suppressed by sw2");
kunit_end_suppress_warning(test, sw2);
KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(sw1), 2);
KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(sw2), 1);
}
struct cross_kthread_data {
bool was_active;
struct completion done;
};
static int cross_kthread_fn(void *data)
{
struct cross_kthread_data *d = data;
d->was_active = kunit_has_active_suppress_warning();
complete(&d->done);
while (!kthread_should_stop())
schedule();
return 0;
}
static void backtrace_suppression_test_cross_kthread(struct kunit *test)
{
struct cross_kthread_data data;
struct task_struct *task;
data.was_active = false;
init_completion(&data.done);
kunit_warning_suppress(test) {
task = kthread_run(cross_kthread_fn, &data, "kunit-cross-test");
KUNIT_ASSERT_FALSE(test, IS_ERR(task));
wait_for_completion(&data.done);
kthread_stop(task);
}
KUNIT_EXPECT_FALSE(test, data.was_active);
}
static struct kunit_case backtrace_suppression_test_cases[] = {
KUNIT_CASE(backtrace_suppression_test_warn_direct),
KUNIT_CASE(backtrace_suppression_test_warn_indirect),
KUNIT_CASE(backtrace_suppression_test_warn_multi),
KUNIT_CASE(backtrace_suppression_test_warn_on_direct),
KUNIT_CASE(backtrace_suppression_test_warn_on_indirect),
KUNIT_CASE(backtrace_suppression_test_count),
KUNIT_CASE(backtrace_suppression_test_active_state),
KUNIT_CASE(backtrace_suppression_test_multi_scope),
KUNIT_CASE(backtrace_suppression_test_cross_kthread),
{}
};
static struct kunit_suite backtrace_suppression_test_suite = {
.name = "backtrace-suppression-test",
.test_cases = backtrace_suppression_test_cases,
};
kunit_test_suites(&backtrace_suppression_test_suite);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("KUnit test to verify warning backtrace suppression");
|