mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-08-15 21:46:42 +00:00

Argument 'p' of reactors_show() and monitor_reactor_show() is not a pointer to struct rv_reactor, it is actually a pointer to the list_head inside struct rv_reactor. Therefore it's wrong to cast 'p' to struct rv_reactor *. This wrong type cast has been there since the beginning. But it still worked because the list_head was the first field in struct rv_reactor_def. This is no longer true since commit3d3c376118
("rv: Merge struct rv_reactor_def into struct rv_reactor") moved the list_head, and this wrong type cast became a functional problem. Properly use container_of() instead. Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Cc: Gabriele Monaco <gmonaco@redhat.com> Link: https://lore.kernel.org/b4febbd6844311209e4c8768b65d508b81bd8c9b.1753625621.git.namcao@linutronix.de Fixes:3d3c376118
("rv: Merge struct rv_reactor_def into struct rv_reactor") Signed-off-by: Nam Cao <namcao@linutronix.de> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
480 lines
10 KiB
C
480 lines
10 KiB
C
// 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 *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;
|
|
|
|
mutex_lock(&rv_interface_lock);
|
|
|
|
retval = -EINVAL;
|
|
|
|
list_for_each_entry(reactor, &rv_reactors_list, list) {
|
|
if (strcmp(ptr, reactor->name) != 0)
|
|
continue;
|
|
|
|
monitor_swap_reactors(mon, reactor);
|
|
|
|
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 *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)
|
|
{
|
|
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)
|
|
{
|
|
mutex_lock(&rv_interface_lock);
|
|
list_del(&reactor->list);
|
|
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,
|
|
.write = reacting_on_write_data,
|
|
.read = reacting_on_read_data,
|
|
};
|
|
|
|
/**
|
|
* reactor_populate_monitor - creates per monitor reactors file
|
|
* @mon: The monitor.
|
|
*
|
|
* Returns 0 if successful, error otherwise.
|
|
*/
|
|
int reactor_populate_monitor(struct rv_monitor *mon)
|
|
{
|
|
struct dentry *tmp;
|
|
|
|
tmp = rv_create_file("reactors", RV_MODE_WRITE, mon->root_d, 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, 2) static void rv_nop_reaction(const 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;
|
|
}
|