// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org> * * Runtime reactor interface. * * A runtime monitor can cause a reaction to the detection of an * exception on the model's execution. By default, the monitors have * tracing reactions, printing the monitor output via tracepoints. * But other reactions can be added (on-demand) via this interface. * * == Registering reactors == * * The struct rv_reactor defines a callback function to be executed * in case of a model exception happens. The callback function * receives a message to be (optionally) printed before executing * the reaction. * * A RV reactor is registered via: * int rv_register_reactor(struct rv_reactor *reactor) * And unregistered via: * int rv_unregister_reactor(struct rv_reactor *reactor) * * These functions are exported to modules, enabling reactors to be * dynamically loaded. * * == User interface == * * The user interface resembles the kernel tracing interface and * presents these files: * * "available_reactors" * - List the available reactors, one per line. * * For example: * # cat available_reactors * nop * panic * printk * * "reacting_on" * - It is an on/off general switch for reactors, disabling * all reactions. * * "monitors/MONITOR/reactors" * - List available reactors, with the select reaction for the given * MONITOR inside []. The default one is the nop (no operation) * reactor. * - Writing the name of an reactor enables it to the given * MONITOR. * * For example: * # cat monitors/wip/reactors * [nop] * panic * printk * # echo panic > monitors/wip/reactors * # cat monitors/wip/reactors * nop * [panic] * printk */ #include <linux/lockdep.h>#include <linux/slab.h> #include "rv.h" /* * Interface for the reactor register. */static LIST_HEAD(rv_reactors_list); static struct rv_reactor *get_reactor_rdef_by_name(char *name){ struct rv_reactor *r; list_for_each_entry(r, &rv_reactors_list, list) { if (strcmp(name, r->name) == 0) return r; } return NULL;} /* * Available reactors seq functions. */static int reactors_show(struct seq_file *m, void *p){ struct rv_reactor *reactor = container_of(p, struct rv_reactor, list); seq_printf(m, "%s\n", reactor->name); return 0;} static void reactors_stop(struct seq_file *m, void *p){ mutex_unlock(&rv_interface_lock);} static void *reactors_start(struct seq_file *m, loff_t *pos){ mutex_lock(&rv_interface_lock); return seq_list_start(&rv_reactors_list, *pos);} static void *reactors_next(struct seq_file *m, void *p, loff_t *pos){ return seq_list_next(p, &rv_reactors_list, pos);} /* * available_reactors seq definition. */static const struct seq_operations available_reactors_seq_ops = { .start = reactors_start, .next = reactors_next, .stop = reactors_stop, .show = reactors_show}; /* * available_reactors interface. */static int available_reactors_open(struct inode *inode, struct file *file){ return seq_open(file, &available_reactors_seq_ops);}; static const struct file_operations available_reactors_ops = { .open = available_reactors_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release}; /* * Monitor's reactor file. */static int monitor_reactor_show(struct seq_file *m, void *p){ struct rv_monitor *mon = m->private; struct rv_reactor *reactor = container_of(p, struct rv_reactor, list); if (mon->reactor == reactor) seq_printf(m, "[%s]\n", reactor->name); else seq_printf(m, "%s\n", reactor->name); return 0;} /* * available_reactors seq definition. */static const struct seq_operations monitor_reactors_seq_ops = { .start = reactors_start, .next = reactors_next, .stop = reactors_stop, .show = monitor_reactor_show}; static void monitor_swap_reactors_single(struct rv_monitor *mon, struct rv_reactor *reactor, bool nested){ bool monitor_enabled; /* nothing to do */ if (mon->reactor == reactor) return; monitor_enabled = mon->enabled; if (monitor_enabled) rv_disable_monitor(mon); mon->reactor = reactor; mon->react = reactor->react; /* enable only once if iterating through a container */ if (monitor_enabled && !nested) rv_enable_monitor(mon);} static void monitor_swap_reactors(struct rv_monitor *mon, struct rv_reactor *reactor){ struct rv_monitor *p = mon; if (rv_is_container_monitor(mon)) list_for_each_entry_continue(p, &rv_monitors_list, list) { if (p->parent != mon) break; monitor_swap_reactors_single(p, reactor, true); } /* * This call enables and disables the monitor if they were active. * In case of a container, we already disabled all and will enable all. * All nested monitors are enabled also if they were off, we may refine * this logic in the future. */ monitor_swap_reactors_single(mon, reactor, false);} static ssize_tmonitor_reactors_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos){ char buff[MAX_RV_REACTOR_NAME_SIZE + 2]; struct rv_monitor *mon; struct rv_reactor *reactor; struct seq_file *seq_f; int retval = -EINVAL; char *ptr; int len; if (count < 1 || count > MAX_RV_REACTOR_NAME_SIZE + 1) return -EINVAL; memset(buff, 0, sizeof(buff)); retval = simple_write_to_buffer(buff, sizeof(buff) - 1, ppos, user_buf, count); if (retval < 0) return -EFAULT; ptr = strim(buff); len = strlen(ptr); if (!len) return count; /* * See monitor_reactors_open() */ seq_f = file->private_data; mon = seq_f->private; guard(mutex)(&rv_interface_lock); list_for_each_entry(reactor, &rv_reactors_list, list) { if (strcmp(ptr, reactor->name) != 0) continue; monitor_swap_reactors(mon, reactor); return count; } return -EINVAL;} /* * available_reactors interface. */static int monitor_reactors_open(struct inode *inode, struct file *file){ struct rv_monitor *mon = inode->i_private; struct seq_file *seq_f; int ret; ret = seq_open(file, &monitor_reactors_seq_ops); if (ret < 0) return ret; /* * seq_open stores the seq_file on the file->private data. */ seq_f = file->private_data; /* * Copy the create file "private" data to the seq_file private data. */ seq_f->private = mon; return 0;}; static const struct file_operations monitor_reactors_ops = { .open = monitor_reactors_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, .write = monitor_reactors_write}; static int __rv_register_reactor(struct rv_reactor *reactor){ struct rv_reactor *r; list_for_each_entry(r, &rv_reactors_list, list) { if (strcmp(reactor->name, r->name) == 0) { pr_info("Reactor %s is already registered\n", reactor->name); return -EINVAL; } } list_add_tail(&reactor->list, &rv_reactors_list); return 0;} /** * rv_register_reactor - register a rv reactor. * @reactor: The rv_reactor to be registered. * * Returns 0 if successful, error otherwise. */int rv_register_reactor(struct rv_reactor *reactor){ 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; } guard(mutex)(&rv_interface_lock); return __rv_register_reactor(reactor);} /** * rv_unregister_reactor - unregister a rv reactor. * @reactor: The rv_reactor to be unregistered. * * Returns 0 if successful, error otherwise. */int rv_unregister_reactor(struct rv_reactor *reactor){ guard(mutex)(&rv_interface_lock); list_del(&reactor->list); return 0;} /* * reacting_on interface. */static bool __read_mostly reacting_on; /** * rv_reacting_on - checks if reacting is on * * Returns 1 if on, 0 otherwise. */static bool rv_reacting_on(void){ /* Ensures that concurrent monitors read consistent reacting_on */ smp_rmb(); return READ_ONCE(reacting_on);} static ssize_t reacting_on_read_data(struct file *filp, char __user *user_buf, size_t count, loff_t *ppos){ char *buff; buff = rv_reacting_on() ? "1\n" : "0\n"; return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff)+1);} static void turn_reacting_off(void){ WRITE_ONCE(reacting_on, false); /* Ensures that concurrent monitors read consistent reacting_on */ smp_wmb();} static void turn_reacting_on(void){ WRITE_ONCE(reacting_on, true); /* Ensures that concurrent monitors read consistent reacting_on */ smp_wmb();} static ssize_t reacting_on_write_data(struct file *filp, const char __user *user_buf, size_t count, loff_t *ppos){ int retval; bool val; retval = kstrtobool_from_user(user_buf, count, &val); if (retval) return retval; guard(mutex)(&rv_interface_lock); if (val) turn_reacting_on(); else turn_reacting_off(); /* * Wait for the execution of all events to finish * before returning to user-space. */ tracepoint_synchronize_unregister(); return count;} static const struct file_operations reacting_on_fops = { .open = simple_open, .write = reacting_on_write_data, .read = reacting_on_read_data,}; /** * 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, struct dentry *root){ struct dentry *tmp; tmp = rv_create_file("reactors", RV_MODE_WRITE, root, mon, &monitor_reactors_ops); if (!tmp) return -ENOMEM; /* * Configure as the rv_nop reactor. */ mon->reactor = get_reactor_rdef_by_name("nop"); return 0;} /* * Nop reactor register */__printf(1, 0) static void rv_nop_reaction(const char *msg, va_list args){} static struct rv_reactor rv_nop = { .name = "nop", .description = "no-operation reactor: do nothing.", .react = rv_nop_reaction}; int init_rv_reactors(struct dentry *root_dir){ int retval; struct dentry *available __free(rv_remove) = rv_create_file("available_reactors", RV_MODE_READ, root_dir, NULL, &available_reactors_ops); 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) return retval; turn_reacting_on(); retain_and_null_ptr(available); retain_and_null_ptr(reacting); return 0;} 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);}EXPORT_SYMBOL_GPL(rv_react);
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
*
* Runtime reactor interface.
*
* A runtime monitor can cause a reaction to the detection of an
* exception on the model's execution. By default, the monitors have
* tracing reactions, printing the monitor output via tracepoints.
* But other reactions can be added (on-demand) via this interface.
*
* == Registering reactors ==
*
* The struct rv_reactor defines a callback function to be executed
* in case of a model exception happens. The callback function
* receives a message to be (optionally) printed before executing
* the reaction.
*
* A RV reactor is registered via:
* int rv_register_reactor(struct rv_reactor *reactor)
* And unregistered via:
* int rv_unregister_reactor(struct rv_reactor *reactor)
*
* These functions are exported to modules, enabling reactors to be
* dynamically loaded.
*
* == User interface ==
*
* The user interface resembles the kernel tracing interface and
* presents these files:
*
* "available_reactors"
* - List the available reactors, one per line.
*
* For example:
* # cat available_reactors
* nop
* panic
* printk
*
* "reacting_on"
* - It is an on/off general switch for reactors, disabling
* all reactions.
*
* "monitors/MONITOR/reactors"
* - List available reactors, with the select reaction for the given
* MONITOR inside []. The default one is the nop (no operation)
* reactor.
* - Writing the name of an reactor enables it to the given
* MONITOR.
*
* For example:
* # cat monitors/wip/reactors
* [nop]
* panic
* printk
* # echo panic > monitors/wip/reactors
* # cat monitors/wip/reactors
* nop
* [panic]
* printk
*/
#include <linux/lockdep.h>
#include <linux/slab.h>
#include "rv.h"
/*
* Interface for the reactor register.
*/
static LIST_HEAD(rv_reactors_list);
static struct rv_reactor *get_reactor_rdef_by_name(char *name)
{
struct rv_reactor *r;
list_for_each_entry(r, &rv_reactors_list, list) {
if (strcmp(name, r->name) == 0)
return r;
}
return NULL;
}
/*
* Available reactors seq functions.
*/
static int reactors_show(struct seq_file *m, void *p)
{
struct rv_reactor *reactor = container_of(p, struct rv_reactor, list);
seq_printf(m, "%s\n", reactor->name);
return 0;
}
static void reactors_stop(struct seq_file *m, void *p)
{
mutex_unlock(&rv_interface_lock);
}
static void *reactors_start(struct seq_file *m, loff_t *pos)
{
mutex_lock(&rv_interface_lock);
return seq_list_start(&rv_reactors_list, *pos);
}
static void *reactors_next(struct seq_file *m, void *p, loff_t *pos)
{
return seq_list_next(p, &rv_reactors_list, pos);
}
/*
* available_reactors seq definition.
*/
static const struct seq_operations available_reactors_seq_ops = {
.start = reactors_start,
.next = reactors_next,
.stop = reactors_stop,
.show = reactors_show
};
/*
* available_reactors interface.
*/
static int available_reactors_open(struct inode *inode, struct file *file)
{
return seq_open(file, &available_reactors_seq_ops);
};
static const struct file_operations available_reactors_ops = {
.open = available_reactors_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
/*
* Monitor's reactor file.
*/
static int monitor_reactor_show(struct seq_file *m, void *p)
{
struct rv_monitor *mon = m->private;
struct rv_reactor *reactor = container_of(p, struct rv_reactor, list);
if (mon->reactor == reactor)
seq_printf(m, "[%s]\n", reactor->name);
else
seq_printf(m, "%s\n", reactor->name);
return 0;
}
/*
* available_reactors seq definition.
*/
static const struct seq_operations monitor_reactors_seq_ops = {
.start = reactors_start,
.next = reactors_next,
.stop = reactors_stop,
.show = monitor_reactor_show
};
static void monitor_swap_reactors_single(struct rv_monitor *mon,
struct rv_reactor *reactor,
bool nested)
{
bool monitor_enabled;
/* nothing to do */
if (mon->reactor == reactor)
return;
monitor_enabled = mon->enabled;
if (monitor_enabled)
rv_disable_monitor(mon);
mon->reactor = reactor;
mon->react = reactor->react;
/* enable only once if iterating through a container */
if (monitor_enabled && !nested)
rv_enable_monitor(mon);
}
static void monitor_swap_reactors(struct rv_monitor *mon, struct rv_reactor *reactor)
{
struct rv_monitor *p = mon;
if (rv_is_container_monitor(mon))
list_for_each_entry_continue(p, &rv_monitors_list, list) {
if (p->parent != mon)
break;
monitor_swap_reactors_single(p, reactor, true);
}
/*
* This call enables and disables the monitor if they were active.
* In case of a container, we already disabled all and will enable all.
* All nested monitors are enabled also if they were off, we may refine
* this logic in the future.
*/
monitor_swap_reactors_single(mon, reactor, false);
}
static ssize_t
monitor_reactors_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
char buff[MAX_RV_REACTOR_NAME_SIZE + 2];
struct rv_monitor *mon;
struct rv_reactor *reactor;
struct seq_file *seq_f;
int retval = -EINVAL;
char *ptr;
int len;
if (count < 1 || count > MAX_RV_REACTOR_NAME_SIZE + 1)
return -EINVAL;
memset(buff, 0, sizeof(buff));
retval = simple_write_to_buffer(buff, sizeof(buff) - 1, ppos, user_buf, count);
if (retval < 0)
return -EFAULT;
ptr = strim(buff);
len = strlen(ptr);
if (!len)
return count;
/*
* See monitor_reactors_open()
*/
seq_f = file->private_data;
mon = seq_f->private;
guard(mutex)(&rv_interface_lock);
list_for_each_entry(reactor, &rv_reactors_list, list) {
if (strcmp(ptr, reactor->name) != 0)
continue;
monitor_swap_reactors(mon, reactor);
return count;
}
return -EINVAL;
}
/*
* available_reactors interface.
*/
static int monitor_reactors_open(struct inode *inode, struct file *file)
{
struct rv_monitor *mon = inode->i_private;
struct seq_file *seq_f;
int ret;
ret = seq_open(file, &monitor_reactors_seq_ops);
if (ret < 0)
return ret;
/*
* seq_open stores the seq_file on the file->private data.
*/
seq_f = file->private_data;
/*
* Copy the create file "private" data to the seq_file private data.
*/
seq_f->private = mon;
return 0;
};
static const struct file_operations monitor_reactors_ops = {
.open = monitor_reactors_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.write = monitor_reactors_write
};
static int __rv_register_reactor(struct rv_reactor *reactor)
{
struct rv_reactor *r;
list_for_each_entry(r, &rv_reactors_list, list) {
if (strcmp(reactor->name, r->name) == 0) {
pr_info("Reactor %s is already registered\n", reactor->name);
return -EINVAL;
}
}
list_add_tail(&reactor->list, &rv_reactors_list);
return 0;
}
/**
* rv_register_reactor - register a rv reactor.
* @reactor: The rv_reactor to be registered.
*
* Returns 0 if successful, error otherwise.
*/
int rv_register_reactor(struct rv_reactor *reactor)
{
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;
}
guard(mutex)(&rv_interface_lock);
return __rv_register_reactor(reactor);
}
/**
* rv_unregister_reactor - unregister a rv reactor.
* @reactor: The rv_reactor to be unregistered.
*
* Returns 0 if successful, error otherwise.
*/
int rv_unregister_reactor(struct rv_reactor *reactor)
{
guard(mutex)(&rv_interface_lock);
list_del(&reactor->list);
return 0;
}
/*
* reacting_on interface.
*/
static bool __read_mostly reacting_on;
/**
* rv_reacting_on - checks if reacting is on
*
* Returns 1 if on, 0 otherwise.
*/
static bool rv_reacting_on(void)
{
/* Ensures that concurrent monitors read consistent reacting_on */
smp_rmb();
return READ_ONCE(reacting_on);
}
static ssize_t reacting_on_read_data(struct file *filp,
char __user *user_buf,
size_t count, loff_t *ppos)
{
char *buff;
buff = rv_reacting_on() ? "1\n" : "0\n";
return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff)+1);
}
static void turn_reacting_off(void)
{
WRITE_ONCE(reacting_on, false);
/* Ensures that concurrent monitors read consistent reacting_on */
smp_wmb();
}
static void turn_reacting_on(void)
{
WRITE_ONCE(reacting_on, true);
/* Ensures that concurrent monitors read consistent reacting_on */
smp_wmb();
}
static ssize_t reacting_on_write_data(struct file *filp, const char __user *user_buf,
size_t count, loff_t *ppos)
{
int retval;
bool val;
retval = kstrtobool_from_user(user_buf, count, &val);
if (retval)
return retval;
guard(mutex)(&rv_interface_lock);
if (val)
turn_reacting_on();
else
turn_reacting_off();
/*
* Wait for the execution of all events to finish
* before returning to user-space.
*/
tracepoint_synchronize_unregister();
return count;
}
static const struct file_operations reacting_on_fops = {
.open = simple_open,
.write = reacting_on_write_data,
.read = reacting_on_read_data,
};
/**
* 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, struct dentry *root)
{
struct dentry *tmp;
tmp = rv_create_file("reactors", RV_MODE_WRITE, root, mon, &monitor_reactors_ops);
if (!tmp)
return -ENOMEM;
/*
* Configure as the rv_nop reactor.
*/
mon->reactor = get_reactor_rdef_by_name("nop");
return 0;
}
/*
* Nop reactor register
*/
__printf(1, 0) static void rv_nop_reaction(const char *msg, va_list args)
{
}
static struct rv_reactor rv_nop = {
.name = "nop",
.description = "no-operation reactor: do nothing.",
.react = rv_nop_reaction
};
int init_rv_reactors(struct dentry *root_dir)
{
int retval;
struct dentry *available __free(rv_remove) =
rv_create_file("available_reactors", RV_MODE_READ, root_dir,
NULL, &available_reactors_ops);
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)
return retval;
turn_reacting_on();
retain_and_null_ptr(available);
retain_and_null_ptr(reacting);
return 0;
}
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);
}
EXPORT_SYMBOL_GPL(rv_react);