linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: Jinchao Wang <wangjinchao600@gmail.com>
To: akpm@linux-foundation.org
Cc: mhiramat@kernel.org, naveen@kernel.org, davem@davemloft.net,
	linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	linux-trace-kernel@vger.kernel.org,
	Jinchao Wang <wangjinchao600@gmail.com>
Subject: [RFC PATCH 05/13] mm/kstackwatch: Add atomic HWBP arm/disarm operations
Date: Mon, 18 Aug 2025 20:26:10 +0800	[thread overview]
Message-ID: <20250818122720.434981-6-wangjinchao600@gmail.com> (raw)
In-Reply-To: <20250818122720.434981-5-wangjinchao600@gmail.com>

Implement the critical atomic operations for dynamically arming and
disarming hardware breakpoints across all CPUs without allocation
overhead.

This patch adds the core functionality that enables kstackwatch to
operate in atomic contexts (such as kprobe handlers):

Key features:
1. ksw_watch_on() - Atomically arm breakpoints on all CPUs with
   specified address and length
2. ksw_watch_off() - Disarm all breakpoints by resetting to dummy marker
3. HWBP updates using arch_reinstall_hw_breakpoint()
4. SMP-safe coordination using work queues and async function calls

The implementation uses a hybrid approach for SMP coordination:
- Current CPU: Direct function call for immediate effect
- Other CPUs: Asynchronous smp_call_function_single_async() for
  non-blocking operation in queue worker

This enables the kprobe handlers (added in subsequent patches) to
instantly arm breakpoints on function entry and disarm on exit,
providing real-time stack corruption detection without the performance
penalties or atomic context limitations of traditional approaches.

Signed-off-by: Jinchao Wang <wangjinchao600@gmail.com>
---
 mm/kstackwatch/kstackwatch.h |   2 +
 mm/kstackwatch/watch.c       | 118 +++++++++++++++++++++++++++++++++++
 2 files changed, 120 insertions(+)

diff --git a/mm/kstackwatch/kstackwatch.h b/mm/kstackwatch/kstackwatch.h
index 256574cd9cb2..910f49014715 100644
--- a/mm/kstackwatch/kstackwatch.h
+++ b/mm/kstackwatch/kstackwatch.h
@@ -41,5 +41,7 @@ extern bool panic_on_catch;
 /* watch management */
 int ksw_watch_init(struct ksw_config *config);
 void ksw_watch_exit(void);
+int ksw_watch_on(u64 watch_addr, u64 watch_len);
+void ksw_watch_off(void);
 
 #endif /* _KSTACKWATCH_H */
diff --git a/mm/kstackwatch/watch.c b/mm/kstackwatch/watch.c
index 5cc2dfef140b..7ab247531961 100644
--- a/mm/kstackwatch/watch.c
+++ b/mm/kstackwatch/watch.c
@@ -1,4 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
+
+#include "linux/printk.h"
 #include <linux/kprobes.h>
 #include <linux/hw_breakpoint.h>
 #include <linux/perf_event.h>
@@ -11,11 +13,24 @@
 
 #include "kstackwatch.h"
 
+#define MAX_STACK_ENTRIES 64
+
 struct perf_event *__percpu *watch_events;
 struct ksw_config *watch_config;
+static DEFINE_SPINLOCK(watch_lock);
 
 static unsigned long long marker;
 
+struct watch_worker {
+	struct work_struct work;
+	int original_cpu;
+} myworker;
+
+static void ksw_watch_on_local_cpu(void *useless);
+
+static DEFINE_PER_CPU(call_single_data_t,
+		      hwbp_csd) = CSD_INIT(ksw_watch_on_local_cpu, NULL);
+
 /* Enhanced breakpoint handler with watch identification */
 static void ksw_watch_handler(struct perf_event *bp,
 			      struct perf_sample_data *data,
@@ -31,6 +46,59 @@ static void ksw_watch_handler(struct perf_event *bp,
 		panic("KSW: Stack corruption detected");
 }
 
+/* Setup single hardware breakpoint on current CPU */
+static void ksw_watch_on_local_cpu(void *useless)
+{
+	struct perf_event *bp;
+	int cpu = smp_processor_id();
+	int ret;
+
+	bp = *per_cpu_ptr(watch_events, cpu);
+	if (!bp)
+		return;
+
+	/* Update breakpoint address */
+	ret = hw_breakpoint_arch_parse(bp, &bp->attr, counter_arch_bp(bp));
+	if (ret) {
+		pr_err("KSW: Failed to parse HWBP for CPU %d ret %d\n", cpu,
+		       ret);
+		return;
+	}
+	ret = arch_reinstall_hw_breakpoint(bp);
+	if (ret) {
+		pr_err("KSW: Failed to install HWBP on CPU %d ret %d\n", cpu,
+		       ret);
+		return;
+	}
+
+	if (bp->attr.bp_addr == (unsigned long)&marker) {
+		pr_info("KSW: HWBP disarmed on CPU %d\n", cpu);
+	} else {
+		pr_info("KSW: HWBP armed on CPU %d at 0x%px (len %llu)\n", cpu,
+			(void *)bp->attr.bp_addr, bp->attr.bp_len);
+	}
+}
+
+static void ksw_watch_on_work_fn(struct work_struct *work)
+{
+	struct watch_worker *worker =
+		container_of(work, struct watch_worker, work);
+	int original_cpu = READ_ONCE(worker->original_cpu);
+	int local_cpu = smp_processor_id();
+	call_single_data_t *csd;
+	int cpu;
+
+	for_each_online_cpu(cpu) {
+		if (cpu == original_cpu)
+			continue;
+		if (cpu == local_cpu)
+			continue;
+		csd = &per_cpu(hwbp_csd, cpu);
+		smp_call_function_single_async(cpu, csd);
+	}
+	ksw_watch_on_local_cpu(NULL);
+}
+
 /* Initialize hardware breakpoint  */
 int ksw_watch_init(struct ksw_config *config)
 {
@@ -50,6 +118,8 @@ int ksw_watch_init(struct ksw_config *config)
 		return ret;
 	}
 
+	/* Initialize work structure */
+	INIT_WORK(&myworker.work, ksw_watch_on_work_fn);
 	watch_config = config;
 	pr_info("KSW: HWBP  initialized\n");
 	return 0;
@@ -63,3 +133,51 @@ void ksw_watch_exit(void)
 
 	pr_info("KSW: HWBP  cleaned up\n");
 }
