Re: [PATCH V4 02/20] rv: Add runtime reactors interface
From: Steven Rostedt
Date: Thu Jun 23 2022 - 16:40:54 EST
On Thu, 16 Jun 2022 10:44:44 +0200
Daniel Bristot de Oliveira <bristot@xxxxxxxxxx> wrote:
> diff --git a/include/linux/rv.h b/include/linux/rv.h
> index 205e65f57637..1e48c6bb74bf 100644
> --- a/include/linux/rv.h
> +++ b/include/linux/rv.h
> @@ -8,6 +8,13 @@
> */
> #ifndef _LINUX_RV_H
> #define _LINUX_RV_H
> +
> +struct rv_reactor {
> + char *name;
> + char *description;
> + void (*react)(char *msg);
> +};
> +
> struct rv_monitor {
> const char *name;
> const char *description;
> @@ -15,9 +22,15 @@ struct rv_monitor {
> int (*start)(void);
> void (*stop)(void);
> void (*reset)(void);
> + void (*react)(char *msg);
> +
> };
>
> extern bool monitoring_on;
> int rv_unregister_monitor(struct rv_monitor *monitor);
> int rv_register_monitor(struct rv_monitor *monitor);
> +
> +extern bool reacting_on;
> +int rv_unregister_reactor(struct rv_reactor *reactor);
> +int rv_register_reactor(struct rv_reactor *reactor);
> #endif /* _LINUX_RV_H */
> diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
> index 6d127cdb00dd..560408fec0c8 100644
> --- a/kernel/trace/rv/Kconfig
> +++ b/kernel/trace/rv/Kconfig
> @@ -10,3 +10,17 @@ menuconfig RV
> theorem proving). RV works by analyzing the trace of the system's
> actual execution, comparing it against a formal specification of
> the system behavior.
> +
> +if RV
Remove the above.
> +
> +config RV_REACTORS
> + bool "Runtime verification reactors"
> + default y if RV
default y
DEPENDS ON RV
> + help
> + Enables the online runtime verification reactors. 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.
> +
> +endif # RV
> diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile
> index fd995379df67..8944274d9b41 100644
> --- a/kernel/trace/rv/Makefile
> +++ b/kernel/trace/rv/Makefile
> @@ -1,3 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0
>
> obj-$(CONFIG_RV) += rv.o
> +obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
> diff --git a/kernel/trace/rv/rv.c b/kernel/trace/rv/rv.c
> index 43af7b13187e..7576d492a974 100644
> --- a/kernel/trace/rv/rv.c
> +++ b/kernel/trace/rv/rv.c
> @@ -362,8 +362,13 @@ static int create_monitor_dir(struct rv_monitor_def *mdef)
> retval = -ENOMEM;
> goto out_remove_root;
> }
> +#ifdef CONFIG_RV_REACTORS
Could you move the ifdefs to a header or above, and then just make
theses functions into nops when not defined. Keeps the actual code
cleaner.
> + retval = reactor_create_monitor_files(mdef);
> + if (retval)
> + goto out_remove_root;
> +#endif
>
> - return retval;
> + return 0;
>
> out_remove_root:
> rv_remove(mdef->root_d);
> @@ -674,7 +679,11 @@ int rv_register_monitor(struct rv_monitor *monitor)
>
> r->monitor = monitor;
>
> - create_monitor_dir(r);
> + retval = create_monitor_dir(r);
> + if (retval) {
> + kfree(r);
> + goto out_unlock;
> + }
>
> list_add_tail(&r->list, &rv_monitors_list);
>
> @@ -732,6 +741,11 @@ int __init rv_init_interface(void)
> rv_create_file("monitoring_on", 0600, rv_root.root_dir, NULL,
> &monitoring_on_fops);
>
> +#ifdef CONFIG_RV_REACTORS
> + init_rv_reactors(rv_root.root_dir);
> + reacting_on = true;
Same here.
Could have init_rv_reactors() return a value and then:
if (init_rv_reactors(...))
reacting_on = true;
> +#endif
> +
> monitoring_on = true;
>
> return 0;
> diff --git a/kernel/trace/rv/rv.h b/kernel/trace/rv/rv.h
> index 0796867a7b1e..6d43f52d72a9 100644
> --- a/kernel/trace/rv/rv.h
> +++ b/kernel/trace/rv/rv.h
> @@ -15,14 +15,28 @@ struct rv_interface {
> #define rv_remove tracefs_remove
>
> #define MAX_RV_MONITOR_NAME_SIZE 32
> +#define MAX_RV_REACTOR_NAME_SIZE 32
>
> extern struct mutex rv_interface_lock;
>
> +#ifdef CONFIG_RV_REACTORS
> +struct rv_reactor_def {
> + struct list_head list;
> + struct rv_reactor *reactor;
> + /* protected by the monitor interface lock */
> + int counter;
> +};
> +#endif
> +
> struct rv_monitor_def {
> struct list_head list;
> struct rv_monitor *monitor;
> +#ifdef CONFIG_RV_REACTORS
> + struct rv_reactor_def *rdef;
> +#endif
> struct dentry *root_d;
> bool enabled;
> + bool reacting;
> bool task_monitor;
> };
>
> @@ -32,3 +46,9 @@ void reset_all_monitors(void);
> int init_rv_monitors(struct dentry *root_dir);
> int get_task_monitor_slot(void);
> void put_task_monitor_slot(int slot);
> +
> +#ifdef CONFIG_RV_REACTORS
> +extern bool reacting_on;
> +int reactor_create_monitor_files(struct rv_monitor_def *mdef);
> +int init_rv_reactors(struct dentry *root_dir);
> +#endif
> diff --git a/kernel/trace/rv/rv_reactors.c b/kernel/trace/rv/rv_reactors.c
> new file mode 100644
> index 000000000000..bfe54d6996cc
> --- /dev/null
> +++ b/kernel/trace/rv/rv_reactors.c
> @@ -0,0 +1,476 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * 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:
> + * [root@f32 rv]# 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 defaul one is the nop (no operation)
> + * reactor.
> + * - Writing the name of an reactor enables it to the given
> + * MONITOR.
> + *
> + * For example:
> + * [root@f32 rv]# cat monitors/wip/reactors
> + * [nop]
> + * panic
> + * printk
> + * [root@f32 rv]# echo panic > monitors/wip/reactors
> + * [root@f32 rv]# cat monitors/wip/reactors
> + * nop
> + * [panic]
> + * printk
> + *
> + * Copyright (C) 2019-2022 Daniel Bristot de Oliveira <bristot@xxxxxxxxxx>
> + */
> +
> +#include <linux/slab.h>
> +
> +#include "rv.h"
> +
> +bool __read_mostly reacting_on;
> +
> +/*
> + * Interface for the reactor register.
> + */
> +LIST_HEAD(rv_reactors_list);
> +
> +static struct rv_reactor_def *get_reactor_rdef_by_name(char *name)
> +{
> + struct rv_reactor_def *r;
> +
> + list_for_each_entry(r, &rv_reactors_list, list) {
> + if (strcmp(name, r->reactor->name) == 0)
> + return r;
> + }
> + return NULL;
> +}
> +
> +/*
> + * Available reactors seq functions.
> + */
> +static int reactors_show(struct seq_file *m, void *p)
> +{
> + struct rv_reactor_def *rea_def = p;
> +
> + seq_printf(m, "%s\n", rea_def->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 reactor file.
> + */
> +static int monitor_reactor_show(struct seq_file *m, void *p)
> +{
> + struct rv_monitor_def *mdef = m->private;
> + struct rv_reactor_def *rdef = p;
> +
> + if (mdef->rdef == rdef)
> + seq_printf(m, "[%s]\n", rdef->reactor->name);
> + else
> + seq_printf(m, "%s\n", rdef->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 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+1];
> + struct rv_monitor_def *mdef;
> + struct rv_reactor_def *rdef;
> + struct seq_file *seq_f;
> + int retval = -EINVAL;
> + char *ptr = buff;
> + 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)
> + return -EFAULT;
> +
> + len = strlen(ptr);
> + if (!len)
> + return count;
> + /*
> + * remove the \n
> + */
> + ptr[len-1] = '\0';
Again, use strim().
> +
> + /*
> + * See monitor_reactors_open()
> + */
> + seq_f = file->private_data;
> + mdef = seq_f->private;
> +
> + mutex_lock(&rv_interface_lock);
> +
> + retval = -EINVAL;
> +
> + /*
> + * nop special case: disable reacting.
> + */
> + if (strcmp(ptr, "nop") == 0) {
> +
> + if (mdef->monitor->enabled)
> + mdef->monitor->stop();
> +
> + mdef->rdef = get_reactor_rdef_by_name("nop");
> + mdef->reacting = false;
> + mdef->monitor->react = NULL;
> +
> + if (mdef->monitor->enabled)
> + mdef->monitor->start();
> +
> + retval = count;
> + goto unlock;
> + }
> +
> + list_for_each_entry(rdef, &rv_reactors_list, list) {
> + if (strcmp(ptr, rdef->reactor->name) == 0) {
Again,
if (strcmp(ptr, rdef->reactor->name) != 0)
continue;
Then we can remove the extra indent.
-- Steve
> + /*
> + * found!
> + */
> + if (mdef->monitor->enabled)
> + mdef->monitor->stop();
> +
> + mdef->rdef = rdef;
> + mdef->reacting = true;
> + mdef->monitor->react = rdef->reactor->react;
> +
> + if (mdef->monitor->enabled)
> + mdef->monitor->start();
> +
> + retval = count;
> + break;
> + }
> + }
> +
> +unlock:
> + mutex_unlock(&rv_interface_lock);
> +
> + return retval;
> +}
> +
> +/*