121 lines
2.9 KiB
C
121 lines
2.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* KUnit helpers for backtrace suppression
|
|
*
|
|
* Copyright (C) 2025 Alessandro Carminati <acarmina@redhat.com>
|
|
* Copyright (C) 2024 Guenter Roeck <linux@roeck-us.net>
|
|
*/
|
|
|
|
#include <kunit/resource.h>
|
|
#include <linux/export.h>
|
|
#include <linux/rculist.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/task.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
#include "hooks-impl.h"
|
|
|
|
struct kunit_suppressed_warning {
|
|
struct list_head node;
|
|
struct task_struct *task;
|
|
struct kunit *test;
|
|
atomic_t counter;
|
|
};
|
|
|
|
static LIST_HEAD(suppressed_warnings);
|
|
static DEFINE_SPINLOCK(suppressed_warnings_lock);
|
|
|
|
static void kunit_suppress_warning_remove(struct kunit_suppressed_warning *w)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&suppressed_warnings_lock, flags);
|
|
list_del_rcu(&w->node);
|
|
spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
|
|
put_task_struct(w->task);
|
|
}
|
|
|
|
KUNIT_DEFINE_ACTION_WRAPPER(kunit_suppress_warning_cleanup,
|
|
kunit_suppress_warning_remove,
|
|
struct kunit_suppressed_warning *);
|
|
|
|
bool kunit_has_active_suppress_warning(void)
|
|
{
|
|
return __kunit_is_suppressed_warning_impl(false);
|
|
}
|
|
EXPORT_SYMBOL_GPL(kunit_has_active_suppress_warning);
|
|
|
|
struct kunit_suppressed_warning *
|
|
kunit_start_suppress_warning(struct kunit *test)
|
|
{
|
|
struct kunit_suppressed_warning *w;
|
|
unsigned long flags;
|
|
int ret;
|
|
|
|
if (kunit_has_active_suppress_warning()) {
|
|
KUNIT_FAIL(test, "Another suppression block is already active");
|
|
return NULL;
|
|
}
|
|
|
|
w = kunit_kzalloc(test, sizeof(*w), GFP_KERNEL);
|
|
if (!w) {
|
|
KUNIT_FAIL(test, "Failed to allocate suppression handle.");
|
|
return NULL;
|
|
}
|
|
|
|
w->task = get_task_struct(current);
|
|
w->test = test;
|
|
|
|
spin_lock_irqsave(&suppressed_warnings_lock, flags);
|
|
list_add_rcu(&w->node, &suppressed_warnings);
|
|
spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
|
|
|
|
ret = kunit_add_action_or_reset(test,
|
|
kunit_suppress_warning_cleanup, w);
|
|
if (ret) {
|
|
KUNIT_FAIL(test, "Failed to add suppression cleanup action.");
|
|
return NULL;
|
|
}
|
|
|
|
return w;
|
|
}
|
|
EXPORT_SYMBOL_GPL(kunit_start_suppress_warning);
|
|
|
|
void kunit_end_suppress_warning(struct kunit *test,
|
|
struct kunit_suppressed_warning *w)
|
|
{
|
|
if (!w)
|
|
return;
|
|
kunit_release_action(test, kunit_suppress_warning_cleanup, w);
|
|
}
|
|
EXPORT_SYMBOL_GPL(kunit_end_suppress_warning);
|
|
|
|
void __kunit_suppress_auto_cleanup(struct kunit_suppressed_warning **wp)
|
|
{
|
|
if (*wp)
|
|
kunit_end_suppress_warning((*wp)->test, *wp);
|
|
}
|
|
EXPORT_SYMBOL_GPL(__kunit_suppress_auto_cleanup);
|
|
|
|
int kunit_suppressed_warning_count(struct kunit_suppressed_warning *w)
|
|
{
|
|
return w ? atomic_read(&w->counter) : 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(kunit_suppressed_warning_count);
|
|
|
|
bool __kunit_is_suppressed_warning_impl(bool count)
|
|
{
|
|
struct kunit_suppressed_warning *w;
|
|
|
|
guard(rcu)();
|
|
list_for_each_entry_rcu(w, &suppressed_warnings, node) {
|
|
if (w->task == current) {
|
|
if (count)
|
|
atomic_inc(&w->counter);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|