+
+/* Legacy API: Arm single hardware breakpoint (backward compatibility) */
+int ksw_watch_on(u64 watch_addr, u64 watch_len)
+{
+	struct perf_event *bp;
+	unsigned long flags;
+	int cpu;
+
+	if (!watch_addr) {
+		pr_err("KSW: Invalid address for arming HWBP\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&watch_lock, flags);
+
+	/* Check if already armed - only need to check one CPU since all share same addr */
+	bp = *this_cpu_ptr(watch_events);
+	if (bp->attr.bp_addr != 0 &&
+	    bp->attr.bp_addr != (unsigned long)&marker && // installted
+	    watch_addr != (unsigned long)&marker) { //restore
+		spin_unlock_irqrestore(&watch_lock, flags);
+		return -EBUSY;
+	}
+
+	/* Update address in all minimal breakpoint structures */
+	for_each_possible_cpu(cpu) {
+		bp = *per_cpu_ptr(watch_events, cpu);
+		WRITE_ONCE(bp->attr.bp_addr, watch_addr);
+		WRITE_ONCE(bp->attr.bp_len, watch_len);
+	}
+
+	WRITE_ONCE(myworker.original_cpu, smp_processor_id());
+
+	spin_unlock_irqrestore(&watch_lock, flags);
+
+	/* Then install on all CPUs */
+	/* Run on current CPU directly */
+	queue_work(system_highpri_wq, &myworker.work);
+	ksw_watch_on_local_cpu(NULL);
+	return 0;
+}
+
+void ksw_watch_off(void)
+{
+	pr_info("KSW: Disarming all HWBPs\n");
+	ksw_watch_on((unsigned long)&marker, sizeof(marker));
+	pr_info("KSW: All HWBPs disarmed\n");
+}
-- 
2.43.0



  reply	other threads:[~2025-08-18 12:28 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-18 12:26 [RFC PATCH 00/13] mm: Introduce Kernel Stack Watch debugging tool Jinchao Wang
2025-08-18 12:26 ` [RFC PATCH 01/13] mm: Add kstackwatch build infrastructure Jinchao Wang
2025-08-18 12:26   ` [RFC PATCH 02/13] x86/HWBP: Add arch_reinstall_hw_breakpoint() for atomic updates Jinchao Wang
2025-08-18 12:26     ` [RFC PATCH 03/13] mm/kstackwatch: Add module core and configuration interface Jinchao Wang
2025-08-18 12:26       ` [RFC PATCH 04/13] mm/kstackwatch: Add HWBP pre-allocation infrastructure Jinchao Wang
2025-08-18 12:26         ` Jinchao Wang [this message]
2025-08-18 12:26           ` [RFC PATCH 06/13] mm/kstackwatch: Add stack address resolution functions Jinchao Wang
2025-08-18 12:26             ` [RFC PATCH 07/13] mm/kstackwatch: Add kprobe and stack watch control Jinchao Wang
2025-08-18 12:26               ` [RFC PATCH 08/13] mm/kstackwatch: Wire up watch and stack subsystems in module core Jinchao Wang
2025-08-18 12:26                 ` [RFC PATCH 09/13] mm/kstackwatch: Add architecture support validation Jinchao Wang
2025-08-18 12:26                   ` [RFC PATCH 10/13] mm/kstackwatch: Handle nested function calls Jinchao Wang
2025-08-18 12:26                     ` [RFC PATCH 11/13] mm/kstackwatch: Ignore corruption in kretprobe trampolines Jinchao Wang
2025-08-18 12:26                       ` [RFC PATCH 12/13] mm/kstackwatch: Add debug and test functions Jinchao Wang
2025-08-18 12:26                         ` [RFC PATCH 13/13] mm/kstackwatch: Add a test module and script Jinchao Wang
2025-08-25 10:31               ` [RFC PATCH 07/13] mm/kstackwatch: Add kprobe and stack watch control Masami Hiramatsu
2025-08-25 13:11                 ` Jinchao Wang
2025-09-01  7:06     ` [RFC PATCH 02/13] x86/HWBP: Add arch_reinstall_hw_breakpoint() for atomic updates Masami Hiramatsu
2025-09-01 10:23       ` Jinchao Wang
2025-09-02 14:11         ` Masami Hiramatsu
2025-09-03  7:58           ` Jinchao Wang
2025-09-04  0:53             ` Jinchao Wang
2025-09-04  1:02             ` Masami Hiramatsu
2025-09-04  1:15               ` Jinchao Wang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250818122720.434981-6-wangjinchao600@gmail.com \
    --to=wangjinchao600@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=davem@davemloft.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=mhiramat@kernel.org \
    --cc=naveen@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox