[RFC PATCH v2 10/13] mm/kwatch: add anchor thread for global watchpoints
From: Jinchao Wang
Date: Fri Jul 17 2026 - 09:10:55 EST
Global variables have no function whose execution can bound the
watch window. Provide one: a kernel thread sleeps for the configured
duration inside a dedicated noinline function,
kwatch_global_anchor(), and the probe runtime hooks that function
like any other target.
When the duration expires the thread schedules a work item that
tears the session down; the expired flag is cleared under the
control-plane mutex so a stale work item from a previous session
cannot stop a new one.
Signed-off-by: Jinchao Wang <wangjinchao600@xxxxxxxxx>
---
mm/kwatch/Makefile | 2 +-
mm/kwatch/anchor.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 86 insertions(+), 1 deletion(-)
create mode 100644 mm/kwatch/anchor.c
diff --git a/mm/kwatch/Makefile b/mm/kwatch/Makefile
index f04673cc5b1c..b196c794619a 100644
--- a/mm/kwatch/Makefile
+++ b/mm/kwatch/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_KWATCH) += kwatch.o
-kwatch-y := deref.o task_ctx.o hwbp.o probe.o
+kwatch-y := deref.o task_ctx.o hwbp.o probe.o anchor.o
diff --git a/mm/kwatch/anchor.c b/mm/kwatch/anchor.c
new file mode 100644
index 000000000000..e87eb5e813ff
--- /dev/null
+++ b/mm/kwatch/anchor.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+
+#include "kwatch.h"
+
+static DECLARE_WAIT_QUEUE_HEAD(kwatch_anchor_wq);
+static struct task_struct *kwatch_anchor_tsk;
+static bool kwatch_anchor_expired;
+
+bool kwatch_anchor_has_expired(void)
+{
+ return READ_ONCE(kwatch_anchor_expired);
+}
+
+void kwatch_anchor_clear_expired(void)
+{
+ WRITE_ONCE(kwatch_anchor_expired, false);
+}
+
+static void kwatch_auto_stop_handler(struct work_struct *work)
+{
+ kwatch_auto_stop();
+}
+
+static DECLARE_WORK(kwatch_auto_stop_work, kwatch_auto_stop_handler);
+
+noinline void kwatch_global_anchor(unsigned long duration_sec)
+{
+ /* TASK_IDLE: a long timed sleep must not inflate loadavg or trip the
+ * hung-task detector the way TASK_UNINTERRUPTIBLE would.
+ */
+ wait_event_idle_timeout(kwatch_anchor_wq, kthread_should_stop(),
+ duration_sec * HZ);
+}
+
+static int kwatch_anchor_thread_fn(void *data)
+{
+ unsigned long duration = (unsigned long)data;
+
+ kwatch_global_anchor(duration);
+
+ if (!kthread_should_stop()) {
+ /* mark before scheduling; cleared under the control mutex */
+ WRITE_ONCE(kwatch_anchor_expired, true);
+ schedule_work(&kwatch_auto_stop_work);
+ }
+
+ while (!kthread_should_stop())
+ schedule_timeout_idle(HZ);
+
+ return 0;
+}
+
+int kwatch_anchor_start(u16 duration)
+{
+ kwatch_anchor_tsk = kthread_run(kwatch_anchor_thread_fn,
+ (void *)(unsigned long)duration,
+ "kwatch_anchor");
+ if (IS_ERR(kwatch_anchor_tsk)) {
+ int ret = PTR_ERR(kwatch_anchor_tsk);
+
+ kwatch_anchor_tsk = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+void kwatch_anchor_stop(void)
+{
+ if (kwatch_anchor_tsk) {
+ kthread_stop(kwatch_anchor_tsk);
+ kwatch_anchor_tsk = NULL;
+ }
+}
+
+void kwatch_anchor_cancel_work(void)
+{
+ cancel_work_sync(&kwatch_auto_stop_work);
+}
--
2.53.0