mirror of https://github.com/torvalds/linux.git
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:
commit
0b1b4a3d8e
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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)); \
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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 retval;
|
||||
|
||||
mon->root_d = no_free_ptr(dir);
|
||||
return 0;
|
||||
|
||||
out_remove_root:
|
||||
rv_remove(mon->root_d);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
logs
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
CONFIG_RV=y
|
||||
|
|
@ -0,0 +1 @@
|
|||
timeout=0
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue