123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <[email protected]>
- *
- * 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/slab.h>
- #include "rv.h"
- /*
- * Interface for the reactor register.
- */
- static 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's 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 void monitor_swap_reactors(struct rv_monitor_def *mdef, struct rv_reactor_def *rdef,
- bool reacting)
- {
- bool monitor_enabled;
- /* nothing to do */
- if (mdef->rdef == rdef)
- return;
- monitor_enabled = mdef->monitor->enabled;
- if (monitor_enabled)
- rv_disable_monitor(mdef);
- /* swap reactor's usage */
- mdef->rdef->counter--;
- rdef->counter++;
- mdef->rdef = rdef;
- mdef->reacting = reacting;
- mdef->monitor->react = rdef->reactor->react;
- if (monitor_enabled)
- rv_enable_monitor(mdef);
- }
- 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_def *mdef;
- struct rv_reactor_def *rdef;
- struct seq_file *seq_f;
- int retval = -EINVAL;
- bool enable;
- 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;
- mdef = seq_f->private;
- mutex_lock(&rv_interface_lock);
- retval = -EINVAL;
- list_for_each_entry(rdef, &rv_reactors_list, list) {
- if (strcmp(ptr, rdef->reactor->name) != 0)
- continue;
- if (rdef == get_reactor_rdef_by_name("nop"))
- enable = false;
- else
- enable = true;
- monitor_swap_reactors(mdef, rdef, enable);
- retval = count;
- break;
- }
- mutex_unlock(&rv_interface_lock);
- return retval;
- }
- /*
- * available_reactors interface.
- */
- static int monitor_reactors_open(struct inode *inode, struct file *file)
- {
- struct rv_monitor_def *mdef = 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 = mdef;
- 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_def *r;
- list_for_each_entry(r, &rv_reactors_list, list) {
- if (strcmp(reactor->name, r->reactor->name) == 0) {
- pr_info("Reactor %s is already registered\n", reactor->name);
- return -EINVAL;
- }
- }
- r = kzalloc(sizeof(struct rv_reactor_def), GFP_KERNEL);
- if (!r)
- return -ENOMEM;
- r->reactor = reactor;
- r->counter = 0;
- list_add_tail(&r->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)
- {
- 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;
- }
- /**
- * 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)
- {
- struct rv_reactor_def *ptr, *next;
- int ret = 0;
- mutex_lock(&rv_interface_lock);
- list_for_each_entry_safe(ptr, next, &rv_reactors_list, list) {
- if (strcmp(reactor->name, ptr->reactor->name) == 0) {
- if (!ptr->counter) {
- list_del(&ptr->list);
- } else {
- printk(KERN_WARNING
- "rv: the rv_reactor %s is in use by %d monitor(s)\n",
- ptr->reactor->name, ptr->counter);
- printk(KERN_WARNING "rv: the rv_reactor %s cannot be removed\n",
- ptr->reactor->name);
- ret = -EBUSY;
- break;
- }
- }
- }
- mutex_unlock(&rv_interface_lock);
- return ret;
- }
- /*
- * reacting_on interface.
- */
- static bool __read_mostly reacting_on;
- /**
- * rv_reacting_on - checks if reacting is on
- *
- * Returns 1 if on, 0 otherwise.
- */
- 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;
- mutex_lock(&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();
- mutex_unlock(&rv_interface_lock);
- return count;
- }
- static const struct file_operations reacting_on_fops = {
- .open = simple_open,
- .llseek = no_llseek,
- .write = reacting_on_write_data,
- .read = reacting_on_read_data,
- };
- /**
- * reactor_populate_monitor - creates per monitor reactors file
- * @mdef: monitor's definition.
- *
- * Returns 0 if successful, error otherwise.
- */
- int reactor_populate_monitor(struct rv_monitor_def *mdef)
- {
- struct dentry *tmp;
- tmp = rv_create_file("reactors", RV_MODE_WRITE, mdef->root_d, mdef, &monitor_reactors_ops);
- if (!tmp)
- return -ENOMEM;
- /*
- * Configure as the rv_nop reactor.
- */
- mdef->rdef = get_reactor_rdef_by_name("nop");
- mdef->rdef->counter++;
- mdef->reacting = false;
- return 0;
- }
- /**
- * reactor_cleanup_monitor - cleanup a monitor reference
- * @mdef: monitor's definition.
- */
- void reactor_cleanup_monitor(struct rv_monitor_def *mdef)
- {
- lockdep_assert_held(&rv_interface_lock);
- mdef->rdef->counter--;
- WARN_ON_ONCE(mdef->rdef->counter < 0);
- }
- /*
- * Nop reactor register
- */
- static void rv_nop_reaction(char *msg)
- {
- }
- 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)
- {
- 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;
- reacting = rv_create_file("reacting_on", RV_MODE_WRITE, root_dir, NULL, &reacting_on_fops);
- if (!reacting)
- goto rm_available;
- retval = __rv_register_reactor(&rv_nop);
- if (retval)
- goto rm_reacting;
- turn_reacting_on();
- return 0;
- rm_reacting:
- rv_remove(reacting);
- rm_available:
- rv_remove(available);
- out_err:
- return -ENOMEM;
- }
|