Skip to content

Commit

Permalink
rv: Add option for nested monitors and include sched
Browse files Browse the repository at this point in the history
Monitors describing complex systems, such as the scheduler, can easily
grow to the point where they are just hard to understand because of the
many possible state transitions.
Often it is possible to break such descriptions into smaller monitors,
sharing some or all events. Enabling those smaller monitors concurrently
is, in fact, testing the system as if we had one single larger monitor.
Splitting models into multiple specification is not only easier to
understand, but gives some more clues when we see errors.

Add the possibility to create container monitors, whose only purpose is
to host other nested monitors. Enabling a container monitor enables all
nested ones, but it's still possible to enable nested monitors
independently.
Add the sched monitor as first container, for now empty.

Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Juri Lelli <juri.lelli@redhat.com>
Link: https://lore.kernel.org/20250305140406.350227-3-gmonaco@redhat.com
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
  • Loading branch information
Gabriele Monaco authored and Steven Rostedt (Google) committed Mar 24, 2025
1 parent 26f8068 commit cb85c66
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 29 deletions.
2 changes: 1 addition & 1 deletion include/linux/rv.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct rv_monitor {

bool rv_monitoring_on(void);
int rv_unregister_monitor(struct rv_monitor *monitor);
int rv_register_monitor(struct rv_monitor *monitor);
int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent);
int rv_get_task_monitor_slot(void);
void rv_put_task_monitor_slot(int slot);

Expand Down
1 change: 1 addition & 0 deletions kernel/trace/rv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ menuconfig RV

source "kernel/trace/rv/monitors/wip/Kconfig"
source "kernel/trace/rv/monitors/wwnr/Kconfig"
source "kernel/trace/rv/monitors/sched/Kconfig"
# Add new monitors here

config RV_REACTORS
Expand Down
1 change: 1 addition & 0 deletions kernel/trace/rv/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ccflags-y += -I $(src) # needed for trace events
obj-$(CONFIG_RV) += rv.o
obj-$(CONFIG_RV_MON_WIP) += monitors/wip/wip.o
obj-$(CONFIG_RV_MON_WWNR) += monitors/wwnr/wwnr.o
obj-$(CONFIG_RV_MON_SCHED) += monitors/sched/sched.o
# Add new monitors here
obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
Expand Down
11 changes: 11 additions & 0 deletions kernel/trace/rv/monitors/sched/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_SCHED
depends on RV
bool "sched monitor"
help
Collection of monitors to check the scheduler behaves according to specifications.
Enable this to enable all scheduler specification supported by the current kernel.

For further information, see:
Documentation/trace/rv/monitor_sched.rst
38 changes: 38 additions & 0 deletions kernel/trace/rv/monitors/sched/sched.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>

#define MODULE_NAME "sched"

#include "sched.h"

struct rv_monitor rv_sched;

struct rv_monitor rv_sched = {
.name = "sched",
.description = "container for several scheduler monitor specifications.",
.enable = NULL,
.disable = NULL,
.reset = NULL,
.enabled = 0,
};

static int __init register_sched(void)
{
rv_register_monitor(&rv_sched, NULL);
return 0;
}

static void __exit unregister_sched(void)
{
rv_unregister_monitor(&rv_sched);
}

module_init(register_sched);
module_exit(unregister_sched);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sched: container for several scheduler monitor specifications.");
3 changes: 3 additions & 0 deletions kernel/trace/rv/monitors/sched/sched.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* SPDX-License-Identifier: GPL-2.0 */

