Skip to contentSkip to conversation

rv/reactors: fix lockdep warning and add tests

PATCH Superseded [email protected] 3 participants 12 messages Sep 6 – Sep 15

Subsystems (3)

v5 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v5 cover rv/reactors: fix lockdep warning and add tests

From: Wen Yang <[email protected]>

We occasionally hit a lockdep "Invalid wait context" warning in production when a reactor callback is preempted by a timer interrupt. On interrupt exit the scheduler takes rq->__lock (LD_WAIT_SPIN) while rv_react() still holds its wait-type-override map, which declared LD_WAIT_FREE. On any kernel where the task context has preemption enabled (not just CONFIG_PREEMPT_RT) this triggers a spurious lockdep report:

[ BUG: Invalid wait context ]
1 lock held by kunit_try_catch/209:
#0: (rv_react_map-wait-type-override){+.+.}-{1:1}
kunit_try_catch/209 is trying to lock:
ffff8a743ed3e8a0 (&rq->__lock){-...}-{2:2}

This series fixes the wait type (patch 1), makes reactor registration failures propagate (patch 2), adds support for module-based reactors (patch 3), and adds KUnit/kselftest coverage (patch 4-5).

Changes in v5:
- Patch 1: the locking rules no longer go to Documentation/trace/rv/monitor_synthesis.rst, which is not about reactors (Gabriele); they are documented in code comments. Functional code unchanged.
- Patch 2: unchanged
- Patch 3: redesigned. v4 only exported the registration helpers, which is harmless while all in-tree reactors are built-in, but would let a loudable reactor module be unloaded while a monitor still refereances its react() callback. Per the suggestion in commit 3d3800b4f7 ("rv: Remove rv_reactor's reference counter"). A new selftest (patch 5) exercises it.
- Patch 4: The test callback is kept busy for two timer ticks so the test also passes with HZ < 200.
- Patch 5: a selftest module that registers a reactor exercises the module pinni g end to end: rmmod must fail while a monitor is attached.

v4: https://lore.kernel.org/lkml/[email protected]/
v3: https://lore.kernel.org/lkml/[email protected]/
v2: https://lore.kernel.org/lkml/[email protected]/
v1: https://lore.kernel.org/lkml/[email protected]/

Wen Yang (5):
rv/reactors: use LD_WAIT_SPIN as the reactor lockdep wait type rv/reactors: propagate rv_register_reactor() error from reactor init rv/reactors: export rv_register_reactor() and rv_unregister_reactor() rv/reactors: add KUnit tests for reactor registration and dispatch selftests/verification: Test loadable module-based reactor

 include/linux/rv.h                            |   3 +
 kernel/trace/rv/Kconfig                       |  12 ++
 kernel/trace/rv/Makefile                      |   1 +
 kernel/trace/rv/reactor_panic.c               |   3 +-
 kernel/trace/rv/reactor_printk.c              |   3 +-
 kernel/trace/rv/rv.c                          |   5 +
 kernel/trace/rv/rv_reactors.c                 |  48 ++++++--
 kernel/trace/rv/rv_reactors_kunit.c           | 110 ++++++++++++++++++
 tools/testing/selftests/verification/Makefile |   1 +
 tools/testing/selftests/verification/config   |   2 +
 .../test.d/rv_reactor_loadable.tc             |  46 ++++++++
 .../verification/test_modules/Makefile        |  16 +++
 .../test_modules/rv_test_reactor.c            |  37 ++++++
 13 files changed, 276 insertions(+), 11 deletions(-)
 create mode 100644 kernel/trace/rv/rv_reactors_kunit.c
 create mode 100644 tools/testing/selftests/verification/test.d/rv_reactor_loadable.tc
 create mode 100644 tools/testing/selftests/verification/test_modules/Makefile
 create mode 100644 tools/testing/selftests/verification/test_modules/rv_test_reactor.c

base-commit: 785095112f4198de49760552374f364043c8dbdf

Signature · 1 line
2.25.1
#1 lore raw

PATCH v5 1/5 rv/reactors: use LD_WAIT_SPIN as the reactor lockdep wait type

From: Wen Yang <[email protected]>

rv_react() overrides the lockdep wait type to LD_WAIT_FREE to enforce that reactor callbacks take no locks. But callbacks run in the context of the triggering tracepoint, which can be preemptible task context on any kernel. A timer interrupt firing during the callback makes the interrupt-exit path schedule and take rq->__lock (LD_WAIT_SPIN) while the LD_WAIT_FREE override is still held, producing a spurious "Invalid wait context" warning:

[ BUG: Invalid wait context ] context-{5:5}
1 lock held by kunit_try_catch/209:
#0: (rv_react_map-wait-type-override){+.+.}-{1:1}
kunit_try_catch/209 is trying to lock:
ffff8a743ed3e8a0 (&rq->__lock){-...}-{2:2}

