Runtime verifier updates for v6.19:

- Adapt the ftracetest script to be run from a different folder
 
   This uses the already existing OPT_TEST_DIR but extends it further to run
   independent tests, then add an --rv flag to allow using the script for
   testing RV (mostly) independently on ftrace.
 
 - Add basic RV selftests in selftests/verification for more validations
 
   Add more validations for available/enabled monitors and reactors. This
   could have caught the bug introducing kernel panic solved above. Tests use
   ftracetest.
 
 - Convert react() function in reactor to use va_list directly
 
   Use a central helper to handle the variadic arguments. Clean up macros
   and mark functions as static.
 
 - Add lockdep annotations to reactors to have lockdep complain of errors
 
   If the reactors are called from improper context. Useful to develop new
   reactors. This highlights a warning in the panic reactor that is related
   to the printk subsystem and not to RV.
 
 - Convert core RV code to use lock guards and __free helpers
 
   This completely removes goto statements.
 
 - Fix compilation if !CONFIG_RV_REACTORS
 
   Fix the warning by keeping LTL monitor variable as always static.
 -----BEGIN PGP SIGNATURE-----
 
 iIoEABYKADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCaTBoVxQccm9zdGVkdEBn
 b29kbWlzLm9yZwAKCRAp5XQQmuv6qtWpAQDxPQAJQvBZ41l9q9Cis7PqGGezT4Nv
 g6Fh/ydMOlJCsQD/R0Xd5JxPmBI8FLCwCfqHo7wYKUhP8GfL/ORPEWhU2gI=
 =EEot
 -----END PGP SIGNATURE-----

Merge tag 'trace-rv-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull runtime verifier updates from Steven Rostedt:

 - Adapt the ftracetest script to be run from a different folder

   This uses the already existing OPT_TEST_DIR but extends it further to
   run independent tests, then add an --rv flag to allow using the
   script for testing RV (mostly) independently on ftrace.

 - Add basic RV selftests in selftests/verification for more validations

   Add more validations for available/enabled monitors and reactors.
   This could have caught the bug introducing kernel panic solved above.
   Tests use ftracetest.

 - Convert react() function in reactor to use va_list directly

   Use a central helper to handle the variadic arguments. Clean up
   macros and mark functions as static.

 - Add lockdep annotations to reactors to have lockdep complain of
   errors

   If the reactors are called from improper context. Useful to develop
   new reactors. This highlights a warning in the panic reactor that is
   related to the printk subsystem and not to RV.

 - Convert core RV code to use lock guards and __free helpers

   This completely removes goto statements.

 - Fix compilation if !CONFIG_RV_REACTORS

   Fix the warning by keeping LTL monitor variable as always static.

* tag 'trace-rv-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  rv: Fix compilation if !CONFIG_RV_REACTORS
  rv: Convert to use __free
  rv: Convert to use lock guard
  rv: Add explicit lockdep context for reactors
  rv: Make rv_reacting_on() static
  rv: Pass va_list to reactors
  selftests/verification: Add initial RV tests
  selftest/ftrace: Generalise ftracetest to use with RV
This commit is contained in:
Linus Torvalds 2025-12-05 10:17:00 -08:00
commit 0b1b4a3d8e
23 changed files with 385 additions and 174 deletions

View File

@ -22700,6 +22700,7 @@ F: Documentation/trace/rv/
F: include/linux/rv.h
F: include/rv/
F: kernel/trace/rv/
F: tools/testing/selftests/verification/
F: tools/verification/
RUST

View File