extern struct rv_monitor rv_sched;
2 changes: 1 addition & 1 deletion kernel/trace/rv/monitors/wip/wip.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ static struct rv_monitor rv_wip = {

static int __init register_wip(void)
{
rv_register_monitor(&rv_wip);
rv_register_monitor(&rv_wip, NULL);
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion kernel/trace/rv/monitors/wwnr/wwnr.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ static struct rv_monitor rv_wwnr = {

static int __init register_wwnr(void)
{
rv_register_monitor(&rv_wwnr);
rv_register_monitor(&rv_wwnr, NULL);
return 0;
}

Expand Down
154 changes: 131 additions & 23 deletions kernel/trace/rv/rv.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ struct dentry *get_monitors_root(void)
/*
* Interface for the monitor register.
*/
static LIST_HEAD(rv_monitors_list);
LIST_HEAD(rv_monitors_list);

static int task_monitor_count;
static bool task_monitor_slots[RV_PER_TASK_MONITORS];
Expand Down Expand Up @@ -206,6 +206,30 @@ void rv_put_task_monitor_slot(int slot)
task_monitor_slots[slot] = false;
}

/*
* Monitors with a parent are nested,
* Monitors without a parent could be standalone or containers.
*/
bool rv_is_nested_monitor(struct rv_monitor_def *mdef)
{
return mdef->parent != NULL;
}

/*
* We set our list to have nested monitors listed after their parent
* if a monitor has a child element its a container.
* Containers can be also identified based on their function pointers:
* as they are not real monitors they do not need function definitions
* for enable()/disable(). Use this condition to find empty containers.
* Keep both conditions in case we have some non-compliant containers.
*/
bool rv_is_container_monitor(struct rv_monitor_def *mdef)
{
struct rv_monitor_def *next = list_next_entry(mdef, list);

return next->parent == mdef->monitor || !mdef->monitor->enable;
}

/*
* This section collects the monitor/ files and folders.
*/
Expand All @@ -229,7 +253,8 @@ static int __rv_disable_monitor(struct rv_monitor_def *mdef, bool sync)

if (mdef->monitor->enabled) {
mdef->monitor->enabled = 0;
mdef->monitor->disable();
if (mdef->monitor->disable)
mdef->monitor->disable();

/*
* Wait for the execution of all events to finish.
Expand All @@ -243,6 +268,60 @@ static int __rv_disable_monitor(struct rv_monitor_def *mdef, bool sync)
return 0;
}

static void rv_disable_single(struct rv_monitor_def *mdef)
{
__rv_disable_monitor(mdef, true);
}

static int rv_enable_single(struct rv_monitor_def *mdef)
{
int retval;

lockdep_assert_held(&rv_interface_lock);

if (mdef->monitor->enabled)
return 0;

retval = mdef->monitor->enable();

if (!retval)
mdef->monitor->enabled = 1;

return retval;
}

static void rv_disable_container(struct rv_monitor_def *mdef)
{
struct rv_monitor_def *p = mdef;
int enabled = 0;

list_for_each_entry_continue(p, &rv_monitors_list, list) {
if (p->parent != mdef->monitor)
break;
enabled += __rv_disable_monitor(p, false);
}
if (enabled)
tracepoint_synchronize_unregister();
mdef->monitor->enabled = 0;
}

static int rv_enable_container(struct rv_monitor_def *mdef)
{
struct rv_monitor_def *p = mdef;
int retval = 0;

list_for_each_entry_continue(p, &rv_monitors_list, list) {
if (retval || p->parent != mdef->monitor)
break;
retval = rv_enable_single(p);
}
if (retval)
rv_disable_container(mdef);
else
mdef->monitor->enabled = 1;
return retval;
}

/**
* rv_disable_monitor - disable a given runtime monitor
* @mdef: Pointer to the monitor definition structure.
Expand All @@ -251,7 +330,11 @@ static int __rv_disable_monitor(struct rv_monitor_def *mdef, bool sync)
*/
int rv_disable_monitor(struct rv_monitor_def *mdef)
{
__rv_disable_monitor(mdef, true);
if (rv_is_container_monitor(mdef))
rv_disable_container(mdef);
else
rv_disable_single(mdef);

return 0;
}

Expand All @@ -265,15 +348,10 @@ int rv_enable_monitor(struct rv_monitor_def *mdef)
{
int retval;

lockdep_assert_held(&rv_interface_lock);

if (mdef->monitor->enabled)
return 0;

retval = mdef->monitor->enable();

if (!retval)
mdef->monitor->enabled = 1;
if (rv_is_container_monitor(mdef))
retval = rv_enable_container(mdef);
else
retval = rv_enable_single(mdef);

return retval;
}
Expand Down Expand Up @@ -336,9 +414,9 @@ static const struct file_operations interface_desc_fops = {
* the monitor dir, where the specific options of the monitor
* are exposed.
*/
static int create_monitor_dir(struct rv_monitor_def *mdef)
static int create_monitor_dir(struct rv_monitor_def *mdef, struct rv_monitor_def *parent)
{
struct dentry *root = get_monitors_root();
struct dentry *root = parent ? parent->root_d : get_monitors_root();
const char *name = mdef->monitor->name;
struct dentry *tmp;
int retval;
Expand Down Expand Up @@ -377,7 +455,11 @@ static int monitors_show(struct seq_file *m, void *p)
{
struct rv_monitor_def *mon_def = p;

seq_printf(m, "%s\n", mon_def->monitor->name);
if (mon_def->parent)
seq_printf(m, "%s:%s\n", mon_def->parent->name,
mon_def->monitor->name);
else
seq_printf(m, "%s\n", mon_def->monitor->name);
return 0;
}

Expand Down Expand Up @@ -514,7 +596,7 @@ static ssize_t enabled_monitors_write(struct file *filp, const char __user *user
struct rv_monitor_def *mdef;
int retval = -EINVAL;
bool enable = true;
char *ptr;
char *ptr, *tmp;
int len;

if (count < 1 || count > MAX_RV_MONITOR_NAME_SIZE + 1)
Expand All @@ -541,6 +623,11 @@ static ssize_t enabled_monitors_write(struct file *filp, const char __user *user

retval = -EINVAL;

/* we support 1 nesting level, trim the parent */
tmp = strstr(ptr, ":");
if (tmp)
ptr = tmp+1;

list_for_each_entry(mdef, &rv_monitors_list, list) {
if (strcmp(ptr, mdef->monitor->name) != 0)
continue;
Expand Down Expand Up @@ -613,7 +700,7 @@ static void reset_all_monitors(void)
struct rv_monitor_def *mdef;

list_for_each_entry(mdef, &rv_monitors_list, list) {
if (mdef->monitor->enabled)
if (mdef->monitor->enabled && mdef->monitor->reset)
mdef->monitor->reset();
}
}
Expand Down Expand Up @@ -685,45 +772,66 @@ static void destroy_monitor_dir(struct rv_monitor_def *mdef)
/**
* rv_register_monitor - register a rv monitor.
* @monitor: The rv_monitor to be registered.
* @parent: The parent of the monitor to be registered, NULL if not nested.
*
* Returns 0 if successful, error otherwise.
*/
int rv_register_monitor(struct rv_monitor *monitor)
int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent)
{
struct rv_monitor_def *r;
struct rv_monitor_def *r, *p = NULL;
int retval = 0;

if (strlen(monitor->name) >= MAX_RV_MONITOR_NAME_SIZE) {
pr_info("Monitor %s has a name longer than %d\n", monitor->name,
MAX_RV_MONITOR_NAME_SIZE);
return -1;
return -EINVAL;
}

mutex_lock(&rv_interface_lock);

list_for_each_entry(r, &rv_monitors_list, list) {
if (strcmp(monitor->name, r->monitor->name) == 0) {
pr_info("Monitor %s is already registered\n", monitor->name);
retval = -1;
retval = -EEXIST;
goto out_unlock;
}
}

if (parent) {
list_for_each_entry(r, &rv_monitors_list, list) {
if (strcmp(parent->name, r->monitor->name) == 0) {
p = r;
break;
}
}
}

if (p && rv_is_nested_monitor(p)) {
pr_info("Parent monitor %s is already nested, cannot nest further\n",
parent->name);
return -EINVAL;
}

r = kzalloc(sizeof(struct rv_monitor_def), GFP_KERNEL);
if (!r) {
retval = -ENOMEM;
goto out_unlock;
}

r->monitor = monitor;
r->parent = parent;

retval = create_monitor_dir(r);
retval = create_monitor_dir(r, p);
if (retval) {
kfree(r);
goto out_unlock;
}

list_add_tail(&r->list, &rv_monitors_list);
/* keep children close to the parent for easier visualisation */
if (p)
list_add(&r->list, &p->list);
else
list_add_tail(&r->list, &rv_monitors_list);

out_unlock:
mutex_unlock(&rv_interface_lock);
Expand Down
4 changes: 4 additions & 0 deletions kernel/trace/rv/rv.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct rv_interface {
#define MAX_RV_REACTOR_NAME_SIZE 32

extern struct mutex rv_interface_lock;
extern struct list_head rv_monitors_list;

#ifdef CONFIG_RV_REACTORS
struct rv_reactor_def {
Expand All @@ -34,6 +35,7 @@ struct rv_reactor_def {
struct rv_monitor_def {
struct list_head list;
struct rv_monitor *monitor;
struct rv_monitor *parent;
struct dentry *root_d;
#ifdef CONFIG_RV_REACTORS
struct rv_reactor_def *rdef;
Expand All @@ -45,6 +47,8 @@ struct rv_monitor_def {
struct dentry *get_monitors_root(void);
int rv_disable_monitor(struct rv_monitor_def *mdef);
int rv_enable_monitor(struct rv_monitor_def *mdef);
bool rv_is_container_monitor(struct rv_monitor_def *mdef);
bool rv_is_nested_monitor(struct rv_monitor_def *mdef);

#ifdef CONFIG_RV_REACTORS
int reactor_populate_monitor(struct rv_monitor_def *mdef);
Expand Down
Loading

0 comments on commit cb85c66

Please sign in to comment.