mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
rv: Add KUnit mock for current
Some monitors do not only rely on tracepoint arguments but also on the currently executing task. This makes it more challenging to mock events in KUnit. Define wrapper functions around current, the functionality is mocked only during KUnit, an additional function call is avoided using a static branch unless any (even unrelated) KUnit test is running. Rely on a global mock_current variable that is set only by the RV KUnit tests and cleared on teardown. Unrelated KUnit tests that happen to trigger RV handlers would see it null and use current. Reviewed-by: Nam Cao <namcao@linutronix.de> Reviewed-by: Wen Yang <wen.yang@linux.dev> Link: https://lore.kernel.org/r/20260723074534.43521-14-gmonaco@redhat.com Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
This commit is contained in:
parent
8da2a88383
commit
cf8f191c06
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <rv/automata.h>
|
||||
#include <linux/rv.h>
|
||||
#include <rv/kunit.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/sched.h>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
/*
|
||||
* Copyright (C) 2026-2029 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
|
||||
*
|
||||
* Declaration of utilities to run KUnit tests.
|
||||
* Declaration of wrappers to allow mocking core functionality, like current,
|
||||
* and other testing utilities.
|
||||
* Necessary only when mocking may be needed. If the RV KUnit test is
|
||||
* enabled, the wrappers incur an additional function call overhead.
|
||||
*/
|
||||
|
||||
#ifndef _RV_KUNIT_H
|
||||
|
|
@ -57,5 +60,14 @@ void prepare_test(struct kunit *test, const struct rv_kunit_mon *mon);
|
|||
void teardown_test(void *arg);
|
||||
struct task_struct *rv_kunit_alloc_mock_task(struct kunit *test);
|
||||
|
||||
void rv_mock_current(struct task_struct *tsk);
|
||||
struct task_struct *rv_get_mock_current(void);
|
||||
|
||||
#define rv_get_current() (unlikely(kunit_get_current_test()) ? rv_get_mock_current() : current)
|
||||
|
||||
#else /* !CONFIG_RV_MONITORS_KUNIT_TEST */
|
||||
|
||||
#define rv_get_current() current
|
||||
|
||||
#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */
|
||||
#endif /* _RV_KUNIT_H */
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/stringify.h>
|
||||
#include <linux/seq_buf.h>
|
||||
#include <rv/instrumentation.h>
|
||||
#include <rv/kunit.h>
|
||||
#include <trace/events/task.h>
|
||||
#include <trace/events/sched.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -122,4 +122,7 @@ config RV_MONITORS_KUNIT_TEST
|
|||
These tests verify that monitors correctly detect violations by
|
||||
triggering fake events and validating the expected reactions.
|
||||
|
||||
Enabling this may slightly increase overhead of some monitors if any
|
||||
unrelated KUnit test is running.
|
||||
|
||||
If unsure, say N.
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bo
|
|||
static void handle_page_fault(void *data, unsigned long address, struct pt_regs *regs,
|
||||
unsigned long error_code)
|
||||
{
|
||||
ltl_atom_pulse(current, LTL_PAGEFAULT, true);
|
||||
ltl_atom_pulse(rv_get_current(), LTL_PAGEFAULT, true);
|
||||
}
|
||||
|
||||
static int enable_pagefault(void)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ static void handle_sched_set_state(void *data, struct task_struct *task, int sta
|
|||
|
||||
static void handle_sched_exit(void *data, bool is_switch)
|
||||
{
|
||||
ltl_atom_pulse(current, LTL_SCHEDULE_IN, true);
|
||||
ltl_atom_pulse(rv_get_current(), LTL_SCHEDULE_IN, true);
|
||||
}
|
||||
|
||||
static void handle_sched_waking(void *data, struct task_struct *task)
|
||||
|
|
@ -73,7 +73,7 @@ static void handle_sched_waking(void *data, struct task_struct *task)
|
|||
if (in_hardirq()) {
|
||||
ltl_atom_pulse(task, LTL_WOKEN_BY_HARDIRQ, true);
|
||||
} else if (in_task()) {
|
||||
if (current->prio <= task->prio)
|
||||
if (rv_get_current()->prio <= task->prio)
|
||||
ltl_atom_pulse(task, LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO, true);
|
||||
} else if (in_nmi()) {
|
||||
ltl_atom_pulse(task, LTL_WOKEN_BY_NMI, true);
|
||||
|
|
@ -83,12 +83,12 @@ static void handle_sched_waking(void *data, struct task_struct *task)
|
|||
static void handle_contention_begin(void *data, void *lock, unsigned int flags)
|
||||
{
|
||||
if (flags & LCB_F_RT)
|
||||
ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, true);
|
||||
ltl_atom_update(rv_get_current(), LTL_BLOCK_ON_RT_MUTEX, true);
|
||||
}
|
||||
|
||||
static void handle_contention_end(void *data, void *lock, int ret)
|
||||
{
|
||||
ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, false);
|
||||
ltl_atom_update(rv_get_current(), LTL_BLOCK_ON_RT_MUTEX, false);
|
||||
}
|
||||
|
||||
static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
|
||||
|
|
@ -97,7 +97,7 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
|
|||
unsigned long args[6];
|
||||
int op, cmd;
|
||||
|
||||
mon = ltl_get_monitor(current);
|
||||
mon = ltl_get_monitor(rv_get_current());
|
||||
|
||||
switch (id) {
|
||||
#ifdef __NR_clock_nanosleep
|
||||
|
|
@ -106,10 +106,10 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
|
|||
#ifdef __NR_clock_nanosleep_time64
|
||||
case __NR_clock_nanosleep_time64:
|
||||
#endif
|
||||
syscall_get_arguments(current, regs, args);
|
||||
syscall_get_arguments(rv_get_current(), regs, args);
|
||||
ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, args[0] == CLOCK_REALTIME);
|
||||
ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, args[1] == TIMER_ABSTIME);
|
||||
ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, true);
|
||||
ltl_atom_update(rv_get_current(), LTL_CLOCK_NANOSLEEP, true);
|
||||
break;
|
||||
|
||||
#ifdef __NR_futex
|
||||
|
|
@ -118,25 +118,25 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
|
|||
#ifdef __NR_futex_time64
|
||||
case __NR_futex_time64:
|
||||
#endif
|
||||
syscall_get_arguments(current, regs, args);
|
||||
syscall_get_arguments(rv_get_current(), regs, args);
|
||||
op = args[1];
|
||||
cmd = op & FUTEX_CMD_MASK;
|
||||
|
||||
switch (cmd) {
|
||||
case FUTEX_LOCK_PI:
|
||||
case FUTEX_LOCK_PI2:
|
||||
ltl_atom_update(current, LTL_FUTEX_LOCK_PI, true);
|
||||
ltl_atom_update(rv_get_current(), LTL_FUTEX_LOCK_PI, true);
|
||||
break;
|
||||
case FUTEX_WAIT:
|
||||
case FUTEX_WAIT_BITSET:
|
||||
case FUTEX_WAIT_REQUEUE_PI:
|
||||
ltl_atom_update(current, LTL_FUTEX_WAIT, true);
|
||||
ltl_atom_update(rv_get_current(), LTL_FUTEX_WAIT, true);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#ifdef __NR_epoll_wait
|
||||
case __NR_epoll_wait:
|
||||
ltl_atom_update(current, LTL_EPOLL_WAIT, true);
|
||||
ltl_atom_update(rv_get_current(), LTL_EPOLL_WAIT, true);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -144,14 +144,14 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
|
|||
|
||||
static void handle_sys_exit(void *data, struct pt_regs *regs, long ret)
|
||||
{
|
||||
struct ltl_monitor *mon = ltl_get_monitor(current);
|
||||
struct ltl_monitor *mon = ltl_get_monitor(rv_get_current());
|
||||
|
||||
ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
|
||||
ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
|
||||
ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
|
||||
ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
|
||||
ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
|
||||
ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, false);
|
||||
ltl_atom_update(rv_get_current(), LTL_CLOCK_NANOSLEEP, false);
|
||||
}
|
||||
|
||||
static int enable_sleep(void)
|
||||
|
|
|
|||
|
|
@ -885,4 +885,30 @@ void rv_clear_testing(struct kunit_suite *suite)
|
|||
mutex_unlock(&rv_interface_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(rv_clear_testing);
|
||||
|
||||
/*
|
||||
* rv_get_mock_current() is called only if we are running from a KUnit test.
|
||||
* This can occur from a legitimate RV test or any unrelated test running when
|
||||
* a real RV monitor is active and triggering events.
|
||||
* We assume the former case is the only one where mock_current is not NULL and
|
||||
* can occur only sequentially (KUnit doesn't run tests in parallel).
|
||||
* We cannot rely on the test's context because there is no way to safely
|
||||
* understand from which test we are running and KUnit utilities require
|
||||
* locking, which is unsafe from NMI or scheduling context.
|
||||
* Note that it is not possible for a real RV monitor to run when the RV KUnit
|
||||
* tests are running (see rv_set_testing()).
|
||||
*/
|
||||
static struct task_struct *mock_current;
|
||||
|
||||
void rv_mock_current(struct task_struct *tsk)
|
||||
{
|
||||
mock_current = tsk;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(rv_mock_current);
|
||||
|
||||
struct task_struct *rv_get_mock_current(void)
|
||||
{
|
||||
return mock_current ?: current;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rv_get_mock_current);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ void teardown_test(void *arg)
|
|||
else
|
||||
mon->rv_this->react = NULL;
|
||||
active_ctx = NULL;
|
||||
rv_mock_current(NULL);
|
||||
|
||||
if (mon->is_per_task)
|
||||
*mon->task_slot = RV_PER_TASK_MONITOR_INIT;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user