| // 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/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_single(struct rv_monitor_def *mdef, | |
| struct rv_reactor_def *rdef, | |
| bool reacting, bool nested) | |
| { | |
| 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; | |
| /* enable only once if iterating through a container */ | |
| if (monitor_enabled && !nested) | |
| rv_enable_monitor(mdef); | |
| } | |
| static void monitor_swap_reactors(struct rv_monitor_def *mdef, | |
| struct rv_reactor_def *rdef, bool reacting) | |
| { | |
| struct rv_monitor_def *p = mdef; | |
| if (rv_is_container_monitor(mdef)) | |
| list_for_each_entry_continue(p, &rv_monitors_list, list) { | |
| if (p->parent != mdef->monitor) | |
| break; | |
| monitor_swap_reactors_single(p, rdef, reacting, 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(mdef, rdef, reacting, 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_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, | |
| .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; | |
| } |