Register CPU online/offline callbacks via cpuhp_setup_state_nocalls()
so stack watches are installed/removed dynamically as CPUs come online
or go offline.

When a new CPU comes online, register a hardware breakpoint for the holder,
avoiding races with watch_on()/watch_off() that may run on another CPU. The
watch address will be updated the next time watch_on() is called.

Signed-off-by: Jinchao Wang <[email protected]>
---
 mm/kstackwatch/watch.c | 52 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/mm/kstackwatch/watch.c b/mm/kstackwatch/watch.c
index f922b4164be5..99184f63d7e3 100644
--- a/mm/kstackwatch/watch.c
+++ b/mm/kstackwatch/watch.c
@@ -85,6 +85,48 @@ static void ksw_watch_on_local_cpu(void *info)
        WARN(ret, "fail to reinstall HWBP on CPU%d ret %d", cpu, ret);
 }
 
+static int ksw_watch_cpu_online(unsigned int cpu)
+{
+       struct perf_event_attr attr;
+       struct ksw_watchpoint *wp;
+       call_single_data_t *csd;
+       struct perf_event *bp;
+
+       mutex_lock(&all_wp_mutex);
+       list_for_each_entry(wp, &all_wp_list, list) {
+               attr = wp->attr;
+               attr.bp_addr = (u64)&holder;
+               bp = perf_event_create_kernel_counter(&attr, cpu, NULL,
+                                                     ksw_watch_handler, wp);
+               if (IS_ERR(bp)) {
+                       pr_warn("%s failed to create watch on CPU %d: %ld\n",
+                               __func__, cpu, PTR_ERR(bp));
+                       continue;
+               }
+
+               per_cpu(*wp->event, cpu) = bp;
+               csd = per_cpu_ptr(wp->csd, cpu);
+               INIT_CSD(csd, ksw_watch_on_local_cpu, wp);
+       }
+       mutex_unlock(&all_wp_mutex);
+       return 0;
+}
+
+static int ksw_watch_cpu_offline(unsigned int cpu)
+{
+       struct ksw_watchpoint *wp;
+       struct perf_event *bp;
+
+       mutex_lock(&all_wp_mutex);
+       list_for_each_entry(wp, &all_wp_list, list) {
+               bp = per_cpu(*wp->event, cpu);
+               if (bp)
+                       unregister_hw_breakpoint(bp);
+       }
+       mutex_unlock(&all_wp_mutex);
+       return 0;
+}
+
 static void ksw_watch_update(struct ksw_watchpoint *wp, ulong addr, u16 len)
 {
        call_single_data_t *csd;
@@ -206,6 +248,16 @@ int ksw_watch_init(void)
        if (ret <= 0)
                return -EBUSY;
 
+       ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+                                       "kstackwatch:online",
+                                       ksw_watch_cpu_online,
+                                       ksw_watch_cpu_offline);
+       if (ret < 0) {
+               ksw_watch_free();
+               pr_err("Failed to register CPU hotplug notifier\n");
+               return ret;
+       }
+
        return 0;
 }
 
-- 
2.43.0


Reply via email to