509 lines · 10.8 KB · C
RawDownload
// 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_tmonitor_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(); 

Lines 1–500 of 509.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t