1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * KUnit test for suppressing warning tracebacks. 4 * 5 * Copyright (C) 2024, Guenter Roeck 6 * Author: Guenter Roeck <linux@roeck-us.net> 7 */ 8 9 #include <kunit/test.h> 10 #include <linux/bug.h> 11 #include <linux/completion.h> 12 #include <linux/kthread.h> 13 14 static void backtrace_suppression_test_warn_direct(struct kunit *test) 15 { 16 if (!IS_ENABLED(CONFIG_BUG)) 17 kunit_skip(test, "requires CONFIG_BUG"); 18 19 kunit_warning_suppress(test) { 20 WARN(1, "This backtrace should be suppressed"); 21 /* 22 * Count must be checked inside the scope; the handle 23 * is not accessible after the block exits. 24 */ 25 KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); 26 } 27 KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning()); 28 } 29 30 static noinline void trigger_backtrace_warn(void) 31 { 32 WARN(1, "This backtrace should be suppressed"); 33 } 34 35 static void backtrace_suppression_test_warn_indirect(struct kunit *test) 36 { 37 if (!IS_ENABLED(CONFIG_BUG)) 38 kunit_skip(test, "requires CONFIG_BUG"); 39 40 kunit_warning_suppress(test) { 41 trigger_backtrace_warn(); 42 KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); 43 } 44 } 45 46 static void backtrace_suppression_test_warn_multi(struct kunit *test) 47 { 48 if (!IS_ENABLED(CONFIG_BUG)) 49 kunit_skip(test, "requires CONFIG_BUG"); 50 51 kunit_warning_suppress(test) { 52 WARN(1, "This backtrace should be suppressed"); 53 trigger_backtrace_warn(); 54 KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2); 55 } 56 } 57 58 static void backtrace_suppression_test_warn_on_direct(struct kunit *test) 59 { 60 if (!IS_ENABLED(CONFIG_BUG)) 61 kunit_skip(test, "requires CONFIG_BUG"); 62 if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE) && !IS_ENABLED(CONFIG_KALLSYMS)) 63 kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE or CONFIG_KALLSYMS"); 64 65 kunit_warning_suppress(test) { 66 WARN_ON(1); 67 KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); 68 } 69 } 70 71 static noinline void trigger_backtrace_warn_on(void) 72 { 73 WARN_ON(1); 74 } 75 76 static void backtrace_suppression_test_warn_on_indirect(struct kunit *test) 77 { 78 if (!IS_ENABLED(CONFIG_BUG)) 79 kunit_skip(test, "requires CONFIG_BUG"); 80 if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE)) 81 kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE"); 82 83 kunit_warning_suppress(test) { 84 trigger_backtrace_warn_on(); 85 KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); 86 } 87 } 88 89 static void backtrace_suppression_test_count(struct kunit *test) 90 { 91 if (!IS_ENABLED(CONFIG_BUG)) 92 kunit_skip(test, "requires CONFIG_BUG"); 93 94 kunit_warning_suppress(test) { 95 KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 0); 96 97 WARN(1, "suppressed"); 98 KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); 99 100 WARN(1, "suppressed again"); 101 KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2); 102 } 103 } 104 105 static void backtrace_suppression_test_active_state(struct kunit *test) 106 { 107 KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning()); 108 109 kunit_warning_suppress(test) { 110 KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning()); 111 } 112 113 KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning()); 114 115 kunit_warning_suppress(test) { 116 KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning()); 117 } 118 119 KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning()); 120 } 121 122 static void backtrace_suppression_test_multi_scope(struct kunit *test) 123 { 124 struct kunit_suppressed_warning *sw1, *sw2; 125 126 if (!IS_ENABLED(CONFIG_BUG)) 127 kunit_skip(test, "requires CONFIG_BUG"); 128 if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE)) 129 kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE"); 130 131 sw1 = kunit_start_suppress_warning(test); 132 trigger_backtrace_warn_on(); 133 WARN(1, "suppressed by sw1"); 134 kunit_end_suppress_warning(test, sw1); 135 136 sw2 = kunit_start_suppress_warning(test); 137 WARN(1, "suppressed by sw2"); 138 kunit_end_suppress_warning(test, sw2); 139 140 KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(sw1), 2); 141 KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(sw2), 1); 142 } 143 144 struct cross_kthread_data { 145 bool was_active; 146 struct completion done; 147 }; 148 149 static int cross_kthread_fn(void *data) 150 { 151 struct cross_kthread_data *d = data; 152 153 d->was_active = kunit_has_active_suppress_warning(); 154 complete(&d->done); 155 while (!kthread_should_stop()) 156 schedule(); 157 return 0; 158 } 159 160 static void backtrace_suppression_test_cross_kthread(struct kunit *test) 161 { 162 struct cross_kthread_data data; 163 struct task_struct *task; 164 165 data.was_active = false; 166 init_completion(&data.done); 167 168 kunit_warning_suppress(test) { 169 task = kthread_run(cross_kthread_fn, &data, "kunit-cross-test"); 170 KUNIT_ASSERT_FALSE(test, IS_ERR(task)); 171 wait_for_completion(&data.done); 172 kthread_stop(task); 173 } 174 175 KUNIT_EXPECT_FALSE(test, data.was_active); 176 } 177 178 static struct kunit_case backtrace_suppression_test_cases[] = { 179 KUNIT_CASE(backtrace_suppression_test_warn_direct), 180 KUNIT_CASE(backtrace_suppression_test_warn_indirect), 181 KUNIT_CASE(backtrace_suppression_test_warn_multi), 182 KUNIT_CASE(backtrace_suppression_test_warn_on_direct), 183 KUNIT_CASE(backtrace_suppression_test_warn_on_indirect), 184 KUNIT_CASE(backtrace_suppression_test_count), 185 KUNIT_CASE(backtrace_suppression_test_active_state), 186 KUNIT_CASE(backtrace_suppression_test_multi_scope), 187 KUNIT_CASE(backtrace_suppression_test_cross_kthread), 188 {} 189 }; 190 191 static struct kunit_suite backtrace_suppression_test_suite = { 192 .name = "backtrace-suppression-test", 193 .test_cases = backtrace_suppression_test_cases, 194 }; 195 kunit_test_suites(&backtrace_suppression_test_suite); 196 197 MODULE_LICENSE("GPL"); 198 MODULE_DESCRIPTION("KUnit test to verify warning backtrace suppression"); 199