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