From cf8f191c06546dff223df12010d4a21f7b631ae3 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Thu, 23 Jul 2026 09:45:30 +0200 Subject: 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 Reviewed-by: Wen Yang Link: https://lore.kernel.org/r/20260723074534.43521-14-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- kernel/trace/rv/Kconfig | 3 +++ kernel/trace/rv/monitors/pagefault/pagefault.c | 2 +- kernel/trace/rv/monitors/sleep/sleep.c | 26 +++++++++++++------------- kernel/trace/rv/rv.c | 26 ++++++++++++++++++++++++++ kernel/trace/rv/rv_monitors_test.c | 1 + 5 files changed, 44 insertions(+), 14 deletions(-) (limited to 'kernel') diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig index 5608314de06b..efa930f94ea4 100644 --- a/kernel/trace/rv/Kconfig +++ b/kernel/trace/rv/Kconfig @@ -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. diff --git a/kernel/trace/rv/monitors/pagefault/pagefault.c b/kernel/trace/rv/monitors/pagefault/pagefault.c index 5e1a2a606783..e52500fd2de0 100644 --- a/kernel/trace/rv/monitors/pagefault/pagefault.c +++ b/kernel/trace/rv/monitors/pagefault/pagefault.c @@ -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) diff --git a/kernel/trace/rv/monitors/sleep/sleep.c b/kernel/trace/rv/monitors/sleep/sleep.c index 4fd5e20151a8..a1b569a4b183 100644 --- a/kernel/trace/rv/monitors/sleep/sleep.c +++ b/kernel/trace/rv/monitors/sleep/sleep.c @@ -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) diff --git a/kernel/trace/rv/rv.c b/kernel/trace/rv/rv.c index 4f577d9b5ba8..29f155c6968b 100644 --- a/kernel/trace/rv/rv.c +++ b/kernel/trace/rv/rv.c @@ -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 diff --git a/kernel/trace/rv/rv_monitors_test.c b/kernel/trace/rv/rv_monitors_test.c index be440bf4b4a7..e58bd677ae0b 100644 --- a/kernel/trace/rv/rv_monitors_test.c +++ b/kernel/trace/rv/rv_monitors_test.c @@ -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; -- cgit v1.2.3