--- /dev/null
+// 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(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;
+
+       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);
+                               return -EBUSY;
+                       }
+               }
+       }
+
+       mutex_unlock(&rv_interface_lock);
+       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.
+ */
+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;
+}