Use LD_WAIT_SPIN instead of LD_WAIT_FREE, which causes false-positive warnings in preemptible contexts due to scheduler preemption taking rq->__lock. Add documentation to runtime-verification.rst.

Fixes 69d8895cb9a9 ("rv: Add explicit lockdep context for reactors")
Cc Thomas Weißschuh <[email protected]>
---
 kernel/trace/rv/rv_reactors.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
kernel/trace/rv/rv_reactors.c +9 -1
index 2f5fc8d18dea..ff7d478227c3 100644
@@ -465,7 +465,15 @@ int init_rv_reactors(struct dentry *root_dir)
 
 void rv_react(struct rv_monitor *monitor, const char *msg, ...)
 {
-	static DEFINE_WAIT_OVERRIDE_MAP(rv_react_map, LD_WAIT_FREE);
+	/*
+	 * Reactors must not explicitly take locks, so they should be
+	 * LD_WAIT_FREE.  However, reactor callbacks can run with preemption
+	 * enabled, meaning the preempting code (e.g. the scheduler taking
+	 * rq->__lock at LD_WAIT_SPIN) may violate that constraint.  Use
+	 * LD_WAIT_SPIN to avoid false-positive lockdep reports.
+	 * But you should still NOT be using locks in reactors.
+	 */
+	static DEFINE_WAIT_OVERRIDE_MAP(rv_react_map, LD_WAIT_SPIN);
 	va_list args;
 
 	if (!rv_reacting_on() || !monitor->react)
Signature · 1 line
2.25.1
#2 lore raw

Re: [PATCH v5 1/5] rv/reactors: use LD_WAIT_SPIN as the reactor lockdep wait type

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] The commit message claims to add documentation, but the patch does not modify any documentation…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The commit message claims to add documentation, but the patch does not modify any documentation files. --

commit 9f88f295cb2517cf173e24803d40575bacb352d6
Author: Wen Yang <[email protected]>

rv/reactors: use LD_WAIT_SPIN as the reactor lockdep wait type

This patch changes the reactor lockdep wait type override from LD_WAIT_FREE to LD_WAIT_SPIN to prevent false-positive "Invalid wait context" warnings. This resolves issues when preempting code, like the scheduler, takes spinlocks while a reactor callback is running.

Quoted 3 lines

Use LD_WAIT_SPIN instead of LD_WAIT_FREE, which causes false-positive warnings in preemptible contexts due to scheduler preemption taking rq->__lock. Add documentation to runtime-verification.rst.

[Severity: Low]
The commit message explicitly claims to add documentation to runtime-verification.rst to describe the LD_WAIT_SPIN logic change made in rv_react() (kernel/trace/rv/rv_reactors.c).

However, looking at the patch, no documentation files are actually modified.

Was the documentation update for this change accidentally dropped from the commit?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
#3 lore raw

PATCH v5 2/5 rv/reactors: propagate rv_register_reactor() error from reactor init

In reply to [email protected]

From: Wen Yang <[email protected]>

Both register_react_printk() and register_react_panic() ignore the return value of rv_register_reactor() and always return 0. If the registration fails (e.g. a duplicate reactor name), the init functions silently report success even though the reactor was not registered.

Propagate the error from rv_register_reactor() so a failed registration is reported instead of being silently ignored.

---
 kernel/trace/rv/reactor_panic.c  | 3 +--
 kernel/trace/rv/reactor_printk.c | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)
