xref: /linux/lib/kunit/bug.c (revision 6b3f7af57881f6d6250c6dcc4d910fe8e855a607)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * KUnit helpers for backtrace suppression
4  *
5  * Copyright (C) 2025 Alessandro Carminati <acarmina@redhat.com>
6  * Copyright (C) 2024 Guenter Roeck <linux@roeck-us.net>
7  */
8 
9 #include <kunit/resource.h>
10 #include <linux/export.h>
11 #include <linux/rculist.h>
12 #include <linux/sched.h>
13 #include <linux/sched/task.h>
14 #include <linux/spinlock.h>
15 
16 #include "hooks-impl.h"
17 
18 struct kunit_suppressed_warning {
19 	struct list_head node;
20 	struct task_struct *task;
21 	struct kunit *test;
22 	atomic_t counter;
23 };
24 
25 static LIST_HEAD(suppressed_warnings);
26 static DEFINE_SPINLOCK(suppressed_warnings_lock);
27 
28 static void kunit_suppress_warning_remove(struct kunit_suppressed_warning *w)
29 {
30 	unsigned long flags;
31 
32 	spin_lock_irqsave(&suppressed_warnings_lock, flags);
33 	list_del_rcu(&w->node);
34 	spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
35 	put_task_struct(w->task);
36 }
37 
38 KUNIT_DEFINE_ACTION_WRAPPER(kunit_suppress_warning_cleanup,
39 			    kunit_suppress_warning_remove,
40 			    struct kunit_suppressed_warning *);
41 
42 bool kunit_has_active_suppress_warning(void)
43 {
44 	return __kunit_is_suppressed_warning_impl(false);
45 }
46 EXPORT_SYMBOL_GPL(kunit_has_active_suppress_warning);
47 
48 struct kunit_suppressed_warning *
49 kunit_start_suppress_warning(struct kunit *test)
50 {
51 	struct kunit_suppressed_warning *w;
52 	unsigned long flags;
53 	int ret;
54 
55 	if (kunit_has_active_suppress_warning()) {
56 		KUNIT_FAIL(test, "Another suppression block is already active");
57 		return NULL;
58 	}
59 
60 	w = kunit_kzalloc(test, sizeof(*w), GFP_KERNEL);
61 	if (!w) {
62 		KUNIT_FAIL(test, "Failed to allocate suppression handle.");
63 		return NULL;
64 	}
65 
66 	w->task = get_task_struct(current);
67 	w->test = test;
68 
69 	spin_lock_irqsave(&suppressed_warnings_lock, flags);
70 	list_add_rcu(&w->node, &suppressed_warnings);
71 	spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
72 
73 	ret = kunit_add_action_or_reset(test,
74 					kunit_suppress_warning_cleanup, w);
75 	if (ret) {
76 		KUNIT_FAIL(test, "Failed to add suppression cleanup action.");
77 		return NULL;
78 	}
79 
80 	return w;
81 }
82 EXPORT_SYMBOL_GPL(kunit_start_suppress_warning);
83 
84 void kunit_end_suppress_warning(struct kunit *test,
85 				struct kunit_suppressed_warning *w)
86 {
87 	if (!w)
88 		return;
89 	kunit_release_action(test, kunit_suppress_warning_cleanup, w);
90 }
91 EXPORT_SYMBOL_GPL(kunit_end_suppress_warning);
92 
93 void __kunit_suppress_auto_cleanup(struct kunit_suppressed_warning **wp)
94 {
95 	if (*wp)
96 		kunit_end_suppress_warning((*wp)->test, *wp);
97 }
98 EXPORT_SYMBOL_GPL(__kunit_suppress_auto_cleanup);
99 
100 int kunit_suppressed_warning_count(struct kunit_suppressed_warning *w)
101 {
102 	return w ? atomic_read(&w->counter) : 0;
103 }
104 EXPORT_SYMBOL_GPL(kunit_suppressed_warning_count);
105 
106 bool __kunit_is_suppressed_warning_impl(bool count)
107 {
108 	struct kunit_suppressed_warning *w;
109 
110 	guard(rcu)();
111 	list_for_each_entry_rcu(w, &suppressed_warnings, node) {
112 		if (w->task == current) {
113 			if (count)
114 				atomic_inc(&w->counter);
115 			return true;
116 		}
117 	}
118 
119 	return false;
120 }
121