Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rv: Add option for nested monitors and include sched

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>

authored by

Gabriele Monaco and committed by
Steven Rostedt (Google)
cb85c660 26f80681

+217 -29
+1 -1
include/linux/rv.h
··· 56 56 57 57 bool rv_monitoring_on(void); 58 58 int rv_unregister_monitor(struct rv_monitor *monitor); 59 - int rv_register_monitor(struct rv_monitor *monitor); 59 + int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent); 60 60 int rv_get_task_monitor_slot(void); 61 61 void rv_put_task_monitor_slot(int slot); 62 62
+1
kernel/trace/rv/Kconfig
··· 27 27 28 28 source "kernel/trace/rv/monitors/wip/Kconfig" 29 29 source "kernel/trace/rv/monitors/wwnr/Kconfig" 30 + source "kernel/trace/rv/monitors/sched/Kconfig" 30 31 # Add new monitors here 31 32 32 33 config RV_REACTORS
+1
kernel/trace/rv/Makefile
··· 5 5 obj-$(CONFIG_RV) += rv.o 6 6 obj-$(CONFIG_RV_MON_WIP) += monitors/wip/wip.o 7 7 obj-$(CONFIG_RV_MON_WWNR) += monitors/wwnr/wwnr.o 8 + obj-$(CONFIG_RV_MON_SCHED) += monitors/sched/sched.o 8 9 # Add new monitors here 9 10 obj-$(CONFIG_RV_REACTORS) += rv_reactors.o 10 11 obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
+11
kernel/trace/rv/monitors/sched/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + config RV_MON_SCHED 4 + depends on RV 5 + bool "sched monitor" 6 + help 7 + Collection of monitors to check the scheduler behaves according to specifications. 8 + Enable this to enable all scheduler specification supported by the current kernel. 9 + 10 + For further information, see: 11 + Documentation/trace/rv/monitor_sched.rst
+38
kernel/trace/rv/monitors/sched/sched.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/kernel.h> 3 + #include <linux/module.h> 4 + #include <linux/init.h> 5 + #include <linux/rv.h> 6 + 7 + #define MODULE_NAME "sched" 8 + 9 + #include "sched.h" 10 + 11 + struct rv_monitor rv_sched; 12 + 13 + struct rv_monitor rv_sched = { 14 + .name = "sched", 15 + .description = "container for several scheduler monitor specifications.", 16 + .enable = NULL, 17 + .disable = NULL, 18 + .reset = NULL, 19 + .enabled = 0, 20 + }; 21 + 22 + static int __init register_sched(void) 23 + { 24 + rv_register_monitor(&rv_sched, NULL); 25 + return 0; 26 + } 27 + 28 + static void __exit unregister_sched(void) 29 + { 30 + rv_unregister_monitor(&rv_sched); 31 + } 32 + 33 + module_init(register_sched); 34 + module_exit(unregister_sched); 35 + 36 + MODULE_LICENSE("GPL"); 37 + MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>"); 38 + MODULE_DESCRIPTION("sched: container for several scheduler monitor specifications.");
+3
kernel/trace/rv/monitors/sched/sched.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + extern struct rv_monitor rv_sched;
+1 -1
kernel/trace/rv/monitors/wip/wip.c
··· 71 71 72 72 static int __init register_wip(void) 73 73 { 74 - rv_register_monitor(&rv_wip); 74 + rv_register_monitor(&rv_wip, NULL); 75 75 return 0; 76 76 } 77 77
+1 -1
kernel/trace/rv/monitors/wwnr/wwnr.c
··· 70 70 71 71 static int __init register_wwnr(void) 72 72 { 73 - rv_register_monitor(&rv_wwnr); 73 + rv_register_monitor(&rv_wwnr, NULL); 74 74 return 0; 75 75 } 76 76
+131 -23
kernel/trace/rv/rv.c
··· 162 162 /* 163 163 * Interface for the monitor register. 164 164 */ 165 - static LIST_HEAD(rv_monitors_list); 165 + LIST_HEAD(rv_monitors_list); 166 166 167 167 static int task_monitor_count; 168 168 static bool task_monitor_slots[RV_PER_TASK_MONITORS]; ··· 207 207 } 208 208 209 209 /* 210 + * Monitors with a parent are nested, 211 + * Monitors without a parent could be standalone or containers. 212 + */ 213 + bool rv_is_nested_monitor(struct rv_monitor_def *mdef) 214 + { 215 + return mdef->parent != NULL; 216 + } 217 + 218 + /* 219 + * We set our list to have nested monitors listed after their parent 220 + * if a monitor has a child element its a container. 221 + * Containers can be also identified based on their function pointers: 222 + * as they are not real monitors they do not need function definitions 223 + * for enable()/disable(). Use this condition to find empty containers. 224 + * Keep both conditions in case we have some non-compliant containers. 225 + */ 226 + bool rv_is_container_monitor(struct rv_monitor_def *mdef) 227 + { 228 + struct rv_monitor_def *next = list_next_entry(mdef, list); 229 + 230 + return next->parent == mdef->monitor || !mdef->monitor->enable; 231 + } 232 + 233 + /* 210 234 * This section collects the monitor/ files and folders. 211 235 */ 212 236 static ssize_t monitor_enable_read_data(struct file *filp, char __user *user_buf, size_t count, ··· 253 229 254 230 if (mdef->monitor->enabled) { 255 231 mdef->monitor->enabled = 0; 256 - mdef->monitor->disable(); 232 + if (mdef->monitor->disable) 233 + mdef->monitor->disable(); 257 234 258 235 /* 259 236 * Wait for the execution of all events to finish. ··· 268 243 return 0; 269 244 } 270 245 246 + static void rv_disable_single(struct rv_monitor_def *mdef) 247 + { 248 + __rv_disable_monitor(mdef, true); 249 + } 250 + 251 + static int rv_enable_single(struct rv_monitor_def *mdef) 252 + { 253 + int retval; 254 + 255 + lockdep_assert_held(&rv_interface_lock); 256 + 257 + if (mdef->monitor->enabled) 258 + return 0; 259 + 260 + retval = mdef->monitor->enable(); 261 + 262 + if (!retval) 263 + mdef->monitor->enabled = 1; 264 + 265 + return retval; 266 + } 267 + 268 + static void rv_disable_container(struct rv_monitor_def *mdef) 269 + { 270 + struct rv_monitor_def *p = mdef; 271 + int enabled = 0; 272 + 273 + list_for_each_entry_continue(p, &rv_monitors_list, list) { 274 + if (p->parent != mdef->monitor) 275 + break; 276 + enabled += __rv_disable_monitor(p, false); 277 + } 278 + if (enabled) 279 + tracepoint_synchronize_unregister(); 280 + mdef->monitor->enabled = 0; 281 + } 282 + 283 + static int rv_enable_container(struct rv_monitor_def *mdef) 284 + { 285 + struct rv_monitor_def *p = mdef; 286 + int retval = 0; 287 + 288 + list_for_each_entry_continue(p, &rv_monitors_list, list) { 289 + if (retval || p->parent != mdef->monitor) 290 + break; 291 + retval = rv_enable_single(p); 292 + } 293 + if (retval) 294 + rv_disable_container(mdef); 295 + else 296 + mdef->monitor->enabled = 1; 297 + return retval; 298 + } 299 + 271 300 /** 272 301 * rv_disable_monitor - disable a given runtime monitor 273 302 * @mdef: Pointer to the monitor definition structure. ··· 330 251 */ 331 252 int rv_disable_monitor(struct rv_monitor_def *mdef) 332 253 { 333 - __rv_disable_monitor(mdef, true); 254 + if (rv_is_container_monitor(mdef)) 255 + rv_disable_container(mdef); 256 + else 257 + rv_disable_single(mdef); 258 + 334 259 return 0; 335 260 } 336 261 ··· 348 265 { 349 266 int retval; 350 267 351 - lockdep_assert_held(&rv_interface_lock); 352 - 353 - if (mdef->monitor->enabled) 354 - return 0; 355 - 356 - retval = mdef->monitor->enable(); 357 - 358 - if (!retval) 359 - mdef->monitor->enabled = 1; 268 + if (rv_is_container_monitor(mdef)) 269 + retval = rv_enable_container(mdef); 270 + else 271 + retval = rv_enable_single(mdef); 360 272 361 273 return retval; 362 274 } ··· 414 336 * the monitor dir, where the specific options of the monitor 415 337 * are exposed. 416 338 */ 417 - static int create_monitor_dir(struct rv_monitor_def *mdef) 339 + static int create_monitor_dir(struct rv_monitor_def *mdef, struct rv_monitor_def *parent) 418 340 { 419 - struct dentry *root = get_monitors_root(); 341 + struct dentry *root = parent ? parent->root_d : get_monitors_root(); 420 342 const char *name = mdef->monitor->name; 421 343 struct dentry *tmp; 422 344 int retval; ··· 455 377 { 456 378 struct rv_monitor_def *mon_def = p; 457 379 458 - seq_printf(m, "%s\n", mon_def->monitor->name); 380 + if (mon_def->parent) 381 + seq_printf(m, "%s:%s\n", mon_def->parent->name, 382 + mon_def->monitor->name); 383 + else 384 + seq_printf(m, "%s\n", mon_def->monitor->name); 459 385 return 0; 460 386 } 461 387 ··· 596 514 struct rv_monitor_def *mdef; 597 515 int retval = -EINVAL; 598 516 bool enable = true; 599 - char *ptr; 517 + char *ptr, *tmp; 600 518 int len; 601 519 602 520 if (count < 1 || count > MAX_RV_MONITOR_NAME_SIZE + 1) ··· 622 540 mutex_lock(&rv_interface_lock); 623 541 624 542 retval = -EINVAL; 543 + 544 + /* we support 1 nesting level, trim the parent */ 545 + tmp = strstr(ptr, ":"); 546 + if (tmp) 547 + ptr = tmp+1; 625 548 626 549 list_for_each_entry(mdef, &rv_monitors_list, list) { 627 550 if (strcmp(ptr, mdef->monitor->name) != 0) ··· 700 613 struct rv_monitor_def *mdef; 701 614 702 615 list_for_each_entry(mdef, &rv_monitors_list, list) { 703 - if (mdef->monitor->enabled) 616 + if (mdef->monitor->enabled && mdef->monitor->reset) 704 617 mdef->monitor->reset(); 705 618 } 706 619 } ··· 772 685 /** 773 686 * rv_register_monitor - register a rv monitor. 774 687 * @monitor: The rv_monitor to be registered. 688 + * @parent: The parent of the monitor to be registered, NULL if not nested. 775 689 * 776 690 * Returns 0 if successful, error otherwise. 777 691 */ 778 - int rv_register_monitor(struct rv_monitor *monitor) 692 + int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent) 779 693 { 780 - struct rv_monitor_def *r; 694 + struct rv_monitor_def *r, *p = NULL; 781 695 int retval = 0; 782 696 783 697 if (strlen(monitor->name) >= MAX_RV_MONITOR_NAME_SIZE) { 784 698 pr_info("Monitor %s has a name longer than %d\n", monitor->name, 785 699 MAX_RV_MONITOR_NAME_SIZE); 786 - return -1; 700 + return -EINVAL; 787 701 } 788 702 789 703 mutex_lock(&rv_interface_lock); ··· 792 704 list_for_each_entry(r, &rv_monitors_list, list) { 793 705 if (strcmp(monitor->name, r->monitor->name) == 0) { 794 706 pr_info("Monitor %s is already registered\n", monitor->name); 795 - retval = -1; 707 + retval = -EEXIST; 796 708 goto out_unlock; 797 709 } 710 + } 711 + 712 + if (parent) { 713 + list_for_each_entry(r, &rv_monitors_list, list) { 714 + if (strcmp(parent->name, r->monitor->name) == 0) { 715 + p = r; 716 + break; 717 + } 718 + } 719 + } 720 + 721 + if (p && rv_is_nested_monitor(p)) { 722 + pr_info("Parent monitor %s is already nested, cannot nest further\n", 723 + parent->name); 724 + return -EINVAL; 798 725 } 799 726 800 727 r = kzalloc(sizeof(struct rv_monitor_def), GFP_KERNEL); ··· 819 716 } 820 717 821 718 r->monitor = monitor; 719 + r->parent = parent; 822 720 823 - retval = create_monitor_dir(r); 721 + retval = create_monitor_dir(r, p); 824 722 if (retval) { 825 723 kfree(r); 826 724 goto out_unlock; 827 725 } 828 726 829 - list_add_tail(&r->list, &rv_monitors_list); 727 + /* keep children close to the parent for easier visualisation */ 728 + if (p) 729 + list_add(&r->list, &p->list); 730 + else 731 + list_add_tail(&r->list, &rv_monitors_list); 830 732 831 733 out_unlock: 832 734 mutex_unlock(&rv_interface_lock);
+4
kernel/trace/rv/rv.h
··· 21 21 #define MAX_RV_REACTOR_NAME_SIZE 32 22 22 23 23 extern struct mutex rv_interface_lock; 24 + extern struct list_head rv_monitors_list; 24 25 25 26 #ifdef CONFIG_RV_REACTORS 26 27 struct rv_reactor_def { ··· 35 34 struct rv_monitor_def { 36 35 struct list_head list; 37 36 struct rv_monitor *monitor; 37 + struct rv_monitor *parent; 38 38 struct dentry *root_d; 39 39 #ifdef CONFIG_RV_REACTORS 40 40 struct rv_reactor_def *rdef; ··· 47 45 struct dentry *get_monitors_root(void); 48 46 int rv_disable_monitor(struct rv_monitor_def *mdef); 49 47 int rv_enable_monitor(struct rv_monitor_def *mdef); 48 + bool rv_is_container_monitor(struct rv_monitor_def *mdef); 49 + bool rv_is_nested_monitor(struct rv_monitor_def *mdef); 50 50 51 51 #ifdef CONFIG_RV_REACTORS 52 52 int reactor_populate_monitor(struct rv_monitor_def *mdef);
+25 -3
kernel/trace/rv/rv_reactors.c
··· 158 158 .show = monitor_reactor_show 159 159 }; 160 160 161 - static void monitor_swap_reactors(struct rv_monitor_def *mdef, struct rv_reactor_def *rdef, 162 - bool reacting) 161 + static void monitor_swap_reactors_single(struct rv_monitor_def *mdef, 162 + struct rv_reactor_def *rdef, 163 + bool reacting, bool nested) 163 164 { 164 165 bool monitor_enabled; 165 166 ··· 180 179 mdef->reacting = reacting; 181 180 mdef->monitor->react = rdef->reactor->react; 182 181 183 - if (monitor_enabled) 182 + /* enable only once if iterating through a container */ 183 + if (monitor_enabled && !nested) 184 184 rv_enable_monitor(mdef); 185 + } 186 + 187 + static void monitor_swap_reactors(struct rv_monitor_def *mdef, 188 + struct rv_reactor_def *rdef, bool reacting) 189 + { 190 + struct rv_monitor_def *p = mdef; 191 + 192 + if (rv_is_container_monitor(mdef)) 193 + list_for_each_entry_continue(p, &rv_monitors_list, list) { 194 + if (p->parent != mdef->monitor) 195 + break; 196 + monitor_swap_reactors_single(p, rdef, reacting, true); 197 + } 198 + /* 199 + * This call enables and disables the monitor if they were active. 200 + * In case of a container, we already disabled all and will enable all. 201 + * All nested monitors are enabled also if they were off, we may refine 202 + * this logic in the future. 203 + */ 204 + monitor_swap_reactors_single(mdef, rdef, reacting, false); 185 205 } 186 206 187 207 static ssize_t