kernel/trace/rv/reactor_panic.c +1 -2
index 76537b8a4343..db7116ceafff 100644
@@ -26,8 +26,7 @@ static struct rv_reactor rv_panic = {
 
 static int __init register_react_panic(void)
 {
-	rv_register_reactor(&rv_panic);
-	return 0;
+	return rv_register_reactor(&rv_panic);
 }
 
 static void __exit unregister_react_panic(void)
kernel/trace/rv/reactor_printk.c +1 -2
index 48c934e315b3..002a10f6aa7b 100644
@@ -25,8 +25,7 @@ static struct rv_reactor rv_printk = {
 
 static int __init register_react_printk(void)
 {
-	rv_register_reactor(&rv_printk);
-	return 0;
+	return rv_register_reactor(&rv_printk);
 }
 
 static void __exit unregister_react_printk(void)
Signature · 1 line
2.25.1
#4 lore raw

PATCH v5 3/5 rv/reactors: export rv_register_reactor() and rv_unregister_reactor()

In reply to [email protected]

From: Wen Yang <[email protected]>

rv_react() is exported to modules, but the reactor registration helpers are not. Export them with EXPORT_SYMBOL_GPL() so reactor modules and the tristate KUnit test module can register and unregister reactors without hitting undefined symbol errors at link time(modpost).

Commit 3d3800b4f7f4 ("rv: Remove reactor's reference counter") noted that if module-based reactors are supported, try_module_get()/module_put() should be used. Add struct module *owner to struct rv_reactor so a module cat set owner = THIS_MODULE; pin the module in monitor_swap_reactors_gingle() and release it when a monitor detaches or is unregistered.

In-tree reactors leave owner = NULL and are unaffected.

---
 include/linux/rv.h            |  3 +++
 kernel/trace/rv/rv.c          |  5 +++++
 kernel/trace/rv/rv_reactors.c | 38 +++++++++++++++++++++++++++++------
 3 files changed, 40 insertions(+), 6 deletions(-)
include/linux/rv.h +3 -0
index 541ba404926a..ff3289ba4f02 100644
@@ -128,10 +128,13 @@ union rv_task_monitor {
 };
 
 #ifdef CONFIG_RV_REACTORS
+struct module;
+
 struct rv_reactor {
 	const char		*name;
 	const char		*description;
 	__printf(1, 0) void	(*react)(const char *msg, va_list args);
+	struct module		*owner;
 	struct list_head	list;
 };
 #endif
kernel/trace/rv/rv.c +5 -0
index 29f155c6968b..458b17c005b3 100644
@@ -803,6 +803,11 @@ int rv_unregister_monitor(struct rv_monitor *monitor)
 	guard(mutex)(&rv_interface_lock);
 
 	rv_disable_monitor(monitor);
+#ifdef CONFIG_RV_REACTORS
+	if (monitor->reactor)
+		module_put(monitor->reactor->owner);
+
+#endif
 	list_del(&monitor->list);
 	destroy_monitor_dir(monitor);
 
kernel/trace/rv/rv_reactors.c +32 -6
index ff7d478227c3..136eb7f47c4a 100644
@@ -62,6 +62,7 @@
  */
 
 #include <linux/lockdep.h>
+#include <linux/module.h>
 #include <linux/slab.h>
 
 #include "rv.h"
@@ -159,7 +160,7 @@ static const struct seq_operations monitor_reactors_seq_ops = {
 	.show	= monitor_reactor_show
 };
 
-static void monitor_swap_reactors_single(struct rv_monitor *mon,
+static int monitor_swap_reactors_single(struct rv_monitor *mon,
 					 struct rv_reactor *reactor,
 					 bool nested)
 {
@@ -167,29 +168,39 @@ static void monitor_swap_reactors_single(struct rv_monitor *mon,
 
 	/* nothing to do */
 	if (mon->reactor == reactor)
-		return;
+		return 0;
+
+	if (reactor->owner && !try_module_get(reactor->owner))
+		return -EBUSY;
 
 	monitor_enabled = mon->enabled;
 	if (monitor_enabled)
 		rv_disable_monitor(mon);
 
+	if (mon->reactor)
+		module_put(mon->reactor->owner);
 	mon->reactor = reactor;
 	mon->react = reactor->react;
 
 	/* enable only once if iterating through a container */
 	if (monitor_enabled && !nested)
 		rv_enable_monitor(mon);
+
+	return 0;
 }
 
-static void monitor_swap_reactors(struct rv_monitor *mon, struct rv_reactor *reactor)
+static int monitor_swap_reactors(struct rv_monitor *mon, struct rv_reactor *reactor)
 {
 	struct rv_monitor *p = mon;
+	int ret;
 
 	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);
+			ret = monitor_swap_reactors_single(p, reactor, true);
+			if (ret)
+				return ret;
 		}
 	/*
 	 * This call enables and disables the monitor if they were active.
@@ -197,7 +208,7 @@ static void monitor_swap_reactors(struct rv_monitor *mon, struct rv_reactor *rea
 	 * 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);
+	return monitor_swap_reactors_single(mon, reactor, false);
 }
 
 static ssize_t
@@ -236,10 +247,14 @@ monitor_reactors_write(struct file *file, const char __user *user_buf,
 	guard(mutex)(&rv_interface_lock);
 
 	list_for_each_entry(reactor, &rv_reactors_list, list) {
+		int ret;
+
 		if (strcmp(ptr, reactor->name) != 0)
 			continue;
 
-		monitor_swap_reactors(mon, reactor);
+		ret = monitor_swap_reactors(mon, reactor);
+		if (ret)
+			return ret;
 
 		return count;
 	}
@@ -314,6 +329,7 @@ int rv_register_reactor(struct rv_reactor *reactor)
 	guard(mutex)(&rv_interface_lock);
 	return __rv_register_reactor(reactor);
 }
+EXPORT_SYMBOL_GPL(rv_register_reactor);
 
 /**
  * rv_unregister_reactor - unregister a rv reactor.
@@ -327,6 +343,7 @@ int rv_unregister_reactor(struct rv_reactor *reactor)
 	list_del(&reactor->list);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(rv_unregister_reactor);
 
 /*
  * reacting_on interface.
@@ -421,6 +438,15 @@ int reactor_populate_monitor(struct rv_monitor *mon, struct dentry *root)
 	 * Configure as the rv_nop reactor.
 	 */
 	mon->reactor = get_reactor_rdef_by_name("nop");
+	if (WARN_ON(!mon->reactor)) {
+		rv_remove(tmp);
+		return -EINVAL;
+	}
+
+	if (mon->reactor->owner && !try_module_get(mon->reactor->owner)) {
+		rv_remove(tmp);
+		return -EBUSY;
+	}
 
 	return 0;
 }
Signature · 1 line
2.25.1
maintainer
#5 lore raw

Re: [PATCH v5 3/5] rv/reactors: export rv_register_reactor() and rv_unregister_reactor()

Quoted 7 lines · On Mon, 2026-09-07 at 01:10 +0800, [email protected] wrote:

From: Wen Yang <[email protected]>

rv_react() is exported to modules, but the reactor registration helpers are not.  Export them with EXPORT_SYMBOL_GPL() so reactor modules and the tristate KUnit test module can register and unregister reactors without hitting undefined symbol errors at link time(modpost).

Quoted 5 lines

Commit 3d3800b4f7f4 ("rv: Remove reactor's reference counter") noted that if module-based reactors are supported, try_module_get()/module_put() should be used. Add struct module *owner to struct rv_reactor so a module cat set owner = THIS_MODULE; pin the module in monitor_swap_reactors_gingle() and release it when a monitor detaches or is unregistered.

You needed these symbols in KUnit and we are exporting them for /potential/ future support of reactors as modules. I don't see any technical reason why we shouldn't support this, but they are currently /not/ supported.

I know sashiko and other LLMs complain about this, and they have a point, but you can ignore them. At most state in this commit message that this does NOT add support for reactors as modules.

Let's focus this series on its original intent (fix a lockdep warning and add some KUnit tests that expose a reproducer), then if adding support for reactors as modules is so simple, you can do it in another series.

If you really want to /also/ add support for reactors as modules in this series, you need to make that very explicit (not just a vague line in the changelog, but rather rewrite the entire cover letter and commit message).
And mind that this would mean your series needs to go through another round of review and serious testing: you are adding a new feature.

Quoted 3 lines

In-tree reactors leave owner = NULL and are unaffected.

Please, whenever you significantly change an already reviewed patch, remove the reviewed-by, so I can quickly see I need to review it again.

Thanks,
Gabriele

Quoted 170 lines

---
 include/linux/rv.h            |  3 +++
 kernel/trace/rv/rv.c          |  5 +++++ kernel/trace/rv/rv_reactors.c | 38 +++++++++++++++++++++++++++++------

 3 files changed, 40 insertions(+), 6 deletions(-)
include/linux/rv.h +0 -0
index 541ba404926a..ff3289ba4f02 100644
@@ -128,10 +128,13 @@ union rv_task_monitor {
  };


 #ifdef CONFIG_RV_REACTORS

include/linux/rv.h +2 -0
+struct module;
+

 struct rv_reactor {
  const char *name;
  const char *description;
  __printf(1, 0) void (*react)(const char *msg, va_list args);

include/linux/rv.h +1 -0
+	struct module		*owner;

  struct list_head list;
 };
 #endif

kernel/trace/rv/rv.c +0 -0
index 29f155c6968b..458b17c005b3 100644
@@ -803,6 +803,11 @@ int rv_unregister_monitor(struct rv_monitor *monitor)
  	guard(mutex)(&rv_interface_lock);


  rv_disable_monitor(monitor);

kernel/trace/rv/rv.c +5 -0
+#ifdef CONFIG_RV_REACTORS
+	if (monitor->reactor)
+		module_put(monitor->reactor->owner);
+
+#endif

  list_del(&monitor->list);
  destroy_monitor_dir(monitor);

kernel/trace/rv/rv_reactors.c +0 -0
index ff7d478227c3..136eb7f47c4a 100644
@@ -62,6 +62,7 @@
   */


 #include <linux/lockdep.h>
+#include <linux/module.h>
 #include <linux/slab.h>

 #include "rv.h"

kernel/trace/rv/rv_reactors.c +0 -0
@@ -159,7 +160,7 @@ static const struct seq_operations
 monitor_reactors_seq_ops = {

  .show = monitor_reactor_show
 };

kernel/trace/rv/rv_reactors.c +1 -1
-static void monitor_swap_reactors_single(struct rv_monitor *mon,
+static int monitor_swap_reactors_single(struct rv_monitor *mon,

  struct rv_reactor *reactor, bool nested)
 {

kernel/trace/rv/rv_reactors.c +0 -0
@@ -167,29 +168,39 @@ static void monitor_swap_reactors_single(struct
 rv_monitor *mon,


  /* nothing to do */
  if (mon->reactor == reactor)

kernel/trace/rv/rv_reactors.c +4 -1
-		return;
+		return 0;
+
+	if (reactor->owner && !try_module_get(reactor->owner))
+		return -EBUSY;


  monitor_enabled = mon->enabled;
  if (monitor_enabled)
  rv_disable_monitor(mon);

kernel/trace/rv/rv_reactors.c +2 -0
+	if (mon->reactor)
+		module_put(mon->reactor->owner);

  mon->reactor = reactor;
  mon->react = reactor->react;

  /* enable only once if iterating through a container */ if (monitor_enabled && !nested) rv_enable_monitor(mon);

kernel/trace/rv/rv_reactors.c +2 -0
+
+	return 0;

 }

-static void monitor_swap_reactors(struct rv_monitor *mon, struct rv_reactor *reactor)
+static int monitor_swap_reactors(struct rv_monitor *mon, struct rv_reactor *reactor)
 {
  struct rv_monitor *p = mon;

kernel/trace/rv/rv_reactors.c +1 -0
+	int ret;


  if (rv_is_container_monitor(mon)) list_for_each_entry_continue(p, &rv_monitors_list, list) { if (p->parent != mon)
  break;

kernel/trace/rv/rv_reactors.c +3 -1
-			monitor_swap_reactors_single(p, reactor, true);
+			ret = monitor_swap_reactors_single(p, reactor, true);
+			if (ret)
+				return ret;

  }
  /*
  * This call enables and disables the monitor if they were active.

kernel/trace/rv/rv_reactors.c +0 -0
@@ -197,7 +208,7 @@ static void monitor_swap_reactors(struct rv_monitor *mon,

struct rv_reactor *rea
  * All nested monitors are enabled also if they were off, we may refine
  * this logic in the future.
  */

kernel/trace/rv/rv_reactors.c +1 -1
-	monitor_swap_reactors_single(mon, reactor, false);
+	return monitor_swap_reactors_single(mon, reactor, false);

 }

 static ssize_t

kernel/trace/rv/rv_reactors.c +0 -0
@@ -236,10 +247,14 @@ monitor_reactors_write(struct file *file, const char
 __user *user_buf,
  	guard(mutex)(&rv_interface_lock);


  list_for_each_entry(reactor, &rv_reactors_list, list) {

kernel/trace/rv/rv_reactors.c +2 -0
+		int ret;
+

  if (strcmp(ptr, reactor->name) != 0) continue;

kernel/trace/rv/rv_reactors.c +3 -1
-		monitor_swap_reactors(mon, reactor);
+		ret = monitor_swap_reactors(mon, reactor);
+		if (ret)
+			return ret;


  return count;
  }

kernel/trace/rv/rv_reactors.c +1 -0
@@ -314,6 +329,7 @@ int rv_register_reactor(struct rv_reactor *reactor)
  	guard(mutex)(&rv_interface_lock);
  	return __rv_register_reactor(reactor);
  }
+EXPORT_SYMBOL_GPL(rv_register_reactor);


 /**
  * rv_unregister_reactor - unregister a rv reactor.

kernel/trace/rv/rv_reactors.c +1 -0
@@ -327,6 +343,7 @@ int rv_unregister_reactor(struct rv_reactor *reactor)
  	list_del(&reactor->list);
  	return 0;
  }
+EXPORT_SYMBOL_GPL(rv_unregister_reactor);


 /*
  * reacting_on interface.

kernel/trace/rv/rv_reactors.c +9 -0
@@ -421,6 +438,15 @@ int reactor_populate_monitor(struct rv_monitor *mon,
 struct dentry *root)
  	 * Configure as the rv_nop reactor.
  	 */
  	mon->reactor = get_reactor_rdef_by_name("nop");
+	if (WARN_ON(!mon->reactor)) {
+		rv_remove(tmp);
+		return -EINVAL;
+	}
+
+	if (mon->reactor->owner && !try_module_get(mon->reactor->owner)) {
+		rv_remove(tmp);
+		return -EBUSY;
+	}


  return 0;
 }

author
#6 lore raw

Re: [PATCH v5 3/5] rv/reactors: export rv_register_reactor() and rv_unregister_reactor()

Quoted 26 lines · Gabriele Monaco
Quoted 7 lines · On Mon, 2026-09-07 at 01:10 +0800, [email protected] wrote:

From: Wen Yang <[email protected]>

rv_react() is exported to modules, but the reactor registration helpers are not.  Export them with EXPORT_SYMBOL_GPL() so reactor modules and the tristate KUnit test module can register and unregister reactors without hitting undefined symbol errors at link time(modpost).

Quoted 5 lines

Commit 3d3800b4f7f4 ("rv: Remove reactor's reference counter") noted that if module-based reactors are supported, try_module_get()/module_put() should be used. Add struct module *owner to struct rv_reactor so a module cat set owner = THIS_MODULE; pin the module in monitor_swap_reactors_gingle() and release it when a monitor detaches or is unregistered.

You needed these symbols in KUnit and we are exporting them for /potential/ future support of reactors as modules. I don't see any technical reason why we shouldn't support this, but they are currently /not/ supported.

I know sashiko and other LLMs complain about this, and they have a point, but you can ignore them. At most state in this commit message that this does NOT add support for reactors as modules.

Let's focus this series on its original intent (fix a lockdep warning and add some KUnit tests that expose a reproducer), then if adding support for reactors as modules is so simple, you can do it in another series.

Quoted 6 lines

If you really want to /also/ add support for reactors as modules in this series, you need to make that very explicit (not just a vague line in the changelog, but rather rewrite the entire cover letter and commit message).
And mind that this would mean your series needs to go through another round of review and serious testing: you are adding a new feature.

Thanks for the review, Gabriele, I agree with your points and will follow them.

Just to clarify the background : the struct module * field and the try_module_get()/module_put() logic were added to address the point raised by sashiko. After this change sashiko no longer complains about it.

The module-based reactor support, together with its kselftest, will be send as a separate series in a few days.

As a side note, we are looking into using the rv framework for functional safety in autonomous driving. Depending on how internal validation progress, there will likely be further changes to the rv subsystem in the near future. I'll be glad to discuss them as they come.

--
Best wishes,
Wen

maintainer
#7 lore raw

Re: [PATCH v5 3/5] rv/reactors: export rv_register_reactor() and rv_unregister_reactor()

Quoted 6 lines · Wen Yang

Thanks for the review, Gabriele, I agree with your points and will follow them.

Just to clarify the background : the struct module * field and the try_module_get()/module_put() logic were added to address the point raised by sashiko. After this change sashiko no longer complains about it.

Yeah I imagined it, sashiko has a point, whatever is EXPORTed can be used by modules, so it may imply modules are supported.
What sashiko ignores is maintainability requirements.
A human is reviewing anyone trying to use reactors as modules, and adding that change without properly advertising it may do more harm than good.

Quoted 2 lines

The module-based reactor support, together with its kselftest, will be send as a separate series in a few days.

Sounds good, so I can review and test it as it deserves.

Quoted 4 lines

As a side note, we are looking into using the rv framework for functional safety in autonomous driving.  Depending on how internal validation progress, there will likely be further changes to the rv subsystem in the near future. I'll be glad to discuss them as they come.

Nice, thanks for sharing, I had a few conversations about RV in functional safety domains over the past weeks, I now better understand the challenges. It'd be very interesting to discuss your requirements too.

Thanks,
Gabriele

#8 lore raw

PATCH v5 4/5 rv/reactors: add KUnit tests for reactor registration and dispatch

In reply to [email protected]

From: Wen Yang <[email protected]>

Add KUnit tests covering the reactor register/unregister lifecycle (including duplicate and name-length rejection) and rv_react() dispatch (a no-op without a callback, exactly one invocation with one). The mdelay() callback keeps the CPU busy so a timer interrupt lands inside rv_react()'s lockdep context, exercising the LD_WAIT_SPIN wait type from the previous patch; a spurious lockdep splat there would show up in the test output.

The dispatch tests rely o reacting_on being enabled, since rv_react() returns early when it if off.

---
 kernel/trace/rv/Kconfig             |  12 +++
 kernel/trace/rv/Makefile            |   1 +
 kernel/trace/rv/rv_reactors_kunit.c | 110 ++++++++++++++++++++++++++++
 3 files changed, 123 insertions(+)
 create mode 100644 kernel/trace/rv/rv_reactors_kunit.c
kernel/trace/rv/Kconfig +12 -0
index efa930f94ea4..9bfd429ffdea 100644
@@ -113,6 +113,18 @@ config RV_REACT_PANIC
 	  Enables the panic reactor. The panic reactor emits a printk()
 	  message if an exception is found and panic()s the system.
 
+config RV_REACTORS_KUNIT
+	tristate "KUnit tests for RV reactors" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	depends on RV_REACTORS
+	default KUNIT_ALL_TESTS
+	help
+	  Enable KUnit tests for RV reactor registration and dispatch.
+	  These tests verify the register/unregister lifecycle, duplicate
+	  rejection, and that rv_react() correctly invokes callbacks.
+
+	  If unsure, say N.
+
 config RV_MONITORS_KUNIT_TEST
 	tristate "KUnit tests for RV monitors" if !KUNIT_ALL_TESTS
 	depends on KUNIT && RV && RV_REACTORS
kernel/trace/rv/Makefile +1 -0
index cdbf68c84f5a..c895d81dfdad 100644
@@ -25,4 +25,5 @@ obj-$(CONFIG_RV_MON_WAKEUP) += monitors/wakeup/wakeup.o
 obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
 obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
 obj-$(CONFIG_RV_REACT_PANIC) += reactor_panic.o
+obj-$(CONFIG_RV_REACTORS_KUNIT) += rv_reactors_kunit.o
 obj-$(CONFIG_RV_MONITORS_KUNIT_TEST) += rv_monitors_test.o
kernel/trace/rv/rv_reactors_kunit.c new file +110 -0

110 lines not on this page · raw message

Signature · 1 line
2.25.1
#9 lore raw

Re: [PATCH v5 4/5] rv/reactors: add KUnit tests for reactor registration and dispatch

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] Typos in commit message and comments, and non-standard use of _Static_assert. --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Typos in commit message and comments, and non-standard use of _Static_assert. --

commit a14edcfd6c547b97a3d6e82ee3b87a759234cdc5
Author: Wen Yang <[email protected]>

rv/reactors: add KUnit tests for reactor registration and dispatch

This commit adds KUnit tests to cover the reactor register and unregister lifecycle, including duplicate and name-length rejection. It also tests the rv_react() dispatch logic.

Quoted 2 lines

The dispatch tests rely o reacting_on being enabled, since rv_react() returns early when it if off.

[Severity: Low]
There appear to be a few typos in the commit message: "rely o" instead of "rely on", and "it if off" instead of "it is off".

[ ... ]

Quoted 6 lines
kernel/trace/rv/rv_reactors_kunit.c new file +0 -0
new file mode 100644
index 0000000000000..a408edcdde04f

kernel/trace/rv/rv_reactors_kunit.c line 9

Quoted 9 lines
kernel/trace/rv/rv_reactors_kunit.c new file +9 -0
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for RV reactor registration and dispatch.
+ *
+ * The dispatch tests rely on reacting_on beinng enabled, since rv_react()
+ * returns early when it is off. It is on by default when the suites run
+ * built-in; as a module, re-enable it if disabled via
+ * /sys/kerne/tracing/rv/reacting_on.
+ */

[Severity: Low]
There are a couple of typos in this file header comment: "beinng" instead of "being", and "/sys/kerne/" instead of "/sys/kernel/".

[ ... ]

kernel/trace/rv/rv_reactors_kunit.c line 43

Quoted 5 lines
kernel/trace/rv/rv_reactors_kunit.c new file +5 -0
+static const char long_reactor_name[] = "kunit_reactor_name_too_long_xxx_";
+_Static_assert(sizeof(long_reactor_name) - 1 >= MAX_RV_REACTOR_NAME_SIZE,
+	       "long_reactor_name must be at least MAX_RV_REACTOR_NAME_SIZE chars");
+
+static void test_name_too_long(struct kunit *test)

[Severity: Low]
Does this code use the C11 keyword _Static_assert directly rather than the kernel's standard static_assert macro? The kernel typically wraps this in include/linux/build_bug.h as static_assert(), which is the standard convention for compile-time assertions across the tree.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
#10 lore raw

PATCH v5 5/5 selftests/verification: Test loadable module-based reactor

In reply to [email protected]

From: Wen Yang <[email protected]>

Add a selftest module that register an RV reactor and a test that exercises the module pinning: rmmod must fail while a monitor is attached to the reactor and succeed after it is detached. A trap unloads the module on failure so it does not break the next insmod.

THe module is built through the kselftest TEST_GEN_MODS_DIR mechanism, like the livepatch selftests.

---
 tools/testing/selftests/verification/Makefile |  1 +
 tools/testing/selftests/verification/config   |  2 +
 .../test.d/rv_reactor_loadable.tc             | 46 +++++++++++++++++++
 .../verification/test_modules/Makefile        | 16 +++++++
 .../test_modules/rv_test_reactor.c            | 37 +++++++++++++++
 5 files changed, 102 insertions(+)
 create mode 100644 tools/testing/selftests/verification/test.d/rv_reactor_loadable.tc
 create mode 100644 tools/testing/selftests/verification/test_modules/Makefile
 create mode 100644 tools/testing/selftests/verification/test_modules/rv_test_reactor.c
tools/testing/selftests/verification/Makefile +1 -0
index aa8790c22a71..7ff7382d11f0 100644
@@ -3,6 +3,7 @@ all:
 
 TEST_PROGS := verificationtest-ktap
 TEST_FILES := test.d settings
+TEST_GEN_MODS_DIR := test_modules
 EXTRA_CLEAN := $(OUTPUT)/logs/*
 
 include ../lib.mk
tools/testing/selftests/verification/config +2 -0
index 43072c1c38f4..ddd7581f07b9 100644
@@ -1 +1,3 @@
 CONFIG_RV=y
+CONFIG_MODULES=y
+CONFIG_MODULES_UNLOAD=y
tools/testing/selftests/verification/test.d/rv_reactor_loadable.tc new file +46 -0

46 lines not on this page · raw message

tools/testing/selftests/verification/test_modules/Makefile new file +16 -0
new file mode 100644
index 000000000000..0af95207a9a9
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+
+TESTMODS_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
+KDIR ?= /lib/modules/$(shell uname -r)/build
+
+obj-m += rv_test_reactor.o
+
+modules:
+ifneq ("$(wildcard $(KDIR))", "")
+	$(Q)$(MAKE) -C $(KDIR) modules KBUILD_EXTMOD=$(TESTMODS_DIR)
+endif
+
+clean:
+ifneq ("$(wildcard $(KDIR))", "")
+	$(Q)$(MAKE) -C $(KDIR) clean KBUILD_EXTMOD=$(TESTMODS_DIR)
+endif
tools/testing/selftests/verification/test_modules/rv_test_reactor.c new file +37 -0
new file mode 100644
index 000000000000..a243df81fa52
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Loadable RV reactor for the verification selftests. Register a
+ * reactor with owner = THIS_MODULE so the selftests can exercise the
+ * module pinning: unloading is refused while a monitor is attached
+ * to the reactor.
+ */
+
+#include <linux/module.h>
+#include <linux/rv.h>
+
+__printf(1, 0) static void rv_test_reaction(const char *msg, va_list args)
+{
+}
+
+static struct rv_reactor rv_test_reactor = {
+	.name = "test_reactor",
+	.description = "selftest reactor: exercise module-based reactors.",
+	.react = rv_test_reaction,
+	.owner = THIS_MODULE,
+};
+
+static int __init rv_test_reactor_init(void)
+{
+	return rv_register_reactor(&rv_test_reactor);
+}
+
+static void __exit rv_test_reactor_exit(void)
+{
+	rv_unregister_reactor(&rv_test_reactor);
+}
+
+module_init(rv_test_reactor_init);
+module_exit(rv_test_reactor_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Loadable RV reactor for verification selftests");
Signature · 1 line
2.25.1
#11 lore raw

Re: [PATCH v5 5/5] selftests/verification: Test loadable module-based reactor

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] Typo in the test cleanup trap prevents the script from properly detaching the reactor and…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Typo in the test cleanup trap prevents the script from properly detaching the reactor and unloading the module on test failure. --

commit fcfb27a313bc6b0b71755c80b19e1e2cbf231cac
Author: Wen Yang <[email protected]>

selftests/verification: Test loadable module-based reactor

Add a selftest module that register an RV reactor and a test that exercises the module pinning: rmmod must fail while a monitor is attached to the reactor and succeed after it is detached. A trap unloads the module on failure so it does not break the next insmod.

Quoted 9 lines
tools/testing/selftests/verification/test.d/rv_reactor_loadable.tc new file +3 -0
new file mode 100644
index 0000000000000..ff16916615215
@@ -0,0 +1,46 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# description: Test a loadable module-based reactor

tools/testing/selftests/verification/test.d/rv_reactor_loadable.tc line 13

Quoted 10 lines
tools/testing/selftests/verification/test.d/rv_reactor_loadable.tc new file +10 -0
+# requires: available_reactors insmod:program rmmod:program
+
+MODULE_KO="$FTRACETEST_ROOT/test_modules/rv_test_reactor.ko"
+[ -f "$MODULE_KO" ] || exit_unsupported
+[ -f /proc/modules ] || exit_unsupported
+monitor=$(ls monitors | head -n 1)
+[ -n "$monitor" ] || exit_unsupported
+
+cleanup() {
+	if grep -q '^test_reactors$' available_reactors; then

[Severity: Medium]
Is there a typo in this grep pattern? Should it be test_reactor instead of test_reactors?

If the test fails and triggers the cleanup trap, this condition will evaluate to false, bypassing the cleanup logic. This leaves the test module loaded and the reactor attached, which pollutes the test environment for subsequent selftests.

Quoted 4 lines
tools/testing/selftests/verification/test.d/rv_reactor_loadable.tc new file +4 -0
+		echo nop > "monitors/$monitor/reactors" || true
+		rmmod rv_test_reactor || true
+	fi
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5