@ -88,7 +88,7 @@ union rv_task_monitor {
struct rv_reactor {
const char *name;
const char *description;
__printf(1, 2) void (*react)(const char *msg, ...);
__printf(1, 0) void (*react)(const char *msg, va_list args);
struct list_head list;
};
#endif
@ -102,7 +102,7 @@ struct rv_monitor {
void (*reset)(void);
#ifdef CONFIG_RV_REACTORS
struct rv_reactor *reactor;
__printf(1, 2) void (*react)(const char *msg, ...);
__printf(1, 0) void (*react)(const char *msg, va_list args);
#endif
struct list_head list;
struct rv_monitor *parent;
@ -116,13 +116,14 @@ int rv_get_task_monitor_slot(void);
void rv_put_task_monitor_slot(int slot);
#ifdef CONFIG_RV_REACTORS
bool rv_reacting_on(void);
int rv_unregister_reactor(struct rv_reactor *reactor);
int rv_register_reactor(struct rv_reactor *reactor);
__printf(2, 3)
void rv_react(struct rv_monitor *monitor, const char *msg, ...);
#else
static inline bool rv_reacting_on(void)
__printf(2, 3)
static inline void rv_react(struct rv_monitor *monitor, const char *msg, ...)
{
return false;
}
#endif /* CONFIG_RV_REACTORS */

View File

@ -16,34 +16,19 @@
#include <linux/bug.h>
#include <linux/sched.h>
#ifdef CONFIG_RV_REACTORS
#define DECLARE_RV_REACTING_HELPERS(name, type) \
static void cond_react_##name(type curr_state, type event) \
{ \
if (!rv_reacting_on() || !rv_##name.react) \
return; \
rv_##name.react("rv: monitor %s does not allow event %s on state %s\n", \
#name, \
model_get_event_name_##name(event), \
model_get_state_name_##name(curr_state)); \
}
#else /* CONFIG_RV_REACTOR */
#define DECLARE_RV_REACTING_HELPERS(name, type) \
static void cond_react_##name(type curr_state, type event) \
{ \
return; \
}
#endif
/*
* Generic helpers for all types of deterministic automata monitors.
*/
#define DECLARE_DA_MON_GENERIC_HELPERS(name, type) \
\
DECLARE_RV_REACTING_HELPERS(name, type) \
static void react_##name(type curr_state, type event) \
{ \
rv_react(&rv_##name, \
"rv: monitor %s does not allow event %s on state %s\n", \
#name, \
model_get_event_name_##name(event), \
model_get_state_name_##name(curr_state)); \
} \
\
/* \
* da_monitor_reset_##name - reset a monitor and setting it to init state \
@ -126,7 +111,7 @@ da_event_##name(struct da_monitor *da_mon, enum events_##name event) \
for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) { \
next_state = model_get_next_state_##name(curr_state, event); \
if (next_state == INVALID_STATE) { \
cond_react_##name(curr_state, event); \
react_##name(curr_state, event); \
trace_error_##name(model_get_state_name_##name(curr_state), \
model_get_event_name_##name(event)); \
return false; \
@ -165,7 +150,7 @@ static inline bool da_event_##name(struct da_monitor *da_mon, struct task_struct
for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) { \
next_state = model_get_next_state_##name(curr_state, event); \
if (next_state == INVALID_STATE) { \
cond_react_##name(curr_state, event); \
react_##name(curr_state, event); \
trace_error_##name(tsk->pid, \
model_get_state_name_##name(curr_state), \
model_get_event_name_##name(event)); \

View File

@ -16,23 +16,9 @@
#error "Please include $(MODEL_NAME).h generated by rvgen"
#endif
#ifdef CONFIG_RV_REACTORS
#define RV_MONITOR_NAME CONCATENATE(rv_, MONITOR_NAME)
static struct rv_monitor RV_MONITOR_NAME;
static void rv_cond_react(struct task_struct *task)
{
if (!rv_reacting_on() || !RV_MONITOR_NAME.react)
return;
RV_MONITOR_NAME.react("rv: "__stringify(MONITOR_NAME)": %s[%d]: violation detected\n",
task->comm, task->pid);
}
#else
static void rv_cond_react(struct task_struct *task)
{
}
#endif
static int ltl_monitor_slot = RV_PER_TASK_MONITOR_INIT;
static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon);
@ -98,7 +84,8 @@ static void ltl_monitor_destroy(void)
static void ltl_illegal_state(struct task_struct *task, struct ltl_monitor *mon)
{
CONCATENATE(trace_error_, MONITOR_NAME)(task);
rv_cond_react(task);
rv_react(&RV_MONITOR_NAME, "rv: "__stringify(MONITOR_NAME)": %s[%d]: violation detected\n",
task->comm, task->pid);
}
static void ltl_attempt_start(struct task_struct *task, struct ltl_monitor *mon)

View File

@ -13,13 +13,9 @@
#include <linux/init.h>
#include <linux/rv.h>
__printf(1, 2) static void rv_panic_reaction(const char *msg, ...)
__printf(1, 0) static void rv_panic_reaction(const char *msg, va_list args)
{
va_list args;
va_start(args, msg);
vpanic(msg, args);
va_end(args);
}
static struct rv_reactor rv_panic = {

View File

@ -12,13 +12,9 @@
#include <linux/init.h>
#include <linux/rv.h>
__printf(1, 2) static void rv_printk_reaction(const char *msg, ...)
__printf(1, 0) static void rv_printk_reaction(const char *msg, va_list args)
{
va_list args;
va_start(args, msg);
vprintk_deferred(msg, args);
va_end(args);
}
static struct rv_reactor rv_printk = {

View File

@ -375,15 +375,13 @@ static ssize_t monitor_enable_write_data(struct file *filp, const char __user *u
if (retval)
return retval;
mutex_lock(&rv_interface_lock);
guard(mutex)(&rv_interface_lock);
if (val)
retval = rv_enable_monitor(mon);
else
retval = rv_disable_monitor(mon);
mutex_unlock(&rv_interface_lock);
return retval ? : count;
}
@ -422,35 +420,27 @@ static const struct file_operations interface_desc_fops = {
static int create_monitor_dir(struct rv_monitor *mon, struct rv_monitor *parent)
{
struct dentry *root = parent ? parent->root_d : get_monitors_root();
const char *name = mon->name;
struct dentry *dir __free(rv_remove) = rv_create_dir(mon->name, root);
struct dentry *tmp;
int retval;
mon->root_d = rv_create_dir(name, root);
if (!mon->root_d)
if (!dir)
return -ENOMEM;
tmp = rv_create_file("enable", RV_MODE_WRITE, mon->root_d, mon, &interface_enable_fops);
if (!tmp) {
retval = -ENOMEM;
goto out_remove_root;
}
tmp = rv_create_file("enable", RV_MODE_WRITE, dir, mon, &interface_enable_fops);
if (!tmp)
return -ENOMEM;
tmp = rv_create_file("desc", RV_MODE_READ, mon->root_d, mon, &interface_desc_fops);
if (!tmp) {
retval = -ENOMEM;
goto out_remove_root;
}
tmp = rv_create_file("desc", RV_MODE_READ, dir, mon, &interface_desc_fops);
if (!tmp)
return -ENOMEM;
retval = reactor_populate_monitor(mon);
retval = reactor_populate_monitor(mon, dir);
if (retval)
goto out_remove_root;
return 0;
out_remove_root:
rv_remove(mon->root_d);
return retval;
mon->root_d = no_free_ptr(dir);
return 0;
}
/*
@ -568,7 +558,7 @@ static void disable_all_monitors(void)
struct rv_monitor *mon;
int enabled = 0;
mutex_lock(&rv_interface_lock);
guard(mutex)(&rv_interface_lock);
list_for_each_entry(mon, &rv_monitors_list, list)
enabled += __rv_disable_monitor(mon, false);
@ -581,8 +571,6 @@ static void disable_all_monitors(void)
*/
tracepoint_synchronize_unregister();
}
mutex_unlock(&rv_interface_lock);
}
static int enabled_monitors_open(struct inode *inode, struct file *file)
@ -623,7 +611,7 @@ static ssize_t enabled_monitors_write(struct file *filp, const char __user *user
if (!len)
return count;
mutex_lock(&rv_interface_lock);
guard(mutex)(&rv_interface_lock);
retval = -EINVAL;
@ -644,13 +632,11 @@ static ssize_t enabled_monitors_write(struct file *filp, const char __user *user
else
retval = rv_disable_monitor(mon);
if (!retval)
retval = count;
break;
if (retval)
return retval;
return count;
}
mutex_unlock(&rv_interface_lock);
return retval;
}
@ -737,7 +723,7 @@ static ssize_t monitoring_on_write_data(struct file *filp, const char __user *us
if (retval)
return retval;
mutex_lock(&rv_interface_lock);
guard(mutex)(&rv_interface_lock);
if (val)
turn_monitoring_on_with_reset();
@ -750,8 +736,6 @@ static ssize_t monitoring_on_write_data(struct file *filp, const char __user *us
*/
tracepoint_synchronize_unregister();
mutex_unlock(&rv_interface_lock);
return count;
}
@ -784,28 +768,26 @@ int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent)
return -EINVAL;
}
mutex_lock(&rv_interface_lock);
guard(mutex)(&rv_interface_lock);
list_for_each_entry(r, &rv_monitors_list, list) {
if (strcmp(monitor->name, r->name) == 0) {
pr_info("Monitor %s is already registered\n", monitor->name);
retval = -EEXIST;
goto out_unlock;
return -EEXIST;
}
}
if (parent && rv_is_nested_monitor(parent)) {
pr_info("Parent monitor %s is already nested, cannot nest further\n",
parent->name);
retval = -EINVAL;
goto out_unlock;
return -EINVAL;
}
monitor->parent = parent;
retval = create_monitor_dir(monitor, parent);
if (retval)
goto out_unlock;
return retval;
/* keep children close to the parent for easier visualisation */
if (parent)
@ -813,9 +795,7 @@ int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent)
else
list_add_tail(&monitor->list, &rv_monitors_list);
out_unlock:
mutex_unlock(&rv_interface_lock);
return retval;
return 0;
}
/**
@ -826,13 +806,12 @@ int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent)
*/
int rv_unregister_monitor(struct rv_monitor *monitor)
{
mutex_lock(&rv_interface_lock);
guard(mutex)(&rv_interface_lock);
rv_disable_monitor(monitor);
list_del(&monitor->list);
destroy_monitor_dir(monitor);
mutex_unlock(&rv_interface_lock);
return 0;
}
@ -840,39 +819,36 @@ int __init rv_init_interface(void)
{
struct dentry *tmp;
int retval;
struct dentry *root_dir __free(rv_remove) = rv_create_dir("rv", NULL);
rv_root.root_dir = rv_create_dir("rv", NULL);
if (!rv_root.root_dir)
goto out_err;
if (!root_dir)
return 1;
rv_root.monitors_dir = rv_create_dir("monitors", rv_root.root_dir);
rv_root.monitors_dir = rv_create_dir("monitors", root_dir);
if (!rv_root.monitors_dir)
goto out_err;
return 1;
tmp = rv_create_file("available_monitors", RV_MODE_READ, rv_root.root_dir, NULL,
tmp = rv_create_file("available_monitors", RV_MODE_READ, root_dir, NULL,
&available_monitors_ops);
if (!tmp)
goto out_err;
return 1;
tmp = rv_create_file("enabled_monitors", RV_MODE_WRITE, rv_root.root_dir, NULL,
tmp = rv_create_file("enabled_monitors", RV_MODE_WRITE, root_dir, NULL,
&enabled_monitors_ops);
if (!tmp)
goto out_err;
return 1;
tmp = rv_create_file("monitoring_on", RV_MODE_WRITE, rv_root.root_dir, NULL,
tmp = rv_create_file("monitoring_on", RV_MODE_WRITE, root_dir, NULL,
&monitoring_on_fops);
if (!tmp)
goto out_err;
retval = init_rv_reactors(rv_root.root_dir);
return 1;
retval = init_rv_reactors(root_dir);
if (retval)
goto out_err;
return 1;
turn_monitoring_on();
return 0;
rv_root.root_dir = no_free_ptr(root_dir);
out_err:
rv_remove(rv_root.root_dir);
printk(KERN_ERR "RV: Error while creating the RV interface\n");
return 1;
return 0;
}

View File

@ -17,6 +17,8 @@ struct rv_interface {
#define rv_create_file tracefs_create_file
#define rv_remove tracefs_remove
DEFINE_FREE(rv_remove, struct dentry *, if (_T) rv_remove(_T));
#define MAX_RV_MONITOR_NAME_SIZE 32
#define MAX_RV_REACTOR_NAME_SIZE 32
@ -30,10 +32,10 @@ bool rv_is_container_monitor(struct rv_monitor *mon);
bool rv_is_nested_monitor(struct rv_monitor *mon);
#ifdef CONFIG_RV_REACTORS
int reactor_populate_monitor(struct rv_monitor *mon);
int reactor_populate_monitor(struct rv_monitor *mon, struct dentry *root);
int init_rv_reactors(struct dentry *root_dir);
#else
static inline int reactor_populate_monitor(struct rv_monitor *mon)
static inline int reactor_populate_monitor(struct rv_monitor *mon, struct dentry *root)
{
return 0;
}

View File

@ -61,6 +61,7 @@
* printk
*/
#include <linux/lockdep.h>
#include <linux/slab.h>
#include "rv.h"
@ -232,9 +233,7 @@ monitor_reactors_write(struct file *file, const char __user *user_buf,
seq_f = file->private_data;
mon = seq_f->private;
mutex_lock(&rv_interface_lock);
retval = -EINVAL;
guard(mutex)(&rv_interface_lock);
list_for_each_entry(reactor, &rv_reactors_list, list) {
if (strcmp(ptr, reactor->name) != 0)
@ -242,13 +241,10 @@ monitor_reactors_write(struct file *file, const char __user *user_buf,
monitor_swap_reactors(mon, reactor);
retval = count;
break;
return count;
}
mutex_unlock(&rv_interface_lock);
return retval;
return -EINVAL;
}
/*
@ -309,18 +305,14 @@ static int __rv_register_reactor(struct rv_reactor *reactor)
*/
int rv_register_reactor(struct rv_reactor *reactor)
{
int retval = 0;
if (strlen(reactor->name) >= MAX_RV_REACTOR_NAME_SIZE) {
pr_info("Reactor %s has a name longer than %d\n",
reactor->name, MAX_RV_MONITOR_NAME_SIZE);
return -EINVAL;
}
mutex_lock(&rv_interface_lock);
retval = __rv_register_reactor(reactor);
mutex_unlock(&rv_interface_lock);
return retval;
guard(mutex)(&rv_interface_lock);
return __rv_register_reactor(reactor);
}
/**
@ -331,9 +323,8 @@ int rv_register_reactor(struct rv_reactor *reactor)
*/
int rv_unregister_reactor(struct rv_reactor *reactor)
{
mutex_lock(&rv_interface_lock);
guard(mutex)(&rv_interface_lock);
list_del(&reactor->list);
mutex_unlock(&rv_interface_lock);
return 0;
}
@ -347,7 +338,7 @@ static bool __read_mostly reacting_on;
*
* Returns 1 if on, 0 otherwise.
*/
bool rv_reacting_on(void)
static bool rv_reacting_on(void)
{
/* Ensures that concurrent monitors read consistent reacting_on */
smp_rmb();
@ -389,7 +380,7 @@ static ssize_t reacting_on_write_data(struct file *filp, const char __user *user
if (retval)
return retval;
mutex_lock(&rv_interface_lock);
guard(mutex)(&rv_interface_lock);
if (val)
turn_reacting_on();
@ -402,8 +393,6 @@ static ssize_t reacting_on_write_data(struct file *filp, const char __user *user
*/
tracepoint_synchronize_unregister();
mutex_unlock(&rv_interface_lock);
return count;
}
@ -416,14 +405,15 @@ static const struct file_operations reacting_on_fops = {
/**
* reactor_populate_monitor - creates per monitor reactors file
* @mon: The monitor.
* @root: The directory of the monitor.
*
* Returns 0 if successful, error otherwise.
*/
int reactor_populate_monitor(struct rv_monitor *mon)
int reactor_populate_monitor(struct rv_monitor *mon, struct dentry *root)
{
struct dentry *tmp;
tmp = rv_create_file("reactors", RV_MODE_WRITE, mon->root_d, mon, &monitor_reactors_ops);
tmp = rv_create_file("reactors", RV_MODE_WRITE, root, mon, &monitor_reactors_ops);
if (!tmp)
return -ENOMEM;
@ -438,7 +428,7 @@ int reactor_populate_monitor(struct rv_monitor *mon)
/*
* Nop reactor register
*/
__printf(1, 2) static void rv_nop_reaction(const char *msg, ...)
__printf(1, 0) static void rv_nop_reaction(const char *msg, va_list args)
{
}
@ -450,30 +440,42 @@ static struct rv_reactor rv_nop = {
int init_rv_reactors(struct dentry *root_dir)
{
struct dentry *available, *reacting;
int retval;
available = rv_create_file("available_reactors", RV_MODE_READ, root_dir, NULL,
&available_reactors_ops);
if (!available)
goto out_err;
struct dentry *available __free(rv_remove) =
rv_create_file("available_reactors", RV_MODE_READ, root_dir,
NULL, &available_reactors_ops);
reacting = rv_create_file("reacting_on", RV_MODE_WRITE, root_dir, NULL, &reacting_on_fops);
if (!reacting)
goto rm_available;
struct dentry *reacting __free(rv_remove) =
rv_create_file("reacting_on", RV_MODE_WRITE, root_dir, NULL, &reacting_on_fops);
if (!reacting || !available)
return -ENOMEM;
retval = __rv_register_reactor(&rv_nop);
if (retval)
goto rm_reacting;
return retval;
turn_reacting_on();
retain_and_null_ptr(available);
retain_and_null_ptr(reacting);
return 0;
rm_reacting:
rv_remove(reacting);
rm_available:
rv_remove(available);
out_err:
return -ENOMEM;
}
void rv_react(struct rv_monitor *monitor, const char *msg, ...)
{
static DEFINE_WAIT_OVERRIDE_MAP(rv_react_map, LD_WAIT_FREE);
va_list args;
if (!rv_reacting_on() || !monitor->react)
return;
va_start(args, msg);
lock_map_acquire_try(&rv_react_map);
monitor->react(msg, args);
lock_map_release(&rv_react_map);
va_end(args);
}

View File

@ -10969,7 +10969,8 @@ static __init int tracer_init_tracefs(void)
tracer_init_tracefs_work_func(NULL);
}
rv_init_interface();
if (rv_init_interface())
pr_err("RV: Error while creating the RV interface\n");
return 0;
}

View File

@ -22,6 +22,7 @@ echo " --fail-unresolved Treat UNRESOLVED as a failure"
echo " -d|--debug Debug mode (trace all shell commands)"
echo " -l|--logdir <dir> Save logs on the <dir>"
echo " If <dir> is -, all logs output in console only"
echo " --rv Run RV selftests instead of ftrace ones"
exit $1
}
@ -133,6 +134,10 @@ parse_opts() { # opts
LINK_PTR=
shift 2
;;
--rv)
RV_TEST=1
shift 1
;;
*.tc)
if [ -f "$1" ]; then
OPT_TEST_CASES="$OPT_TEST_CASES `abspath $1`"
@ -152,9 +157,13 @@ parse_opts() { # opts
;;
esac
done
if [ ! -z "$OPT_TEST_CASES" ]; then
if [ -n "$OPT_TEST_CASES" ]; then
TEST_CASES=$OPT_TEST_CASES
fi
if [ -n "$OPT_TEST_DIR" -a -f "$OPT_TEST_DIR"/test.d/functions ]; then
TOP_DIR=$OPT_TEST_DIR
TEST_DIR=$TOP_DIR/test.d
fi
}
# Parameters
@ -190,10 +199,6 @@ fi
TOP_DIR=`absdir $0`
TEST_DIR=$TOP_DIR/test.d
TEST_CASES=`find_testcases $TEST_DIR`
LOG_TOP_DIR=$TOP_DIR/logs
LOG_DATE=`date +%Y%m%d-%H%M%S`
LOG_DIR=$LOG_TOP_DIR/$LOG_DATE/
LINK_PTR=$LOG_TOP_DIR/latest
KEEP_LOG=0
KTAP=0
DEBUG=0
@ -201,14 +206,23 @@ VERBOSE=0
UNSUPPORTED_RESULT=0
UNRESOLVED_RESULT=0
STOP_FAILURE=0
RV_TEST=0
# Parse command-line options
parse_opts $*
LOG_TOP_DIR=$TOP_DIR/logs
LOG_DATE=`date +%Y%m%d-%H%M%S`
LOG_DIR=$LOG_TOP_DIR/$LOG_DATE/
LINK_PTR=$LOG_TOP_DIR/latest
[ $DEBUG -ne 0 ] && set -x
# Verify parameters
if [ -z "$TRACING_DIR" -o ! -d "$TRACING_DIR" ]; then
errexit "No ftrace directory found"
if [ $RV_TEST -ne 0 ]; then
TRACING_DIR=$TRACING_DIR/rv
if [ ! -d "$TRACING_DIR" ]; then
err_ret=$err_skip
errexit "rv is not configured in this kernel"
fi
fi
# Preparing logs
@ -419,7 +433,7 @@ trap 'SIG_RESULT=$XFAIL' $SIG_XFAIL
__run_test() { # testfile
# setup PID and PPID, $$ is not updated.
(cd $TRACING_DIR; read PID _ < /proc/self/stat; set -e; set -x;
checkreq $1; initialize_ftrace; . $1)
checkreq $1; initialize_system; . $1)
[ $? -ne 0 ] && kill -s $SIG_FAIL $SIG_PID
}
@ -496,7 +510,7 @@ for t in $TEST_CASES; do
exit 1
fi
done
(cd $TRACING_DIR; finish_ftrace) # for cleanup
(cd $TRACING_DIR; finish_system) # for cleanup
prlog ""
prlog "# of passed: " `echo $PASSED_CASES | wc -w`

View File

@ -28,7 +28,7 @@ unmount_tracefs() {
local mount_point="$1"
# Need to make sure the mount isn't busy so that we can umount it
(cd $mount_point; finish_ftrace;)
(cd $mount_point; finish_system;)
cleanup
}

View File

@ -104,7 +104,7 @@ clear_dynamic_events() { # reset all current dynamic events
done
}
initialize_ftrace() { # Reset ftrace to initial-state
initialize_system() { # Reset ftrace to initial-state
# As the initial state, ftrace will be set to nop tracer,
# no events, no triggers, no filters, no function filters,
# no probes, and tracing on.
@ -134,8 +134,8 @@ initialize_ftrace() { # Reset ftrace to initial-state
enable_tracing
}
finish_ftrace() {
initialize_ftrace
finish_system() {
initialize_system
# And recover it to default.
[ -f options/pause-on-trace ] && echo 0 > options/pause-on-trace
}

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
logs

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
all:
TEST_PROGS := verificationtest-ktap
TEST_FILES := test.d settings
EXTRA_CLEAN := $(OUTPUT)/logs/*
include ../lib.mk

View File

@ -0,0 +1 @@
CONFIG_RV=y

View File

@ -0,0 +1 @@
timeout=0

View File

@ -0,0 +1,39 @@
check_requires() { # Check required files, monitors and reactors
for i in "$@" ; do
p=${i%:program}
m=${i%:monitor}
r=${i%:reactor}
if [ $p != $i ]; then
if ! which $p ; then
echo "Required program $p is not found."
exit_unresolved
fi
elif [ $m != $i ]; then
if ! grep -wq $m available_monitors ; then
echo "Required monitor $m is not configured."
exit_unsupported
fi
elif [ $r != $i ]; then
if ! grep -wq $r available_reactors ; then
echo "Required reactor $r is not configured."
exit_unsupported
fi
elif [ ! -e $i ]; then
echo "Required feature interface $i doesn't exist."
exit_unsupported
fi
done
}
initialize_system() { # Reset RV to initial-state
echo > enabled_monitors
for m in monitors/*; do
echo nop > $m/reactors || true
done
echo 1 > monitoring_on
echo 1 > reacting_on || true
}
finish_system() {
initialize_system
}

View File

@ -0,0 +1,75 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later
# description: Test monitor enable/disable
test_simple_monitor() {
local monitor="$1"
local prefix="$2" # nested monitors
echo 1 > "monitors/$prefix$monitor/enable"
grep -q "$monitor$" enabled_monitors
echo 0 > "monitors/$prefix$monitor/enable"
! grep -q "$monitor$" enabled_monitors
echo "$monitor" >> enabled_monitors
grep -q 1 "monitors/$prefix$monitor/enable"
echo "!$monitor" >> enabled_monitors
grep -q 0 "monitors/$prefix$monitor/enable"
}
test_container_monitor() {
local monitor="$1"
local nested
echo 1 > "monitors/$monitor/enable"
grep -q "^$monitor$" enabled_monitors
for nested_dir in "monitors/$monitor"/*; do
[ -d "$nested_dir" ] || continue
nested=$(basename "$nested_dir")
grep -q "^$monitor:$nested$" enabled_monitors
done
test -n "$nested"
echo 0 > "monitors/$monitor/enable"
! grep -q "^$monitor$" enabled_monitors
for nested_dir in "monitors/$monitor"/*; do
[ -d "$nested_dir" ] || continue
nested=$(basename "$nested_dir")
! grep -q "^$monitor:$nested$" enabled_monitors
done
echo "$monitor" >> enabled_monitors
grep -q 1 "monitors/$monitor/enable"
for nested_dir in "monitors/$monitor"/*; do
[ -d "$nested_dir" ] || continue
nested=$(basename "$nested_dir")
grep -q "^$monitor:$nested$" enabled_monitors
done
echo "!$monitor" >> enabled_monitors
grep -q 0 "monitors/$monitor/enable"
for nested_dir in "monitors/$monitor"/*; do
[ -d "$nested_dir" ] || continue
nested=$(basename "$nested_dir")
test_simple_monitor "$nested" "$monitor/"
done
}
for monitor_dir in monitors/*; do
monitor=$(basename "$monitor_dir")
if find "$monitor_dir" -mindepth 1 -type d | grep -q .; then
test_container_monitor "$monitor"
else
test_simple_monitor "$monitor"
fi
done
! echo non_existent_monitor > enabled_monitors
! grep -q "^non_existent_monitor$" enabled_monitors

View File

@ -0,0 +1,68 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later
# description: Test monitor reactor setting
# requires: available_reactors
test_monitor_reactor() {
local monitor="$1"
local prefix="$2" # nested monitors
while read -r reactor; do
[ "$reactor" = nop ] && continue
echo "$reactor" > "monitors/$prefix$monitor/reactors"
grep -q "\\[$reactor\\]" "monitors/$prefix$monitor/reactors"
done < available_reactors
echo nop > "monitors/$prefix$monitor/reactors"
grep -q "\\[nop\\]" "monitors/$prefix$monitor/reactors"
}
test_container_monitor() {
local monitor="$1"
local nested
while read -r reactor; do
[ "$reactor" = nop ] && continue
echo "$reactor" > "monitors/$monitor/reactors"
grep -q "\\[$reactor\\]" "monitors/$monitor/reactors"
for nested_dir in "monitors/$monitor"/*; do
[ -d "$nested_dir" ] || continue
nested=$(basename "$nested_dir")
grep -q "\\[$reactor\\]" "monitors/$monitor/$nested/reactors"
done
done < available_reactors
test -n "$nested"
echo nop > "monitors/$monitor/reactors"
grep -q "\\[nop\\]" "monitors/$monitor/reactors"
for nested_dir in "monitors/$monitor"/*; do
[ -d "$nested_dir" ] || continue
nested=$(basename "$nested_dir")
grep -q "\\[nop\\]" "monitors/$monitor/$nested/reactors"
done
for nested_dir in "monitors/$monitor"/*; do
[ -d "$nested_dir" ] || continue
nested=$(basename "$nested_dir")
test_monitor_reactor "$nested" "$monitor/"
done
}
for monitor_dir in monitors/*; do
monitor=$(basename "$monitor_dir")
if find "$monitor_dir" -mindepth 1 -type d | grep -q .; then
test_container_monitor "$monitor"
else
test_monitor_reactor "$monitor"
fi
done
monitor=$(ls /sys/kernel/tracing/rv/monitors -1 | head -n 1)
test -f "monitors/$monitor/reactors"
! echo non_existent_reactor > "monitors/$monitor/reactors"
! grep -q "\\[non_existent_reactor\\]" "monitors/$monitor/reactors"

View File

@ -0,0 +1,18 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later
# description: Check available monitors
for monitor_dir in monitors/*; do
monitor=$(basename "$monitor_dir")
grep -q "^$monitor$" available_monitors
grep -q . "$monitor_dir"/desc
for nested_dir in "$monitor_dir"/*; do
[ -d "$nested_dir" ] || continue
nested=$(basename "$nested_dir")
grep -q "^$monitor:$nested$" available_monitors
grep -q . "$nested_dir"/desc
done
done

View File

@ -0,0 +1,30 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later
# description: Test wwnr monitor with printk reactor
# requires: available_reactors wwnr:monitor printk:reactor stress-ng:program
load() { # returns true if there was a reaction
local lines_before num
num=$((($(nproc) + 1) / 2))
lines_before=$(dmesg | wc -l)
stress-ng --cpu-sched "$num" --timer "$num" -t 5 -q
dmesg | tail -n $((lines_before + 1)) | grep -q "rv: monitor wwnr does not allow event"
}
echo 1 > monitors/wwnr/enable
echo printk > monitors/wwnr/reactors
load
echo 0 > monitoring_on
! load
echo 1 > monitoring_on
load
echo 0 > reacting_on
! load
echo 1 > reacting_on
echo nop > monitors/wwnr/reactors
echo 0 > monitors/wwnr/enable

View File

@ -0,0 +1,8 @@
#!/bin/sh -e
# SPDX-License-Identifier: GPL-2.0-only
#
# ftracetest-ktap: Wrapper to integrate ftracetest with the kselftest runner
#
# Copyright (C) Arm Ltd., 2023
../ftrace/ftracetest -K -v --rv ../verification