From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qt1-f174.google.com (mail-qt1-f174.google.com [209.85.160.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 011B03CF204 for ; Tue, 14 Jul 2026 18:23:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053393; cv=none; b=ZNi7tunJsYP0xqtxoMAGV204Zk0q8XLOFMUmoNJ68fwBlXPd3e88C73bqumkQY8fshZ7AfwzP1wFoLWMQBZ0eUZD8iCy63C4adTMkpH1uAjIGQsw2MNh863cBXR+KUVVCQieL5w3XHvDZZqdk3WUBXdCCTXTXeMsyscyDfvJK2I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053393; c=relaxed/simple; bh=PlLf3uatOB4V7qqPGolTM2wzfx6QOLYYVgfstlgW/F4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=b+g1f5VdLvLigQM315M6rXu6vkxLMbs+deqHg77gP7LCah31X/xo3RAyjiW4exFz3BLGvYoElry/dRz48SDUpcVwddIL68OF6c9IprkXdnHwHc5wgxPa44BZ8/IOLpuOz8xBJTSRrb0RzTS9ePuSnXWKnWICYQgsNB6Qy3e6GTk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=qtxUn+nj; arc=none smtp.client-ip=209.85.160.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="qtxUn+nj" Received: by mail-qt1-f174.google.com with SMTP id d75a77b69052e-51c1805b8a7so46527591cf.3 for ; Tue, 14 Jul 2026 11:23:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053391; x=1784658191; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=/HcjDBHsn65ZCZzOohF40VNQnTu2vZZX71z4vh7Fe7A=; b=qtxUn+nj2yOaLXO2zhqCCEQWZOcuK2JwB81wvo5O9JN/UkLt2LrJ5W256pSWcbnBJ7 uuMGNjFYVbHUM9xe4CRaJiXjDwpF6rz5R1QjXPvnUsDcFa3zDhzPaHMH/20h8R94hRxI 2q2u3868r17JVddrGy9Vht2HRenX/AZj8kpAW3GXJVDkG+zlWSkvA4/ldKtm103PxlnV 6KaPj5WZuO9uFUdCKndgqLMDdaGH4GjhufT0hdEJ5b60+vWRj/jh/1hVQzypv8V2D69Q 9/g3FqGjODqs3X4UkD3dJJk1Gb9PYGLWDLuDw7OvsuNQnupk28RB0UfiG/JopIBHzFkp YrVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053391; x=1784658191; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=/HcjDBHsn65ZCZzOohF40VNQnTu2vZZX71z4vh7Fe7A=; b=HgLDVet54kevD0tATcDq+ZOw3u0KOPJoq+rTGcPr1/eUyyAmEfNnu5rTOo8kYgILnC ZwutDmEodjr+l+jMhxepBMe2X9TkoTj3eZQxO5j0Wpp19hCLxTTkA5dYyum3RQdVTaWl B78r6n+JkpTTqSjQ0stRKcVh2YlguH1uLNF0xPfid/yvX6AbndK+qfjHPQipPLoNljb3 9mR4J8xyH1D6NRa71Ey6HNeyC+G8IYi9C/nkYshht2l720tzQIdFItJsrtsGWqP3ToXx NtKa1BkY16TD6tVmHhlShoZNMtdNuQtWaXobJrZIrXuXowqnjlk1fnNBvIPCBlc0KC4v RNVQ== X-Forwarded-Encrypted: i=1; AHgh+Rr0Lpl+ZvoG1+Iy8x8+F5xao1NXfVTS786SaXShnS29UhUHRs3asVVZ+M7VMlBmNn3OGhb5PUvnCMvOzXg=@vger.kernel.org X-Gm-Message-State: AOJu0YwxlKBiGcq/ztiVBV8qg1NT5jSeki1wxnh950qHPhgODwBfvC4g RoJcnt5VEFcP2ewdb+Ghgp97g/TAe1qoYDz27wV6SwjUHj6DWrm+ygHS X-Gm-Gg: AfdE7ck7J4b5UOihHVgAv6Krly5XTzx2Pnc3ApppciVqUHb1feCdDpI5PlKmyzxogml Cytfqn7WL50wEA+2AMUQJcAygKHgidTMmCt4vmk2F7KOeQblFHGJg4xNqtZKrbbSV1lpLGZ9aVd bDtkw9oozwcF/gBo+EFlm900Sc4Odhq1+9AoufwgrP7x9jRNxnyz5Ah0SCKuBaOlmZNHvcZMrCi JZAZpHT6rC2FB2Db41j9NQ0Td6nw0jlIP1SpyJ+rEKGEhw45MdqjreTcZYkquv273TO6QVIRN9C ERrPsBqJ5J37gWHza9j+cEnMoDD5tSo5FCIsE+kTiJSq4cCShuZbqbOHVKAoJY4NO3BITQfcazY pN28+UM0tWStQ8+TAJpTcyByimoO8d7ovfj1jVd07p3IaBMZFSPMRPlUgi4tNXL+VjaxNglI3Cw e2ijcAd2b0zh2KLTUsU7EzE9dfdJFgtFx1tSKF3c7kXL5YfTiWdSY= X-Received: by 2002:ac8:5cd4:0:b0:51c:7b12:5fce with SMTP id d75a77b69052e-51cbf311623mr144967821cf.74.1784053390733; Tue, 14 Jul 2026 11:23:10 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-51caab6ed34sm116210141cf.4.2026.07.14.11.23.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:23:10 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 01/13] arch: add HAVE_REINSTALL_HW_BREAKPOINT Date: Wed, 15 Jul 2026 02:22:31 +0800 Message-ID: <20260714182243.10687-2-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Some architectures can update the address, length or type of an installed hardware breakpoint in place, without releasing and re-reserving its slot. Add an opt-in Kconfig symbol so generic code can offer such an operation on architectures that implement arch_reinstall_hw_breakpoint(). This is a prerequisite for KWatch, which re-points preallocated per-CPU breakpoints from atomic context, where the register/release path (which may sleep and rebalances slot constraints) cannot be used. Signed-off-by: Jinchao Wang --- arch/Kconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index fa7507ac8e13..41b3784e0ddd 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -457,6 +457,16 @@ config HAVE_MIXED_BREAKPOINTS_REGS Select this option if your arch implements breakpoints under the latter fashion. =20 +config HAVE_REINSTALL_HW_BREAKPOINT + bool + depends on HAVE_HW_BREAKPOINT + help + Depending on the arch implementation of hardware breakpoints, + some of them are able to update the breakpoint configuration + without release and reserve the hardware breakpoint register. + What configuration is able to update depends on hardware and + software implementation. + config HAVE_USER_RETURN_NOTIFIER bool =20 --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qk1-f181.google.com (mail-qk1-f181.google.com [209.85.222.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 619CC412BED for ; Tue, 14 Jul 2026 18:23:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053404; cv=none; b=myP/5nmSqGOQCEmcG2tj2u0y7Q+04RY1/nYH2cXviOHj4fR51maS7CedKUBayj7reuZT5yvP40kUj6ZmybrMb1a7Uk440bg0bqU9ieyTWa1M3TEoN5lOJGu5+vAZapbTNDJ9zZ5/wo3D63fQVGquDzXZWk0N5hzvNZYitcKCubw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053404; c=relaxed/simple; bh=iVQiaA1gzp4jOsMpivY/o63c2zQA1k2CD9ORqm0FVf0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PRthtMBt42WCF8KYv+3whj0g3ByFklQaiEoedVqDrCAWA3Hb2muJp2B20wce/VwCDx06Mgt/zO3a+iakMrR7iAXF4AC30K+NREKDerJ4/5CgCOp5mxBdfuI8yp4B1gc2vZkfYtMznaQBPAEK1IamWpb3G1o4OHo2CtkX+I07lVY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Jur5QtTv; arc=none smtp.client-ip=209.85.222.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Jur5QtTv" Received: by mail-qk1-f181.google.com with SMTP id af79cd13be357-92e512a9a6bso234481985a.2 for ; Tue, 14 Jul 2026 11:23:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053401; x=1784658201; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=lu1r+dI7YhT/+Q6NrL3ZLzbfp3koK+yzDpN5AF+wuNI=; b=Jur5QtTvSrOL3J5B6UY4ZG+7iCg1zrBxM9REA587hphoH16xCNakYhvzt3pifzkFuN 2u1UWTSRIsGAfFozHgYi4tTcEL5VPyS+FETaAZg2olAs/ZlMxtKxf6YkvNCeoND9TAMD VRJuJ9XEqED6w7BWVRk1qmwv1bd/YECeamFa/w8Y3JnQWp4vixTI6lYM+GX6ivzpCGz9 qkTOHySpgE8kAIWBSNnTysy6BTWGP+wMc+mWhizqYOaABSJ9X4FI703wXD+9plm219ig AKM9Wi++sQr2MNhmxa/CSwAl+mwzoTzrv9QjwmHKVmy6zaTCVSBzVperO11F58T8Eb8b 9jMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053401; x=1784658201; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=lu1r+dI7YhT/+Q6NrL3ZLzbfp3koK+yzDpN5AF+wuNI=; b=WnP02FGArwGBhx9s8JfP9BF8NN238muzCeiB8yAQzrqr50wyfMkxXoG/xkuVoECFmY FzTkoQmB7mv4ZSuOY3Z0g40OQ4t4Zs3le/5sjIiOwAy80YOc+r56DwOcNu/o5BBl7Ef5 tMpiurdBI/sY39DfSW7lrnpnOUUhlQ5MjSr0EZuTT88kJRXGylaVD53ydZz8VXLHo8oQ Q6ZkaFYAyg5bannzYL4rvrNGIOFwaXaPN+Qr1Y2MOjnjOna4RygMdknJDrzCn6S843KJ NjO16Jhy+ZFiPt0obOvZxyyqOPHW9aw9su77RaNwCf/9LY8trbO/UC6j/AYH72ZJMvAa Dtxg== X-Forwarded-Encrypted: i=1; AHgh+Rrwzms3ErI2tMKSH/ptT5MFPMuXwc8SQGcHJWkQVt5M0U0UiSD1jjlc7iUmVEhGuFm0rhy02j83iuoYyp8=@vger.kernel.org X-Gm-Message-State: AOJu0Yxal13/l7yAke79iSa35SXa/M0sJle+AoMpuGhYmDpDqZagH8V2 so7Ovinge3qJTUWoHzx5QzwWdvrC+UDW8p4uv4jjfDW1lRC9dL43GRnl X-Gm-Gg: AfdE7cljNRuBc9HF2oAODRyd6XQfCJWF/fI6K7IxYBOy4+F/7Mt7SyiKGlXRrI11mfk dFQXF8EWRS8Lt//DGT1eZUZMhFSVm9L1iI0Cjqrpf2WWIZZzX6aoXg/RhkgPFmczEyDHRc4CtvE Iuw2CtukjG24Xqx3Z6WQM79K0fKNTO8i92c3Jed/8i0pG4Koix2Odxb2LeHa7NR0Rctt5Jo1QQ/ T+4vj7ZEYwVASjZPJZrm5fxaRpG2WoMm7OEpT4NGri8s9T+htn6pCnuI+wYllFXSqetRQXI4gvH q+t2VkH2/XlUrq4OyWOrzuNJ2vIlxPnTmKTUwS3pUQni13H2MszpsZ+NbbXiTAde4FlhA17FRND WaUZQvq4ubpT17k6N2dN9WgyqpbzSxHWbRIeakTFwL+3T0Rm5rlVYLvrg38EVTJF4RsyUKzD9kQ E5flGJ3ks+fY42hAk3oLa2KW9eDsB6fJrbBhsW6Xniv2pby+sb2dg= X-Received: by 2002:a05:620a:408c:b0:92e:9cb3:3f71 with SMTP id af79cd13be357-93086a5a11emr367980985a.36.1784053400994; Tue, 14 Jul 2026 11:23:20 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id af79cd13be357-92ee5d33689sm1501801285a.36.2026.07.14.11.23.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:23:20 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 02/13] x86/hw_breakpoint: Unify breakpoint install/uninstall Date: Wed, 15 Jul 2026 02:22:32 +0800 Message-ID: <20260714182243.10687-3-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Consolidate breakpoint management to reduce code duplication. The diffstat was misleading, so the stripped code size is compared instead. After refactoring, it is reduced from 11976 bytes to 11448 bytes on my x86_64 system built with clang. This also makes it easier to introduce arch_reinstall_hw_breakpoint(). In addition, including linux/types.h to fix a missing build dependency. Signed-off-by: Jinchao Wang Reviewed-by: Masami Hiramatsu (Google) --- arch/x86/include/asm/hw_breakpoint.h | 6 ++ arch/x86/kernel/hw_breakpoint.c | 141 +++++++++++++++------------ 2 files changed, 84 insertions(+), 63 deletions(-) diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw= _breakpoint.h index 0bc931cd0698..aa6adac6c3a2 100644 --- a/arch/x86/include/asm/hw_breakpoint.h +++ b/arch/x86/include/asm/hw_breakpoint.h @@ -5,6 +5,7 @@ #include =20 #define __ARCH_HW_BREAKPOINT_H +#include =20 /* * The name should probably be something dealt in @@ -18,6 +19,11 @@ struct arch_hw_breakpoint { u8 type; }; =20 +enum bp_slot_action { + BP_SLOT_ACTION_INSTALL, + BP_SLOT_ACTION_UNINSTALL, +}; + #include #include #include diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoin= t.c index f846c15f21ca..877509539300 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c @@ -49,7 +49,6 @@ static DEFINE_PER_CPU(unsigned long, cpu_debugreg[HBP_NUM= ]); */ static DEFINE_PER_CPU(struct perf_event *, bp_per_reg[HBP_NUM]); =20 - static inline unsigned long __encode_dr7(int drnum, unsigned int len, unsigned int type) { @@ -86,96 +85,112 @@ int decode_dr7(unsigned long dr7, int bpnum, unsigned = *len, unsigned *type) } =20 /* - * Install a perf counter breakpoint. - * - * We seek a free debug address register and use it for this - * breakpoint. Eventually we enable it in the debug control register. - * - * Atomic: we hold the counter->ctx->lock and we only handle variables - * and registers local to this cpu. + * We seek a slot and change it or keep it based on the action. + * Returns slot number on success, negative error on failure. + * Must be called with IRQs disabled. */ -int arch_install_hw_breakpoint(struct perf_event *bp) +static int manage_bp_slot(struct perf_event *bp, enum bp_slot_action actio= n) { - struct arch_hw_breakpoint *info =3D counter_arch_bp(bp); - unsigned long *dr7; - int i; - - lockdep_assert_irqs_disabled(); + struct perf_event *old_bp; + struct perf_event *new_bp; + int slot; + + switch (action) { + case BP_SLOT_ACTION_INSTALL: + old_bp =3D NULL; + new_bp =3D bp; + break; + case BP_SLOT_ACTION_UNINSTALL: + old_bp =3D bp; + new_bp =3D NULL; + break; + default: + return -EINVAL; + } =20 - for (i =3D 0; i < HBP_NUM; i++) { - struct perf_event **slot =3D this_cpu_ptr(&bp_per_reg[i]); + for (slot =3D 0; slot < HBP_NUM; slot++) { + struct perf_event **curr =3D this_cpu_ptr(&bp_per_reg[slot]); =20 - if (!*slot) { - *slot =3D bp; - break; + if (*curr =3D=3D old_bp) { + *curr =3D new_bp; + return slot; } } =20 - if (WARN_ONCE(i =3D=3D HBP_NUM, "Can't find any breakpoint slot")) - return -EBUSY; + if (old_bp) { + WARN_ONCE(1, "Can't find matching breakpoint slot"); + return -EINVAL; + } + + WARN_ONCE(1, "No free breakpoint slots"); + return -EBUSY; +} + +static void setup_hwbp(struct arch_hw_breakpoint *info, int slot, bool ena= ble) +{ + unsigned long dr7; =20 - set_debugreg(info->address, i); - __this_cpu_write(cpu_debugreg[i], info->address); + set_debugreg(info->address, slot); + __this_cpu_write(cpu_debugreg[slot], info->address); =20 - dr7 =3D this_cpu_ptr(&cpu_dr7); - *dr7 |=3D encode_dr7(i, info->len, info->type); + dr7 =3D this_cpu_read(cpu_dr7); + if (enable) + dr7 |=3D encode_dr7(slot, info->len, info->type); + else + dr7 &=3D ~__encode_dr7(slot, info->len, info->type); =20 /* - * Ensure we first write cpu_dr7 before we set the DR7 register. - * This ensures an NMI never see cpu_dr7 0 when DR7 is not. + * Enabling: + * Ensure we first write cpu_dr7 before we set the DR7 register. + * This ensures an NMI never see cpu_dr7 0 when DR7 is not. */ + if (enable) + this_cpu_write(cpu_dr7, dr7); + barrier(); =20 - set_debugreg(*dr7, 7); + set_debugreg(dr7, 7); + if (info->mask) - amd_set_dr_addr_mask(info->mask, i); + amd_set_dr_addr_mask(enable ? info->mask : 0, slot); =20 - return 0; + /* + * Disabling: + * Ensure the write to cpu_dr7 is after we've set the DR7 register. + * This ensures an NMI never see cpu_dr7 0 when DR7 is not. + */ + if (!enable) + this_cpu_write(cpu_dr7, dr7); } =20 /* - * Uninstall the breakpoint contained in the given counter. - * - * First we search the debug address register it uses and then we disable - * it. - * - * Atomic: we hold the counter->ctx->lock and we only handle variables - * and registers local to this cpu. + * find suitable breakpoint slot and set it up based on the action */ -void arch_uninstall_hw_breakpoint(struct perf_event *bp) +static int arch_manage_bp(struct perf_event *bp, enum bp_slot_action actio= n) { - struct arch_hw_breakpoint *info =3D counter_arch_bp(bp); - unsigned long dr7; - int i; + struct arch_hw_breakpoint *info; + int slot; =20 lockdep_assert_irqs_disabled(); =20 - for (i =3D 0; i < HBP_NUM; i++) { - struct perf_event **slot =3D this_cpu_ptr(&bp_per_reg[i]); - - if (*slot =3D=3D bp) { - *slot =3D NULL; - break; - } - } - - if (WARN_ONCE(i =3D=3D HBP_NUM, "Can't find any breakpoint slot")) - return; + slot =3D manage_bp_slot(bp, action); + if (slot < 0) + return slot; =20 - dr7 =3D this_cpu_read(cpu_dr7); - dr7 &=3D ~__encode_dr7(i, info->len, info->type); + info =3D counter_arch_bp(bp); + setup_hwbp(info, slot, action !=3D BP_SLOT_ACTION_UNINSTALL); =20 - set_debugreg(dr7, 7); - if (info->mask) - amd_set_dr_addr_mask(0, i); + return 0; +} =20 - /* - * Ensure the write to cpu_dr7 is after we've set the DR7 register. - * This ensures an NMI never see cpu_dr7 0 when DR7 is not. - */ - barrier(); +int arch_install_hw_breakpoint(struct perf_event *bp) +{ + return arch_manage_bp(bp, BP_SLOT_ACTION_INSTALL); +} =20 - this_cpu_write(cpu_dr7, dr7); +void arch_uninstall_hw_breakpoint(struct perf_event *bp) +{ + arch_manage_bp(bp, BP_SLOT_ACTION_UNINSTALL); } =20 static int arch_bp_generic_len(int x86_len) --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qv1-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9CC25412C07 for ; Tue, 14 Jul 2026 18:23:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053416; cv=none; b=H4XoPI8VN10AuIYwXziXA5RsMtErW2u9Vx2MzNX7BTuzbtonWKREHdCE2zlu5v0sYhJampA0n5dFfMIs+dGtMCDSv8wQxw5hAuZCMR6SIW5Dh/7VwEPAB8Ppwu6G7hUagih6ncEeT9DVrQh2g1vsvcn4GtRITpmkcTOsGG66VkA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053416; c=relaxed/simple; bh=4jktZnR0Pc2J7o9JFOWqLz2AFfowdAE5d7umNHBmtls=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dpQaxSb9x/T48sGbaoCGKUp5YJAOizq0tyREnm4+GALm/w+jXVTqyuayXoP/xuCurMBqs6fERIuJgSr/tZP6OIS9poYLKygEF0c1gKnAhAcbDwSHvYyUUZSN+h+RTuf+NM9GofikwQhWjDzUt9zS9jokUxSjpjjHCT3clCf2ZKY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=W/uYmZ8g; arc=none smtp.client-ip=209.85.219.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="W/uYmZ8g" Received: by mail-qv1-f50.google.com with SMTP id 6a1803df08f44-9063b380982so22585786d6.0 for ; Tue, 14 Jul 2026 11:23:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053413; x=1784658213; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=dlvBm4NItea/+plJuhWRRJi1bfhjVkmixX1HjLGBPlA=; b=W/uYmZ8gEQNcJJpfYjNkCOkHIRSulmC9MgO0+6GH5Zlz1ob9T27rWwkjFtwDYGT32V T3sl0sF0ziCMO02IF+45IUl6Z4XE6Fer5piqLvLFaSGjKOKoSchXa6uCx1XyknDeVKM7 5Xvz9XyZavQGDN18vGn8XDUW5ABO6s3cIk/ibFGDnBFUoDXIOOuaJT5JNZvsSMnc1uGN ct2JiGDWuP5HVlU6kUFBRRC2WElQb2DfEEWBcT6mjpbkzxxQ6Dm77+S8JtiQurrlLjtN Z7jqPeyRd3NvonDHfCNtkocjzC0zSUbWc4/ONJxIKKrX1bZNB8DrlIcGzpx/xZAV2iP5 ijkw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053413; x=1784658213; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=dlvBm4NItea/+plJuhWRRJi1bfhjVkmixX1HjLGBPlA=; b=kHiL/qUkITld66b+S040yUPYR6jZpqPxhdoblLgCbiZCcJGPi9fKA2l1Slk7bSMZVw 3sXjtC9aaXQCSeKXuSfGl1sNk+Mtykkj7J0bG+oetg2aHVRk/U+T+99lGVcA01YnN21E nDHEiaERJP6QB58Ju1J+VstirjnpdXyInuoNk6z41ExaNy01HUTtJFT6jxZaDZntV+va k3qMmSAiP3PyjgOD1NeEd92LuP80M+8/zrC/yusOnzpzLbEXkAX25ptp1WU10/Ltrfzy cqNE2lpLDd5xvWJA/3IozdTfV4+wBRTWO06YBV5rtI8Z12/haN2Uck4h7+x4cWeONTK7 iMvw== X-Forwarded-Encrypted: i=1; AHgh+Ro8wYydcfVTewCIi/11cMJusF7IbkrY4MkQ2ghUmvSKH23ZuFg73eKNtO2OdvcEOhAGgw10WBOxARXPHBE=@vger.kernel.org X-Gm-Message-State: AOJu0YxOOT4VLuw1XPfm6lc5yMgXYz/q7HhTSprU3hWOLMLL6rBrvlGd E64BvG/+cfiwQUp65aimzCxCxioCdJdkZilWyYF2uGvxTii2nMhLvIOX X-Gm-Gg: AfdE7clPd3DHMHrxEskg3I35AwYtKFOX45ytyqflkEa7RpS1ni3oAwu0voYZECXkg1x LRikEI97jUJMCHwR4hNuRC2EMwPanJ7iN+jPFLgk2k0Cvwe9ABhEZmDN6dmmD3OBojGCoqSRlY8 LKLjGGmxsztTGq2+W53EkvxXo0XqK4m/6R568hlS7IBfeVeHzb84Pv2P5WOylGJxpTUKCvdqYba ntNNsJopUztnJN+ORF+5EXwOi61IqboBUCtIGdqxQEkMLeE9nz4EhjvRPSxkDooBAKAwcDZw2nl jQCXB+GB9ySaruar542tulbIJfNONxQytO7bgE3QBoDyrmGW8/A2CJxUYxrkqKfkAWwGUi1UZDF oNU9hS+B6yMSSUyw1BxLg2Dk0w6yazcW9bUwrPPZTazhwLoab0vdRW5Oi7RHshkL3p10BL2//2K LG6UvpUbjSd/ebVf8X7MWadHsrzhmqK1aEyh0yHNpmXV6xk6RFJfk= X-Received: by 2002:a05:6214:5913:b0:8f2:2e4e:f5f with SMTP id 6a1803df08f44-90401b75879mr187179916d6.45.1784053413454; Tue, 14 Jul 2026 11:23:33 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ffd248e666sm173753776d6.0.2026.07.14.11.23.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:23:32 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 03/13] x86/hw_breakpoint: Add arch_reinstall_hw_breakpoint Date: Wed, 15 Jul 2026 02:22:33 +0800 Message-ID: <20260714182243.10687-4-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The new arch_reinstall_hw_breakpoint() function can be used in an atomic context, unlike the more expensive free and re-allocation path. This allows callers to efficiently re-establish an existing breakpoint, and x86 advertises the capability via HAVE_REINSTALL_HW_BREAKPOINT. Since a REINSTALL may change bp_len, setup_hwbp() must clear the slot's stale len/type and enable bits in DR7 before re-encoding: OR-merging the new encoding over the old one would keep the CPU watching with the stale width (verified in QEMU by reading DR7 after re-arming watch_len=3D1 over a len8 breakpoint: 0x999906aa merged without the clearing, 0x199906aa with it). Signed-off-by: Jinchao Wang --- arch/x86/Kconfig | 1 + arch/x86/include/asm/hw_breakpoint.h | 2 ++ arch/x86/kernel/hw_breakpoint.c | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index bdad90f210e4..5be698db0241 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -246,6 +246,7 @@ config X86 select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINS select HAVE_HW_BREAKPOINT + select HAVE_REINSTALL_HW_BREAKPOINT select HAVE_IOREMAP_PROT select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64 select HAVE_IRQ_TIME_ACCOUNTING diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw= _breakpoint.h index aa6adac6c3a2..c22cc4e87fc5 100644 --- a/arch/x86/include/asm/hw_breakpoint.h +++ b/arch/x86/include/asm/hw_breakpoint.h @@ -21,6 +21,7 @@ struct arch_hw_breakpoint { =20 enum bp_slot_action { BP_SLOT_ACTION_INSTALL, + BP_SLOT_ACTION_REINSTALL, BP_SLOT_ACTION_UNINSTALL, }; =20 @@ -65,6 +66,7 @@ extern int hw_breakpoint_exceptions_notify(struct notifie= r_block *unused, =20 =20 int arch_install_hw_breakpoint(struct perf_event *bp); +int arch_reinstall_hw_breakpoint(struct perf_event *bp); void arch_uninstall_hw_breakpoint(struct perf_event *bp); void hw_breakpoint_pmu_read(struct perf_event *bp); void hw_breakpoint_pmu_unthrottle(struct perf_event *bp); diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoin= t.c index 877509539300..4221dbb899f9 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c @@ -100,6 +100,10 @@ static int manage_bp_slot(struct perf_event *bp, enum = bp_slot_action action) old_bp =3D NULL; new_bp =3D bp; break; + case BP_SLOT_ACTION_REINSTALL: + old_bp =3D bp; + new_bp =3D bp; + break; case BP_SLOT_ACTION_UNINSTALL: old_bp =3D bp; new_bp =3D NULL; @@ -134,10 +138,13 @@ static void setup_hwbp(struct arch_hw_breakpoint *inf= o, int slot, bool enable) __this_cpu_write(cpu_debugreg[slot], info->address); =20 dr7 =3D this_cpu_read(cpu_dr7); + /* + * Clear the slot's stale len/type and enable bits first: a REINSTALL + * with a different bp_len would otherwise OR-merge both encodings. + */ + dr7 &=3D ~__encode_dr7(slot, 0xf, 0); if (enable) dr7 |=3D encode_dr7(slot, info->len, info->type); - else - dr7 &=3D ~__encode_dr7(slot, info->len, info->type); =20 /* * Enabling: @@ -188,6 +195,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp) return arch_manage_bp(bp, BP_SLOT_ACTION_INSTALL); } =20 +int arch_reinstall_hw_breakpoint(struct perf_event *bp) +{ + return arch_manage_bp(bp, BP_SLOT_ACTION_REINSTALL); +} + void arch_uninstall_hw_breakpoint(struct perf_event *bp) { arch_manage_bp(bp, BP_SLOT_ACTION_UNINSTALL); --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qt1-f170.google.com (mail-qt1-f170.google.com [209.85.160.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6FF87331EA9 for ; Tue, 14 Jul 2026 18:31:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053861; cv=none; b=jQUZuK/LRB9fOco0aXYZ2Pprbc8e4yGTcc8WconhdFqwMejg/SVbKIqvPx0l8lSGZkrxHdowCIoId/jLeknPAaZUA6F0FT/QoU5FELG9NqZosWflt7P9IpedsF2+DDr0H+5L0DR19/FGqgaGvPBk+gXL/0rsS7gF5ZS/nn9n41k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053861; c=relaxed/simple; bh=FHK60SJXTYT3avGIezwouMxmlq7uogDooasnOh5j77M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XtB6Cv2nnf1MNu5AzbZhkibB9upeBVtGCy6taLU0FmxCdOYsZW+3r0GigADS48Ncq/uif6UhI7LyVSna/Ri3eL/+K1AsEumSwBHXGLZhHlrhd4ZUpl0aoZoBRgfhY5tsFYW65x3k1oPsZ8igwHApLS4qPIdDJJUdwO1ehHZ5X6c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=WUXD74rY; arc=none smtp.client-ip=209.85.160.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WUXD74rY" Received: by mail-qt1-f170.google.com with SMTP id d75a77b69052e-51c16ac21acso9357011cf.0 for ; Tue, 14 Jul 2026 11:31:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053859; x=1784658659; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=XbJ+trxR7t2GCSYkV+uASOu7DEK0LHp1KcDwbXdFB5Y=; b=WUXD74rYONhqHtUftV7/r2zoWXVS/VCNwvFGavyIQWkBSP7RukQbuxV6ld49DYR+Td qCfOh4kw/TJtDJ9LKrjeYkrF4FgDESk0vFyQrOjAdsdjdBH+9iigzB0Fxg0VWjI/pi73 oYGEKQbLZ+g1+k+d0uL7jcV9yaAJrj851jKQk+kkGTGVYzFfOB5h29H/zQVnLcl5ikIK 5RMvRp4/WE9VVgYf95q7tBkLBNq6t0G2L2s1OkNHDFniTjQ7zOzx2BTcvtYZBMPw1Bic w58LuiJHIfORrSHlPXCUcpWFj5QNiWq7uG1WYYlNUetZKO7bbt0NQwR0XL2AvtOi5Gza 15Nw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053859; x=1784658659; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=XbJ+trxR7t2GCSYkV+uASOu7DEK0LHp1KcDwbXdFB5Y=; b=CgPSAMOkI6CyZVpxuE1sGYQhEIMx/U2Ii6ROz54UWgxImcJS1DFG++c4jrWgjhoMZ9 cvMM9EgOg/pXpERo+YCYMX165x5cVbNgkQiZiio5xFCZUlX9Vh2holOxByWpr8n5oKGZ NiRK3ynaDqs4Doy7bhCuiKa3jtwXnThA7eU+SqxO+StyhdlGC5M3a3yjvgbbO0OEX2lJ 1pBAffaEMIhUognZdZGZGqQGzXCI1HRJbMFutb75NAAZyhwHDIh+sZGSCQBiVAynw2BO 6GfoHmTbUXeY0uncXBCTxNkMBbXt+jK12ut0jYJ+beGp5gKB8bmLSQr0kkPmCCCP9GwE iCzQ== X-Forwarded-Encrypted: i=1; AHgh+RqAldhukOY9ruiTRLj2qbV7c1hfHT/6BK3ntQwuFSk1bHYemvZy/b7rhXab4NASiyCo8Lvb8885J2fvolA=@vger.kernel.org X-Gm-Message-State: AOJu0YzNU1bZJjbJEd9cM67JmkKcWa+wus/EEQfDFouXYGFy/NnrJOJr RV03QE9O7IXx1EYkAzdo7nlGB5CRU1S4NYbgSIIkJwGbY6L7EpNVfcLj X-Gm-Gg: AfdE7cma9LMouesb/8hki7OKF5zIDy0QPxtAL9MuZRAjzSSj+393zaoyfjymI3K+3wH 3QF7p0xIQ9lxj41u/hVJ4yEwiwFPRMAnLJ5DuRsMS2w07OWhGMM8+RRvP1FI7pLPSdkZ/pe4/Y9 ztS2XwCoghfjoHhLR2d5GJ/NDkc5MQSqx88tvwQqhvH/gozXwFCh1Cmq+Yi33KsU1LxoLCKMpcs l8e99jyrGj6ygyQhii4+h97nSRcv017ouUBKofcwuAZIOG1NKyFenXwCaW1kOeS4xMwuOx2yEyH jfSwTNY4Gl7GVcWusYwzxMx8pjZkCeoYv9ZP5P1TwjAhjmQIJkb8JGJpivHMd2dtJdINBnWw/jd hsSTLESXf44rg0yOaN2yZYQYDZ0FlZa+Qj8PnTGO6YnHzDLqkgaMoQwRMQdV4ZiIiZthwIM55Jo OpUm5kUpo77vkXZujUAft8XlunojRsjYQgFeYTaFzJiJhroYfvVFc= X-Received: by 2002:a05:622a:1f98:b0:51c:84cb:b9b0 with SMTP id d75a77b69052e-51cbf301566mr148948911cf.81.1784053859166; Tue, 14 Jul 2026 11:30:59 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-51caae24d04sm117191211cf.18.2026.07.14.11.30.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:30:58 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 04/13] HWBP: Add modify_wide_hw_breakpoint_local() API Date: Wed, 15 Jul 2026 02:30:49 +0800 Message-ID: <20260714183049.12383-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Masami Hiramatsu (Google)" Add modify_wide_hw_breakpoint_local() arch-wide interface which allows hwbp users to update watch address on-line. This is available if the arch supports CONFIG_HAVE_REINSTALL_HW_BREAKPOINT. Note that this allows to change the type only for compatible types, because it does not release and reserve the hwbp slot based on type. For instance, you can not change HW_BREAKPOINT_W to HW_BREAKPOINT_X. Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Jinchao Wang --- include/linux/hw_breakpoint.h | 6 ++++++ kernel/events/hw_breakpoint.c | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h index db199d653dd1..ea373f2587f8 100644 --- a/include/linux/hw_breakpoint.h +++ b/include/linux/hw_breakpoint.h @@ -81,6 +81,9 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr, perf_overflow_handler_t triggered, void *context); =20 +extern int modify_wide_hw_breakpoint_local(struct perf_event *bp, + struct perf_event_attr *attr); + extern int register_perf_hw_breakpoint(struct perf_event *bp); extern void unregister_hw_breakpoint(struct perf_event *bp); extern void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cp= u_events); @@ -124,6 +127,9 @@ register_wide_hw_breakpoint(struct perf_event_attr *att= r, perf_overflow_handler_t triggered, void *context) { return NULL; } static inline int +modify_wide_hw_breakpoint_local(struct perf_event *bp, + struct perf_event_attr *attr) { return -ENOSYS; } +static inline int register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } static inline void unregister_hw_breakpoint(struct perf_event *bp) { } static inline void diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index 789add0c185a..20ca64f30508 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -888,6 +888,43 @@ void unregister_wide_hw_breakpoint(struct perf_event *= __percpu *cpu_events) } EXPORT_SYMBOL_GPL(unregister_wide_hw_breakpoint); =20 +/** + * modify_wide_hw_breakpoint_local - update breakpoint config for local CPU + * @bp: the hwbp perf event for this CPU + * @attr: the new attribute for @bp + * + * This does not release and reserve the slot of a HWBP; it just reuses the + * current slot on local CPU. So the users must update the other CPUs by + * themselves. + * Also, since this does not release/reserve the slot, this can not change= the + * type to incompatible type of the HWBP. + * Return err if attr is invalid or the CPU fails to update debug register + * for new @attr. + */ +#ifdef CONFIG_HAVE_REINSTALL_HW_BREAKPOINT +int modify_wide_hw_breakpoint_local(struct perf_event *bp, + struct perf_event_attr *attr) +{ + int ret; + + if (find_slot_idx(bp->attr.bp_type) !=3D find_slot_idx(attr->bp_type)) + return -EINVAL; + + ret =3D hw_breakpoint_arch_parse(bp, attr, counter_arch_bp(bp)); + if (ret) + return ret; + + return arch_reinstall_hw_breakpoint(bp); +} +#else +int modify_wide_hw_breakpoint_local(struct perf_event *bp, + struct perf_event_attr *attr) +{ + return -EOPNOTSUPP; +} +#endif +EXPORT_SYMBOL_GPL(modify_wide_hw_breakpoint_local); + /** * hw_breakpoint_is_used - check if breakpoints are currently used * --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qt1-f174.google.com (mail-qt1-f174.google.com [209.85.160.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B2C632DEA8C for ; Tue, 14 Jul 2026 18:31:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053883; cv=none; b=T8qQQQGGI726C6Pbl0LWrTzkjOzX675IIq9hDVHX8OOdl+cCxTrUiedRVEdZBLpoic+sDhXOUCUUWZIKcPZ8ox8oZvdYxg4qmdhjAtWP1SXLtdaRqQXo6m2ZLdx67SZXlBc6lDVRTHN8+Bu7hTrm7H90vDzLpv1v6zAUaS3jsWs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053883; c=relaxed/simple; bh=fDbK7c6+B4xWjTfmF+IPyJhunYFzAjMQ8Y6rqVfMvII=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lEi6ACSlR5qFzgZ9zrYsY6/bmyoO4cEei8pBsj9gYlvJft8REOqRIa3ZZkjwlfeP0Q+PlmRnZMxB/qmXvCeQ+0zWNKmnOUy3yM/R/KHDRTbopDNdAJ2xQdDoG5lEzBUgVLIKHsWLeg7MxNpjsFkEQEAqSJ+buez2s3g7p20UWeQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Dg5MGjBl; arc=none smtp.client-ip=209.85.160.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Dg5MGjBl" Received: by mail-qt1-f174.google.com with SMTP id d75a77b69052e-51bfa429aa6so8587381cf.0 for ; Tue, 14 Jul 2026 11:31:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053881; x=1784658681; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=0sIaGf5EzkbjI19/agb6agMi3UfIlX+gCu5QJjKxqwU=; b=Dg5MGjBl2B7ggqi+x72dw/x92mZHUvAd33U8llzS12W3vblEwMVN0ER95oXPaaN2nP LO/lGOyJLj43ohBYRLYt4EskZ8szGdO4Eui56ZxlV6+WE72mvuW939aLk4mi7vX+HaPz 5fTp5c4FJSG9LmlikMSRStEEWDykJkF7l38/36Pdc4gwiRMpPjaHL1YwJ7fWTRsIPOIp S51RhZimapd/ESUDDU949OUIGBIqsiUrMYkExp/A8FWm0IPicEAtRqMrHsVW5wSiLP72 L31tOFwz8+8SnIrSBOKlbx/n/99bJwgieFOfO7dqV/jAyvf1++MFLeqd14dGK1XdRyKC 5w2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053881; x=1784658681; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=0sIaGf5EzkbjI19/agb6agMi3UfIlX+gCu5QJjKxqwU=; b=Wda2IRhx8VSY8VS2d+bE5wg6xmHQObOy7J2rNus6ORHt34ByH+8lknbo+8uXrAbvdE dDtBu4TsCu5FRhelPdh/bnfY6VhBKR0pDHytuqLYEXHOueAMcPPHvjg8Iwu3ShF6gmHC TRzmFfVVaEOjZJpx7GF/AelWRW+6h/YvexdMvnUStyE1BmKpsOhkOwfPI7snY0BVnxtA kQyiXMlr2/FBrea2OMzLQkQ67KgfAyx4JR1S88jRRUuxjZ13c7XuuHUvDcp//Amu+kLQ ZzkaDdzO01+T/HLb+zZymAF3JagfJ0+8KNOia6ZmimuPgX2yr3JXuyP24jQQsJJmBDDO ZF6Q== X-Forwarded-Encrypted: i=1; AHgh+RpHfOKDFDJLbsCi/I2JI4O2FI3cJHbEaFyQMQ5pPPwAlkLgU7j8ItUbN2+sa7lYVquJ9CjIE21u61wfuok=@vger.kernel.org X-Gm-Message-State: AOJu0YwnUmVgV+trYVSb6p9q4IzxH5wR7BtDrYVcDL4MeIVv6l/87Jeg muPQchDLJuuj6+hUOHyicZ26667awnCAE7Q+pYsUp7ziyvdwoao7cX6N X-Gm-Gg: AfdE7cnH0kdprPXT2Bt+Ojbu9VuvA0MPB5Z7HyJL2lypyY4lfXk/VIX3sYQSXQ/H9tN wMyu2KOm/7pPvg0ZBrGxN1ATgyUtIaYdaXtk56Aoq9K1Qa/xAjMmBcL/hB86JpKekd7QgOkXkjm vQQGVXmwAfiKW5DWyzNjtkWg6QQyPIH5RPl0ltjaxBt9t33VqR3Xi7xotRBGP1iqBDUuDYfVNz2 h7LLFQSmVIOZvXhfwhfrOQBF/lBU9KqYLjQOBJatocnZxxMXNdcC3isfFJG99zeiMdX272GVWz5 syWykFeSn0kqkwvG5tFLDNZwr4EIE7NBjUrSi3ygQWypP1NEg1UFRMi+10JkgqAaCGcKMsFWGpJ 5kMWuRGQ/ADdw6CTLPvg9n3HZZIvC+FsPSMPcCviLQpA8HczQjdiWfmiDSUVDLnHhxGWwuRH/+p OArOzxAT2E9oVFZ8Nn5o2+vM4EUJREdElmg8rClBw48yy+168sSv4= X-Received: by 2002:a05:622a:306:b0:51c:f64:bc39 with SMTP id d75a77b69052e-51cbf741118mr134331951cf.3.1784053880463; Tue, 14 Jul 2026 11:31:20 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-51caae24d04sm117196081cf.18.2026.07.14.11.31.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:31:19 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 05/13] mm/kwatch: add watch expression parser and dereference engine Date: Wed, 15 Jul 2026 02:31:07 +0800 Message-ID: <20260714183107.12463-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" KWatch watches a memory address that is only known once the target function runs, e.g. "argument 1, plus 8, dereferenced once". Add the two halves of that mechanism: - kwatch_deref_parse() turns a textual watch expression {base}[+-off][->[+-]off]... into a kwatch_config: a base anchor (arg1..arg6, stack, an absolute address or - for built-in KWatch - a symbol name) plus a static offset chain. - kwatch_deref_resolve() replays the chain at probe time against pt_regs. Every pointer load goes through get_kernel_nofault() and the final address must be a kernel address. Also add the internal kwatch.h header shared by the rest of the series. Nothing is built yet; the Kconfig entry comes with the control plane. Signed-off-by: Jinchao Wang --- mm/kwatch/Makefile | 3 + mm/kwatch/deref.c | 174 +++++++++++++++++++++++++++++++++++++++++++++ mm/kwatch/kwatch.h | 107 ++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 mm/kwatch/Makefile create mode 100644 mm/kwatch/deref.c create mode 100644 mm/kwatch/kwatch.h diff --git a/mm/kwatch/Makefile b/mm/kwatch/Makefile new file mode 100644 index 000000000000..69c21ae62123 --- /dev/null +++ b/mm/kwatch/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_KWATCH) +=3D kwatch.o + +kwatch-y :=3D deref.o diff --git a/mm/kwatch/deref.c b/mm/kwatch/deref.c new file mode 100644 index 000000000000..a93c76139e7c --- /dev/null +++ b/mm/kwatch/deref.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "kwatch.h" + +int kwatch_deref_resolve(const struct kwatch_config *cfg, struct pt_regs *= regs, + unsigned long *out_addr, u16 *out_len) +{ + unsigned long addr =3D 0; + int i; + + /* 1. Resolve the Base Anchor */ + if (cfg->base =3D=3D KWATCH_BASE_STACK) { + addr =3D kernel_stack_pointer(regs); + if (unlikely(!addr)) + return -EINVAL; + } else if (cfg->base >=3D KWATCH_BASE_ARG1 && + cfg->base <=3D KWATCH_BASE_ARG6) { + int arg_idx =3D cfg->base - KWATCH_BASE_ARG1; + + addr =3D regs_get_kernel_argument(regs, arg_idx); + } else if (cfg->base =3D=3D KWATCH_BASE_ABS_ADDR || + cfg->base =3D=3D KWATCH_BASE_GLOBAL_SYM) { + /* Zero-latency load of the static symbol location */ + addr =3D cfg->sym_addr; + } else { + return -EINVAL; + } + + /* 2. The Pointer-Chasing FSM */ + for (i =3D 0; i < cfg->offset_count; i++) { + addr +=3D cfg->offsets[i]; + + if (i < cfg->offset_count - 1) { + unsigned long next_addr; + + /* Dynamically read the pointer contents at runtime */ + if (get_kernel_nofault(next_addr, (unsigned long *)addr)) + return -EFAULT; + + addr =3D next_addr; + } + } + + /* Enforce strict Kernel-Space boundary */ + if (unlikely(addr < TASK_SIZE_MAX)) + return -EINVAL; + + *out_addr =3D addr; + *out_len =3D cfg->watch_len; + return 0; +} + +int kwatch_deref_parse(struct kwatch_config *cfg, const char *watch_expr) +{ + char *p, *sep, *dup_expr; + char type =3D '\0'; + bool is_deref =3D false; + int ret =3D 0; + + dup_expr =3D kstrdup(watch_expr, GFP_KERNEL); + if (!dup_expr) + return -ENOMEM; + + cfg->offset_count =3D 1; + cfg->offsets[0] =3D 0; + + /* 1. Isolate and Resolve Base Anchor */ + p =3D dup_expr; + sep =3D NULL; + while (*p) { + if (*p =3D=3D '+') { + sep =3D p; + type =3D '+'; + break; + } + if (*p =3D=3D '-') { + sep =3D p; + type =3D '-'; + if (p[1] =3D=3D '>') + is_deref =3D true; + break; + } + p++; + } + + if (type) + *sep =3D '\0'; + + if (!strcmp(dup_expr, "stack")) { + cfg->base =3D KWATCH_BASE_STACK; + } else if (!strncmp(dup_expr, "arg", 3) && strlen(dup_expr) =3D=3D 4) { + int arg_num; + + if (kstrtoint(dup_expr + 3, 10, &arg_num) || arg_num < 1 || + arg_num > 6) { + ret =3D -EINVAL; + goto out; + } + cfg->base =3D KWATCH_BASE_ARG1 + (arg_num - 1); + } else if (kstrtoul(dup_expr, 0, &cfg->sym_addr) =3D=3D 0) { + cfg->base =3D KWATCH_BASE_ABS_ADDR; + } else { +#if IS_BUILTIN(CONFIG_KWATCH) + cfg->sym_addr =3D kallsyms_lookup_name(dup_expr); + if (!cfg->sym_addr) { + pr_err("Failed to resolve symbol name: %s\n", dup_expr); + ret =3D -EINVAL; + goto out; + } + cfg->base =3D KWATCH_BASE_GLOBAL_SYM; +#else + pr_err("cannot resolve symbol %s when built as a module, use a hex addre= ss\n", + dup_expr); + ret =3D -EINVAL; + goto out; +#endif + } + + if (!type) + goto out; + + /* 2. Resolve Base Offset (if + or - exists) */ + if (!is_deref) { + char *next; + + *sep =3D type; /* Restore the '+' or '-' for kstrtol */ + next =3D strstr(sep, "->"); + if (next) + *next =3D '\0'; + + if (kstrtol(sep, 0, &cfg->offsets[0])) { + ret =3D -EINVAL; + goto out; + } + + p =3D next ? next + 2 : NULL; + } else { + /* Jump directly to the first dereference after '->' */ + p =3D sep + 2; + } + + /* 3. Resolve Dereference Chain */ + while (p) { + char *next; + + if (cfg->offset_count >=3D MAX_DEREF_CHAIN) { + ret =3D -E2BIG; + goto out; + } + + next =3D strstr(p, "->"); + if (next) + *next =3D '\0'; + + if (kstrtol(p, 0, &cfg->offsets[cfg->offset_count++])) { + ret =3D -EINVAL; + goto out; + } + + p =3D next ? next + 2 : NULL; + } + +out: + kfree(dup_expr); + return ret; +} diff --git a/mm/kwatch/kwatch.h b/mm/kwatch/kwatch.h new file mode 100644 index 000000000000..e1ac8ae312f6 --- /dev/null +++ b/mm/kwatch/kwatch.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _MM_KWATCH_H +#define _MM_KWATCH_H + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CONFIG_STR_LEN 512 +#define MAX_DEREF_CHAIN 4 + +struct kwatch_watchpoint; + +struct kwatch_tsk_ctx { + struct task_struct *task; + struct kwatch_watchpoint *wp; + u16 depth; + u32 epoch; +}; + +struct kwatch_watchpoint { + struct perf_event *__percpu *event; + call_single_data_t __percpu *csd_arm; + call_single_data_t __percpu *csd_disarm; + struct perf_event_attr attr; + atomic_t in_use; // multi-consumer safe get/put + struct list_head list; // for cpu online and offline + + struct task_struct *arm_tsk; + atomic_t pending_ipis; + atomic_t refcount; + bool teardown; +}; + +enum kwatch_access_type { + KWATCH_ACCESS_W, + KWATCH_ACCESS_R, + KWATCH_ACCESS_RW, + KWATCH_ACCESS_X, +}; + +enum kwatch_base_type { + KWATCH_BASE_STACK, + KWATCH_BASE_ABS_ADDR, + KWATCH_BASE_GLOBAL_SYM, + KWATCH_BASE_ARG1, + KWATCH_BASE_ARG2, + KWATCH_BASE_ARG3, + KWATCH_BASE_ARG4, + KWATCH_BASE_ARG5, + KWATCH_BASE_ARG6, +}; + +struct kwatch_config { + u16 max_watch; + char func_name[KSYM_NAME_LEN]; + u16 func_offset; + u16 depth; + u16 duration; + enum kwatch_access_type access_type; + u16 watch_len; + + /* Unified Deref Engine State */ + enum kwatch_base_type base; + char watch_expr[MAX_CONFIG_STR_LEN]; + unsigned long sym_addr; + long offsets[MAX_DEREF_CHAIN]; + u8 offset_count; + u16 max_concurrency; +}; + +int kwatch_hwbp_prealloc(u16 max_watch, enum kwatch_access_type access_typ= e); +void kwatch_hwbp_free(void); +int kwatch_hwbp_get(struct kwatch_watchpoint **out_wp); +void kwatch_hwbp_arm(struct kwatch_watchpoint *wp, unsigned long addr, u16= len); +int kwatch_hwbp_put(struct kwatch_watchpoint *wp); + +int kwatch_probe_start(struct kwatch_config *cfg); +void kwatch_probe_stop(void); +void kwatch_probe_mute(bool mute); +bool kwatch_probe_validate_hit(struct pt_regs *regs, struct task_struct *a= rm_tsk); +unsigned long kwatch_probe_nmi_rejected(void); + +int kwatch_tsk_ctx_prealloc(u16 max_concurrency); +struct kwatch_tsk_ctx *kwatch_tsk_ctx_get(bool can_alloc); +void kwatch_tsk_ctx_put(void); +void kwatch_tsk_ctx_reset(struct kwatch_tsk_ctx *ctx, u32 new_epoch); +void kwatch_tsk_ctx_release_wps(void); +void kwatch_tsk_ctx_free(void); + +void kwatch_global_anchor(unsigned long duration_sec); +int kwatch_anchor_start(u16 duration); +void kwatch_anchor_stop(void); +void kwatch_anchor_cancel_work(void); +bool kwatch_anchor_has_expired(void); +void kwatch_anchor_clear_expired(void); +void kwatch_auto_stop(void); + +int kwatch_deref_resolve(const struct kwatch_config *cfg, struct pt_regs *= regs, + unsigned long *out_addr, u16 *out_len); +int kwatch_deref_parse(struct kwatch_config *cfg, const char *watch_expr); + +#endif /* _MM_KWATCH_H */ --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qk1-f178.google.com (mail-qk1-f178.google.com [209.85.222.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3514E4156FA for ; Tue, 14 Jul 2026 18:31:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053900; cv=none; b=pfohvJR9yyVWo3quex5fE7NXjNP/CDtpTfz3YMEF/UEqvgzHXszON+BLR0L2wSTvw8JmxwGw3X7DihofNIf+Td3v64NaUgeXFwwApqVvXQKnlP2aWwcElqnlwmCIBiovboZU/KDg5ZOdb08jxSdBdxEPFpcPSwJNg0cYyqp0Rp8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053900; c=relaxed/simple; bh=OAAD1xnXyhFbGaKlMDMrY7iHeLmxhSqKZFy2RD64VyQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cLnhjDSG7VTbzfZJd2zy+wYCkyLwfzhNMgX242tFAavNUx+tCxxTscg2vN5Ni29bE0TxOPyy6Hn8qOtuIjV5stubOvzbQLOBv6z/wKsOdjKtg25U0ADXiwON/zi6G4LfgHMdxHWHe+WyTsb4zrG9scKTLobAaVQ/2DKXvY1FDbI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=SqL6QUJM; arc=none smtp.client-ip=209.85.222.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="SqL6QUJM" Received: by mail-qk1-f178.google.com with SMTP id af79cd13be357-920f33347f5so317091085a.3 for ; Tue, 14 Jul 2026 11:31:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053898; x=1784658698; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=gexNtD7XQMYc5ZpBXX88c2pnTg9Mi5LE1qEYRrtsHvs=; b=SqL6QUJMJKTccqNHgRN9cLBKaFiqXeB0EegkLpkwwn9vAplf9GLFJSaxKR2YR1hHzj oZSdhR4F4hCCPrUhUcSi4gj5VUcj1+RQK47VXVCvUtJycZCfMHqcI9rLoHMA1fKunS4E RF+aAibmDQpVRwCSHN4VBzqVMBmm0E9s/Z+5NkPC7q+cZcAvBQ6A2AArW4e3lelA6O0m SFzTIi/VIgqB6GuE/DX8S+KyARCnH0IooSluBovU//iS/ixot0A9sbXcgEQm/OyUuTEB SaFt7bPBGIi3EycNJtYTpwsFZmG6NngWZ4eBg+K8B0Yqupse/wx/nU2tFuzoswAE30Q7 mgGw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053898; x=1784658698; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=gexNtD7XQMYc5ZpBXX88c2pnTg9Mi5LE1qEYRrtsHvs=; b=bJ9prGM5HOhHx1KnqgS54aGh5HUce4aeUNX+1frCDfI22MmhvLEzr/iTM3nOfHnN0+ P8s5WZ7IZtiP0o+E5/7+dhYMMISkXhqLrWtv3/3P6mnU01yYWAdajid4BXnocMIZAaiH v7f5yJQbSHnfIHIDMK7OzXb/zzEtsmxLeaIDcLMOgnO+zwan6YR8BRJvYyvZDxcM88yB vG23z9kRmQqc2VaXDkD/S0mkQndVIKZ1rFF+MnwUBt9R1g61qshJ2VcBRsfTyeRxiEwt GRzoneyAtJWOS1WWlSFoZbRykMCy8n+pSgBDaW3JtHFZnr1EfCzwv2qRpU4zjCnRcRFc OlfQ== X-Forwarded-Encrypted: i=1; AHgh+Rr/Rv593S3fXO89xd/ylq1rlUYyO0n+H4Hbj0cqzQKimvljW2DObIUxEWEDQG9khW+iRI6fG3jR+8Gla0s=@vger.kernel.org X-Gm-Message-State: AOJu0Yy0oWU6+r6/DHne8d4sR3DE/8PRNfmqD05BeZ6JVOtGvUX73MjL 2maWN7REApofAPjv+8jH0b1KYkffoEDIo6gacuOqvkidqfa/i60B0yt1 X-Gm-Gg: AfdE7cnG0Maf25wYwhpUxz/M3f7Hz8jTYybwRqkzIrkoHrLD6vSU/R9TboA1ayVlg5P CTdNF9b255Qwb/rtivpiknKGR9BH/Dq8xScFnXsUC2cUEa+imkhBF1ZXjKmC1sGiOz2HiYi/Phg Yj8xwUPjNHA9IC2Pc5zx6h/Q7AhbEmjg0xoXdbNnQkySimh4SDrzaMrWWaghnebbdadDWmnOl9L 7Zt5MXj8LyYMFK1Cy/N0jVcZW32dRUIJt990bQAeSunTHuKSCkUdcHqub8PvsFhrLXQJb+Ck1Rh YRzipyqC18n2LEXUcI1TvrtywjI7dZTebfbLz+V3jPWAvzzCO3vcMk2T2VgOpHNLawb6zrMEAey DE+y5rpAudeqJyqBvJtECpWuFvmdujIdbKE8gE2qqSAZbzDfTx0OuzROskU2X3dK4x0uVJJKGaf AOp6hn2LUFKhYI6qGmJMaVhxofCTqNH/hTO7WHUDdos9LBACMJlrNpkVaaeGYHoQ== X-Received: by 2002:a05:620a:40d6:b0:930:927f:f81f with SMTP id af79cd13be357-930927ff9b4mr130636785a.91.1784053897781; Tue, 14 Jul 2026 11:31:37 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id af79cd13be357-92ee5bb3542sm1473718285a.20.2026.07.14.11.31.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:31:37 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 06/13] mm/kwatch: add lockless per-task context pool Date: Wed, 15 Jul 2026 02:31:29 +0800 Message-ID: <20260714183129.12542-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A task that enters the watched function needs somewhere to keep its window state (nesting depth, owned watchpoint, config epoch). The lookup runs in kprobe and NMI-like contexts, so it must not allocate or take locks. Use a preallocated open-addressing array hashed by task_struct pointer. Slots are claimed with cmpxchg() and released with smp_store_release(); lookup is a read-only probe sequence. The pool size (max_concurrency) bounds how many tasks can be inside watch windows concurrently; excess tasks are simply not tracked. Signed-off-by: Jinchao Wang --- mm/kwatch/Makefile | 2 +- mm/kwatch/task_ctx.c | 105 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 mm/kwatch/task_ctx.c diff --git a/mm/kwatch/Makefile b/mm/kwatch/Makefile index 69c21ae62123..cc6574df0d68 100644 --- a/mm/kwatch/Makefile +++ b/mm/kwatch/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_KWATCH) +=3D kwatch.o =20 -kwatch-y :=3D deref.o +kwatch-y :=3D deref.o task_ctx.o diff --git a/mm/kwatch/task_ctx.c b/mm/kwatch/task_ctx.c new file mode 100644 index 000000000000..f8e582f0dcfe --- /dev/null +++ b/mm/kwatch/task_ctx.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include "kwatch.h" + +static u16 kwatch_ctx_pool_size; +static u16 kwatch_ctx_pool_mask; + +static struct kwatch_tsk_ctx *kwatch_ctx_pool; + +int kwatch_tsk_ctx_prealloc(u16 max_concurrency) +{ + if (!max_concurrency) + max_concurrency =3D 256; + + kwatch_ctx_pool_size =3D roundup_pow_of_two(max_concurrency); + kwatch_ctx_pool_mask =3D kwatch_ctx_pool_size - 1; + + if (unlikely(!kwatch_ctx_pool)) { + kwatch_ctx_pool =3D kcalloc(kwatch_ctx_pool_size, + sizeof(struct kwatch_tsk_ctx), + GFP_KERNEL); + if (!kwatch_ctx_pool) + return -ENOMEM; + } + return 0; +} + +struct kwatch_tsk_ctx *kwatch_tsk_ctx_get(bool can_alloc) +{ + int start_idx, i, idx; + struct task_struct *t; + + if (unlikely(!kwatch_ctx_pool)) + return NULL; + + start_idx =3D hash_ptr(current, ilog2(kwatch_ctx_pool_size)); + + for (i =3D 0; i < kwatch_ctx_pool_size; i++) { + idx =3D (start_idx + i) & kwatch_ctx_pool_mask; + t =3D READ_ONCE(kwatch_ctx_pool[idx].task); + if (t =3D=3D current) + return &kwatch_ctx_pool[idx]; + } + + if (!can_alloc) + return NULL; + + for (i =3D 0; i < kwatch_ctx_pool_size; i++) { + idx =3D (start_idx + i) & kwatch_ctx_pool_mask; + t =3D READ_ONCE(kwatch_ctx_pool[idx].task); + if (!t) { + if (!cmpxchg(&kwatch_ctx_pool[idx].task, NULL, current)) + return &kwatch_ctx_pool[idx]; + } + } + + return NULL; +} + +void kwatch_tsk_ctx_reset(struct kwatch_tsk_ctx *ctx, u32 new_epoch) +{ + struct kwatch_watchpoint *wp =3D xchg(&ctx->wp, NULL); + + if (wp) + kwatch_hwbp_put(wp); + ctx->depth =3D 0; + ctx->epoch =3D new_epoch; +} + +void kwatch_tsk_ctx_put(void) +{ + struct kwatch_tsk_ctx *ctx =3D kwatch_tsk_ctx_get(false); + + if (unlikely(!ctx)) + return; + + kwatch_tsk_ctx_reset(ctx, 0); + + /* Pairs with READ_ONCE() in kwatch_tsk_ctx_get() */ + smp_store_release(&ctx->task, NULL); +} + +void kwatch_tsk_ctx_release_wps(void) +{ + int i; + + if (!kwatch_ctx_pool) + return; + + for (i =3D 0; i < kwatch_ctx_pool_size; i++) { + struct kwatch_watchpoint *wp =3D xchg(&kwatch_ctx_pool[i].wp, + NULL); + if (wp) + kwatch_hwbp_put(wp); + } +} + +void kwatch_tsk_ctx_free(void) +{ + kfree(kwatch_ctx_pool); + kwatch_ctx_pool =3D NULL; +} --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qv1-f46.google.com (mail-qv1-f46.google.com [209.85.219.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E4C7C417BF8 for ; Tue, 14 Jul 2026 18:31:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053922; cv=none; b=hvNVsjeHkQ2fSsS36lkwEB5Uj1ODg9L0wGzskFyCnXnBlWRlBj4oZPw5jUE93kvr9L9LUjYIdXjRfM7QpahpolOd8OTukwMmcfN5f8StU9msZJ/eYy/4omB6AgHvA3sU3o0yef8Ozr5I+cfNJwuLZMC3CXdlPOwXw65Xix0dVwY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053922; c=relaxed/simple; bh=VjO/40vwtlEqoqa0MWjtEwISafs1DuSDXBphWFAVeM8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=h1yK2N3Yhzoyd2PCdlw8UVozOaBewNUgVhV2M9cOxSbQY68qnstEGmtpJf3ZaPclLHHbw/7NhJGw8lchDhv11WwDDWLICEBphbtnaEwrbmBLyUY5IVjQTBQ1d/E6fdWQA7lFXWP3Kay4hUjtkN81cUy8JbclaPrzvMfxV3PMgY8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=MU2gdJms; arc=none smtp.client-ip=209.85.219.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="MU2gdJms" Received: by mail-qv1-f46.google.com with SMTP id 6a1803df08f44-8eefd0c5f59so14067586d6.3 for ; Tue, 14 Jul 2026 11:31:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053919; x=1784658719; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=KwQ36ycy2027O2OnXTT+jaKQ0lX0IY/IjFba+f+HbbQ=; b=MU2gdJmsxzKlkum3NlI0Bt6u3MFh3Ge5SRJ6lQACJSbB9X94H807fYOy5FY9pHzcLk FAPshpyZbymGqiTrfInEYL+xJM6h/oaOOpjzr1etSvudNc5wz42NKh2EA02P52g8NJ/X Vz8ujfqLmPfBh3LaLE/6h9bj/JEwNDdrLPpoFVuoZiGB2o/izJeNX70lG6X6gJsBGKQG oO+O2JmhN7hUbkx/eE6O3VuTqbwhj0OotnbPsIYddQnQu7cNIj+14bmB7zPHDYpY569J aaDJHcM0taT1bB8QAmbad63CSfgofVa6PXb0YdjckVRWTbaSsv/euNsp6FtUFy6fywaj dD+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053919; x=1784658719; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=KwQ36ycy2027O2OnXTT+jaKQ0lX0IY/IjFba+f+HbbQ=; b=LXrSVesdJ6tcI0QXumv9Ka+VBLwQE5BcBke4EY2YRBMUrB4Tnl6Samh/ogXlgX5z5H 12hWux6eMgzdL0lwi+NLoOkiEV32NysZsy4bfBR6p1sZI4zxcWwKoovkzEHCt65JLQ5N 9PNSbMrjVqxl8NAz3/kh0mAvzv/71Ofi24tgXZVc/rLkUi0mUCcCla6AbBQfozpboPzm OAEg614a/cqMVsAgBIhXIcqh9/T/JDmM9tk40feH+NolZTy4kPwVnE+1ukvG0W9SR16i jOJLC4uDwmxU54JXE1gdP/Yjyx/OuFC1iRcluokHUlIDw9BaRRhPG3JcRsr5mN+4ZtkF 4fYA== X-Forwarded-Encrypted: i=1; AHgh+RoVBz/bErG4ylNt3kl4HZjX8U+gZxWht3C4f3fyyYbJAbFnaSZ9LWS4MWLiGHKI82kLMJ2SmlUBHWl2awQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyJOayWHjF76hbHQRh7WI9wtMzJiX47ky1GoIGMJ9CNaf0vFixH HjINdaUwBc0ghxlBaWS6L4U5HJwx1vkGTXlMtqicY0A1Do+DZMt4iuI0 X-Gm-Gg: AfdE7ckThI8+Pc21mvnc2N6DiPID9dONZWTUDl7/ZAACIwju87FVY4MWBi3xVw3Uokj 4OQBxPUkmwZqFHmyRZQix4ZEBhXcCBYoxLBJrrGKt75oSNm16VGDMg1P/bapAnmRmT1PKfBaakb Qr9uL4ls3p6w4oFKNYvGxd5brfnFac0VpwoDM2I3KrC9+0NMrwY/1sjJiSbLZPL9xU36kWzVHhC U5QXMb5zX85WYqMw1aV1ARDBzoIKEIin1R2r01toIq8ZoFH1cBTFbYjQau92qAL0MpNiITs0nz0 EN03KKkFXjVOiGlWigeKoXDVBWh5/F8wq1wd3CoD5nurshbIqyNUbzxprqc0hGLnq65zs53GrDl EuSmIF/mYeEUADZBe4D12ClCYv2crnf8NzKg3zfJMbdnUx2QUxWBJ3iwp2XWyrYQlAvVHl4f34I T+M/wHvGDZLowiplOzPTJEhiWYtD7CXWmLx4eo70XRVCw1tIwTagqd60quu/bWfg== X-Received: by 2002:a05:6214:3d0f:b0:8cc:ea7f:d3c8 with SMTP id 6a1803df08f44-90401578bbbmr161798496d6.30.1784053918640; Tue, 14 Jul 2026 11:31:58 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ffd87cacb8sm178386286d6.49.2026.07.14.11.31.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:31:58 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 07/13] stacktrace: export stack_trace_save_regs() Date: Wed, 15 Jul 2026 02:31:46 +0800 Message-ID: <20260714183146.12598-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The other stack_trace_save_*() flavours are exported, but the regs variant is not, so no module can capture a stack trace for a given pt_regs. KWatch, which may be built as a module, uses it to record who wrote to a watched address from the hardware breakpoint handler. Export it like its siblings. Signed-off-by: Jinchao Wang --- kernel/stacktrace.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c index afb3c116da91..d853c40f916b 100644 --- a/kernel/stacktrace.c +++ b/kernel/stacktrace.c @@ -175,6 +175,7 @@ unsigned int stack_trace_save_regs(struct pt_regs *regs= , unsigned long *store, arch_stack_walk(consume_entry, &c, current, regs); return c.len; } +EXPORT_SYMBOL_GPL(stack_trace_save_regs); =20 #ifdef CONFIG_HAVE_RELIABLE_STACKTRACE /** @@ -325,6 +326,7 @@ unsigned int stack_trace_save_regs(struct pt_regs *regs= , unsigned long *store, save_stack_trace_regs(regs, &trace); return trace.nr_entries; } +EXPORT_SYMBOL_GPL(stack_trace_save_regs); =20 #ifdef CONFIG_HAVE_RELIABLE_STACKTRACE /** --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qk1-f170.google.com (mail-qk1-f170.google.com [209.85.222.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 05B883D813C for ; Tue, 14 Jul 2026 18:32:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053941; cv=none; b=csPfyBy2Tp8d/FLuD+eTpHIJg1Vs8M6GQZbfCLGyacociyRD/FviJ/rD+pdWU8lpHN4HN9QyU8CSjQFzcibHDHxpgdP3LLgp1nX6dJr69+CXOxrVHpqqUhvLXJhKBPh4/23KgpIylyGyeZVEIKGwfesz5/LQuOE+0wJXIAdYH+Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053941; c=relaxed/simple; bh=qwHJQSkHR9FiX9eLZJcFQ7+CaEXGbJDg7NFSWEISO8A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IwKFns1BQ1lGry2kxJ+ILHVh3i+FWnHIg8w5VzJqA5k2C+NNYzBO5Y4KbeHN0ahOWpL2FauVKgQ3/xjsSrKOW7mp1LMP13ll6aBl2kedweQPo3rkc78l4ZiZT7Jv2TwYPmnl7Cl9NWnqsKrl2nY0UFGd4liggg8u/HdpEafk03s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=RnPa9xbu; arc=none smtp.client-ip=209.85.222.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RnPa9xbu" Received: by mail-qk1-f170.google.com with SMTP id af79cd13be357-92e6a434cabso99458885a.1 for ; Tue, 14 Jul 2026 11:32:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053939; x=1784658739; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=2DejjWbYnVcQ4XXUkfoVFnqVdAcjxTlD3K8bB22eXeE=; b=RnPa9xbuls1QpCNB8wPSIG46xMbL0IuXquMY5ZzlF7HORy8c1PfGbLBFvsGu+zo/p9 K8+Jmbl0GZXmY9Ktz0UM10FCgbIbF30Pmuu9ptlMau1PwUHFggO73fpGlsbrNDGou1hT lVfWl9NmvRMKD/oCctu6onxWGAJ612i+A4GNd7OXoULIZ7Ow9VNudh4FcGSME4G2y0es HgwwIdLZhL5fKw2qiwYbqnFVckKCYzczy177wAT/kxetJyaZVuo9pkrkJyOpcI6Dydq+ ep9KKkpSyWa3H1VktpFsNqmL+IE8uEgDnzx0i2/1XPWjUFg2qjjxrljEjXsGvOErEhQs 1+LQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053939; x=1784658739; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=2DejjWbYnVcQ4XXUkfoVFnqVdAcjxTlD3K8bB22eXeE=; b=jbEuwJbahMIYZ8d6of27TV8EswMo8DxX8uNYyivWcioyHOY2crLs6TfqkOq4CUSV/R QD6mg0cn9ivCzReu6yRFthGmpUJP83S1wTpgew2XK9XrQw7iIUEf+UmU9LT64FqDodrj aq1GwdnWm1RAiF2q8iT4iDhJJCDfR7+D3SJSIgXExa05cpxZzEryU6Fl+FtqfPeREJyC AWajbvDtcAGE+iCbhUFEItowJFDeW4Ab86U5CNXyCXE2QOnoCMxAUj4pT7urM+7uoyM6 es60csnANFknEkzZQsLiSBC6uqcLUMmWpm8Lx2O2zqnRYFfCHeW3Kre8gY3L8aEJtk7d ANCw== X-Forwarded-Encrypted: i=1; AHgh+Rqcfh9Ml1wOksQm2cArfpPOnh75alB/rCO1310zAlpdZALAx5+KpQJPVVooTB2grF2UWdtD26WJeoOCqes=@vger.kernel.org X-Gm-Message-State: AOJu0YxBGPIiwRx1kEWKIM4jnDfJPSG8I3Y9mZzexOXm7j58UJEMXVRQ yV448LV4s0J/IWyT612H1uN/yfP44CFQUnXol6YrCKmwRiCPFNaCUjN/ X-Gm-Gg: AfdE7cnkpCrpZf7Ip8rG3hGAHzfXkvVHiiFchJT7PIP0GYIg09Whi+hw5P/4z37h463 TQkq49Qw0ZPH9SDBhgCRVah+WEUn395SEnuVtkRo0wfRzLqw2CD3JVk09PieVxv2DqvHOtpLJ6B 9gPo2ciy32mT4IHBduM/cckAlbmG31gfEMJtjx6zYs1V4znxjnVPWkl3rNOPMcOgfQu6tTetlyS 8DuxzEKovaCjlP8v+fMVCOzL01/3+gTJNoIgYjfpfvPXPARRcXWXWMw8k/W5yzv+oghhu2W3tKG kxTygS/UPU0IBdavj1KEOPYELxy2pwh0+Eh5q2HrAZKBP6T/B/sQ/43y2234lifTPlB5FqEOM2v MJcIwUuRhQjit7Z3K18laAl/zYuxmoKeiCBrSGnNZ4nKwgJ7fu6eSXe/pUq0ZKg5yn2TCVvqxrT /nBYZfCNnow2SSU6CqQIfxSi4rjNfde4PmjHVWbtjmhfJ0qN6x69M= X-Received: by 2002:a05:620a:460c:b0:92e:6637:d98 with SMTP id af79cd13be357-92ef2b35181mr1396308185a.18.1784053938726; Tue, 14 Jul 2026 11:32:18 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id af79cd13be357-92ee5bab293sm1547341685a.18.2026.07.14.11.32.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:32:17 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 08/13] mm/kwatch: add hardware breakpoint backend Date: Wed, 15 Jul 2026 02:32:06 +0800 Message-ID: <20260714183206.12688-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Manage a preallocated pool of wide (per-CPU) perf hardware breakpoints. All breakpoints are registered up front against a dummy address; arming a watchpoint only re-points an already-registered event, so the arm path can run from a kprobe handler. - kwatch_hwbp_get()/put() claim and release pool entries with per-slot cmpxchg, safe for concurrent consumers on any CPU. - kwatch_hwbp_arm() updates the local CPU synchronously via modify_wide_hw_breakpoint_local() and broadcasts asynchronous IPIs to the other CPUs. Arm-side IPIs are rate-limited per CPU; disarm IPIs are refcounted so an entry is only recycled once every CPU has dropped it. - Hits are reported through the kwatch:kwatch_hit tracepoint with a short stack trace: the ftrace ring buffer is usable from NMI-like context and survives a subsequent crash, unlike printk. - A CPU hotplug callback creates/destroys the per-CPU events as CPUs come and go. Signed-off-by: Jinchao Wang --- include/trace/events/kwatch.h | 57 ++++++ mm/kwatch/Makefile | 2 +- mm/kwatch/hwbp.c | 358 ++++++++++++++++++++++++++++++++++ 3 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 include/trace/events/kwatch.h create mode 100644 mm/kwatch/hwbp.c diff --git a/include/trace/events/kwatch.h b/include/trace/events/kwatch.h new file mode 100644 index 000000000000..edb95405c386 --- /dev/null +++ b/include/trace/events/kwatch.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM kwatch + +#if !defined(_TRACE_KWATCH_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_KWATCH_H + +#include +#include + +#define KWATCH_STACK_DEPTH 8 + +struct trace_seq; +const char *kwatch_trace_print_stack(struct trace_seq *p, + const unsigned long *stack, + unsigned int nr); + +TRACE_EVENT(kwatch_hit, + TP_PROTO(unsigned long ip, unsigned long sp, unsigned long addr, + u64 time_ns, + unsigned long *stack_entries, unsigned int stack_nr), + TP_ARGS(ip, sp, addr, time_ns, stack_entries, stack_nr), + + TP_STRUCT__entry( + __field(unsigned long, ip) + __field(unsigned long, sp) + __field(unsigned long, addr) + __field(u64, time_ns) + __field(unsigned int, stack_nr) + __array(unsigned long, stack, KWATCH_STACK_DEPTH) + ), + + TP_fast_assign( + unsigned int i; + + __entry->ip =3D ip; + __entry->sp =3D sp; + __entry->addr =3D addr; + __entry->time_ns =3D time_ns; + __entry->stack_nr =3D min_t(unsigned int, stack_nr, + KWATCH_STACK_DEPTH); + for (i =3D 0; i < __entry->stack_nr; i++) + __entry->stack[i] =3D stack_entries[i]; + ), + + TP_printk("KWatch HIT: time=3D%llu.%06lu ip=3D%pS addr=3D0x%lx%s", + __entry->time_ns / 1000000000ULL, + (unsigned long)((__entry->time_ns / 1000ULL) % 1000000ULL), + (void *)__entry->ip, __entry->addr, + kwatch_trace_print_stack(p, __entry->stack, + __entry->stack_nr)) +); + +#endif /* _TRACE_KWATCH_H */ + +/* This part must be outside protection */ +#include diff --git a/mm/kwatch/Makefile b/mm/kwatch/Makefile index cc6574df0d68..b2bc3003c89b 100644 --- a/mm/kwatch/Makefile +++ b/mm/kwatch/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_KWATCH) +=3D kwatch.o =20 -kwatch-y :=3D deref.o task_ctx.o +kwatch-y :=3D deref.o task_ctx.o hwbp.o diff --git a/mm/kwatch/hwbp.c b/mm/kwatch/hwbp.c new file mode 100644 index 000000000000..19498ba03826 --- /dev/null +++ b/mm/kwatch/hwbp.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kwatch.h" + +static LIST_HEAD(kwatch_all_wp_list); +static struct kwatch_watchpoint **kwatch_wp_slots; +static u16 kwatch_wp_nr; +static DEFINE_MUTEX(kwatch_all_wp_mutex); +static unsigned long kwatch_dummy_holder __aligned(8); +static int kwatch_hwbp_cpuhp_state =3D CPUHP_INVALID; + +#define CREATE_TRACE_POINTS +#include + +/* + * Render the saved stack like the ftrace built-in stacktrace / dump_stack= () + * style. Symbol resolution runs at trace read time, not in the hit path. + */ +const char *kwatch_trace_print_stack(struct trace_seq *p, + const unsigned long *stack, + unsigned int nr) +{ + const char *ret =3D trace_seq_buffer_ptr(p); + unsigned int i; + + for (i =3D 0; i < nr; i++) + trace_seq_printf(p, "\n =3D> %pS", (void *)stack[i]); + trace_seq_putc(p, 0); + return ret; +} + +static void kwatch_hwbp_handler(struct perf_event *bp, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct kwatch_watchpoint *wp =3D bp->overflow_handler_context; + unsigned long stack_entries[KWATCH_STACK_DEPTH]; + unsigned int stack_nr; + + if (!kwatch_probe_validate_hit(regs, wp->arm_tsk)) + return; + + stack_nr =3D stack_trace_save_regs(regs, stack_entries, KWATCH_STACK_DEPT= H, 2); + trace_kwatch_hit(instruction_pointer(regs), kernel_stack_pointer(regs), + wp->attr.bp_addr, local_clock(), + stack_entries, stack_nr); +} + +static void kwatch_hwbp_arm_local(void *info) +{ + struct kwatch_watchpoint *wp =3D info; + struct perf_event *bp; + unsigned long flags; + int cpu, err; + + local_irq_save(flags); + + cpu =3D smp_processor_id(); + bp =3D per_cpu(*wp->event, cpu); + + if (unlikely(!bp)) + goto out; + + kwatch_probe_mute(true); + barrier(); + + err =3D modify_wide_hw_breakpoint_local(bp, &wp->attr); + if (unlikely(err)) { + WARN_ONCE(1, + "KWatch: HWBP reinstall failed on CPU%d (err=3D%d, addr=3D0x%llx, len= =3D%llu)\n", + cpu, err, wp->attr.bp_addr, wp->attr.bp_len); + } + + barrier(); + kwatch_probe_mute(false); + +out: + local_irq_restore(flags); +} + +static inline void kwatch_hwbp_try_recycle(struct kwatch_watchpoint *wp) +{ + if (atomic_dec_and_test(&wp->pending_ipis)) { + if (!READ_ONCE(wp->teardown)) + atomic_set_release(&wp->in_use, 0); + + atomic_dec(&wp->refcount); + } +} + +static void kwatch_hwbp_disarm_local(void *info) +{ + struct kwatch_watchpoint *wp =3D info; + + kwatch_hwbp_arm_local(info); + kwatch_hwbp_try_recycle(wp); +} + +static int kwatch_hwbp_cpu_online(unsigned int cpu) +{ + struct perf_event_attr attr; + struct kwatch_watchpoint *wp; + struct perf_event *bp; + + mutex_lock(&kwatch_all_wp_mutex); + list_for_each_entry(wp, &kwatch_all_wp_list, list) { + attr =3D wp->attr; + attr.bp_addr =3D (unsigned long)&kwatch_dummy_holder; + bp =3D perf_event_create_kernel_counter(&attr, cpu, NULL, + kwatch_hwbp_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) =3D bp; + } + mutex_unlock(&kwatch_all_wp_mutex); + return 0; +} + +static int kwatch_hwbp_cpu_offline(unsigned int cpu) +{ + struct kwatch_watchpoint *wp; + struct perf_event *bp; + + mutex_lock(&kwatch_all_wp_mutex); + list_for_each_entry(wp, &kwatch_all_wp_list, list) { + bp =3D per_cpu(*wp->event, cpu); + if (bp) { + unregister_hw_breakpoint(bp); + per_cpu(*wp->event, cpu) =3D NULL; + } + } + mutex_unlock(&kwatch_all_wp_mutex); + return 0; +} + +int kwatch_hwbp_get(struct kwatch_watchpoint **out_wp) +{ + struct kwatch_watchpoint *wp; + int i; + + /* + * Per-slot cmpxchg claim: safe for concurrent consumers on any CPU, + * unlike llist_del_first() which requires a single consumer. + */ + for (i =3D 0; i < kwatch_wp_nr; i++) { + wp =3D kwatch_wp_slots[i]; + if (atomic_read(&wp->in_use)) + continue; + if (atomic_cmpxchg(&wp->in_use, 0, 1) =3D=3D 0) { + atomic_inc(&wp->refcount); + *out_wp =3D wp; + return 0; + } + } + return -EBUSY; +} + +void kwatch_hwbp_arm(struct kwatch_watchpoint *wp, unsigned long addr, u16= len) +{ + static DEFINE_PER_CPU(u64, last_ipi_time); + int cur_cpu; + call_single_data_t *csd; + int cpu; + bool is_disarm =3D (addr =3D=3D (unsigned long)&kwatch_dummy_holder); + + wp->attr.bp_addr =3D addr; + wp->attr.bp_len =3D len; + + if (!is_disarm) + wp->arm_tsk =3D current; + + /* ensure attr update visible to other cpu before sending IPI */ + smp_wmb(); + + atomic_set(&wp->pending_ipis, 1); + cur_cpu =3D get_cpu(); + + if (!is_disarm) { + u64 now =3D local_clock(); + u64 last =3D this_cpu_read(last_ipi_time); + + if (now - last < 1000000ULL) { + put_cpu(); + return; + } + this_cpu_write(last_ipi_time, now); + } + for_each_online_cpu(cpu) { + if (cpu =3D=3D cur_cpu) + continue; + + if (is_disarm) + atomic_inc(&wp->pending_ipis); + + csd =3D per_cpu_ptr(is_disarm ? wp->csd_disarm : wp->csd_arm, + cpu); + if (smp_call_function_single_async(cpu, csd) && is_disarm) + kwatch_hwbp_try_recycle(wp); + } + put_cpu(); + + if (is_disarm) + kwatch_hwbp_disarm_local(wp); + else + kwatch_hwbp_arm_local(wp); +} + +int kwatch_hwbp_put(struct kwatch_watchpoint *wp) +{ + kwatch_hwbp_arm(wp, (unsigned long)&kwatch_dummy_holder, + sizeof(unsigned long)); + + return 0; +} + +void kwatch_hwbp_free(void) +{ + struct kwatch_watchpoint *wp, *tmp; + + kwatch_wp_nr =3D 0; + kfree(kwatch_wp_slots); + kwatch_wp_slots =3D NULL; + + if (kwatch_hwbp_cpuhp_state !=3D CPUHP_INVALID) { + cpuhp_remove_state_nocalls(kwatch_hwbp_cpuhp_state); + kwatch_hwbp_cpuhp_state =3D CPUHP_INVALID; + } + + mutex_lock(&kwatch_all_wp_mutex); + list_for_each_entry_safe(wp, tmp, &kwatch_all_wp_list, list) { + list_del(&wp->list); + + WRITE_ONCE(wp->teardown, true); + atomic_dec(&wp->refcount); + + /* Wait for all async IPIs to finish */ + while (atomic_read(&wp->refcount) > 0) + cpu_relax(); + + unregister_wide_hw_breakpoint(wp->event); + free_percpu(wp->csd_arm); + free_percpu(wp->csd_disarm); + kfree(wp); + } + mutex_unlock(&kwatch_all_wp_mutex); +} + +int kwatch_hwbp_prealloc(u16 max_watch, enum kwatch_access_type access_typ= e) +{ + struct kwatch_watchpoint *wp; + int success =3D 0, cpu; + u32 bp_type; + int ret; + + switch (access_type) { + case KWATCH_ACCESS_X: + bp_type =3D HW_BREAKPOINT_X; + break; + case KWATCH_ACCESS_R: + bp_type =3D HW_BREAKPOINT_R; + break; + case KWATCH_ACCESS_RW: + bp_type =3D HW_BREAKPOINT_RW; + break; + case KWATCH_ACCESS_W: + default: + bp_type =3D HW_BREAKPOINT_W; + break; + } + + while (!max_watch || success < max_watch) { + wp =3D kzalloc_obj(*wp); + if (!wp) + break; + + wp->csd_arm =3D alloc_percpu(call_single_data_t); + wp->csd_disarm =3D alloc_percpu(call_single_data_t); + if (!wp->csd_arm || !wp->csd_disarm) { + free_percpu(wp->csd_arm); + free_percpu(wp->csd_disarm); + kfree(wp); + break; + } + + for_each_possible_cpu(cpu) { + INIT_CSD(per_cpu_ptr(wp->csd_arm, cpu), + kwatch_hwbp_arm_local, wp); + INIT_CSD(per_cpu_ptr(wp->csd_disarm, cpu), + kwatch_hwbp_disarm_local, wp); + } + + wp->teardown =3D false; + + hw_breakpoint_init(&wp->attr); + wp->attr.bp_addr =3D (unsigned long)&kwatch_dummy_holder; + wp->attr.bp_len =3D sizeof(unsigned long); + wp->attr.bp_type =3D bp_type; + + wp->event =3D register_wide_hw_breakpoint(&wp->attr, + kwatch_hwbp_handler, + wp); + if (IS_ERR((void *)wp->event)) { + free_percpu(wp->csd_arm); + free_percpu(wp->csd_disarm); + kfree(wp); + break; + } + + atomic_set(&wp->refcount, 1); + + mutex_lock(&kwatch_all_wp_mutex); + list_add(&wp->list, &kwatch_all_wp_list); + mutex_unlock(&kwatch_all_wp_mutex); + success++; + } + + if (!success) + return -EBUSY; + + kwatch_wp_slots =3D kcalloc(success, sizeof(*kwatch_wp_slots), + GFP_KERNEL); + if (!kwatch_wp_slots) { + kwatch_hwbp_free(); + return -ENOMEM; + } + mutex_lock(&kwatch_all_wp_mutex); + list_for_each_entry(wp, &kwatch_all_wp_list, list) + kwatch_wp_slots[kwatch_wp_nr++] =3D wp; + mutex_unlock(&kwatch_all_wp_mutex); + + ret =3D cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "kwatch:online", + kwatch_hwbp_cpu_online, + kwatch_hwbp_cpu_offline); + if (ret < 0) { + kwatch_hwbp_free(); + return ret; + } + + kwatch_hwbp_cpuhp_state =3D ret; + return 0; +} --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qk1-f173.google.com (mail-qk1-f173.google.com [209.85.222.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2E18C43C05E for ; Tue, 14 Jul 2026 18:32:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053967; cv=none; b=hEoPkpu3yWNWLxYaC8I78TBCKkVmFMBzp8z4wBqwNibGmD1ocoV/uRp4dAsVujxWYdAC3FCrGH/jEZB+iSF84cEXQWQ/XXsU0Jtw7XblxHvalndzqlcOfypcf4NNwyZTFqat/gensauu7YBv1Gk9zx3J/puhGi8wuCWd3jfHzlI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053967; c=relaxed/simple; bh=U8Z0nvRnaFFrw3lPHvPCl/wITya9sSFlUIbcZxe00bU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=j/oEgSah6EdKtFTBHI6BPWiIYgJKoTtAV8sPeLK+NHHfelIJon5LNLlBO3XSx70rSOU9lukN6K2LTNDM6SmIn0/olU/amP3MqLVNxfdo9+zXn5WsegIyaBXfIUcWCND4BKNzowTpxzNR8Hw6yPFpSHCj3p0UqdUtI9e/JB57KpE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=l6FYtvg9; arc=none smtp.client-ip=209.85.222.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="l6FYtvg9" Received: by mail-qk1-f173.google.com with SMTP id af79cd13be357-92f03daaa97so252946585a.2 for ; Tue, 14 Jul 2026 11:32:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053965; x=1784658765; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=JG+YXpLaf6eTeKDHjsfPWQsS8e418Ifa944UVLgQD7U=; b=l6FYtvg9K7qTr9PRUV9BLwvHtUGDx48DgF7q5vOQqGr0YH7Q14H5Kodm8HCyVugJl3 4e+LaK37kdjGNqYiLjxI8ECiu+ZUIFV0y0OP+KCadlKsdUD40QRV1F6EeGK71rxxB5HE BUizXm9wvsLq+Wa50677s7e+fg3UlcEWyk8iJndeZjadc296WmkqBbR67JvWsrZXiGbb gmzwKwE3hFBVJkXwLD1XCai9/+ias7Ge1D/ANkcEDMOqA5uF8Z1gDpqwcFrRU9E8HVnc hRWSXYsBdAxnn0YTl1OxnKe7zoWHg5nc/piaWsjXS0dROWE2pruHRC+VwUdqGRfxA9gs qokA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053965; x=1784658765; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=JG+YXpLaf6eTeKDHjsfPWQsS8e418Ifa944UVLgQD7U=; b=dZx98Dc6MnUhkDkKtz/sE3MzuwW1k0cSXjf3FRjdG79I/M2gFp6PE/XQuvh7mWLbqQ hn4Tl3uTt9+6DLsVwrdTzqkRNA1gnY4cXtk8jghnJPblw8FCnIZ1QmidB4MmFqCkAHAX D1H1q3JfGVHA3mL5KuD+7nMhe8i5eRufJnlXgl6+ThLf4dKr+SDwR0RSTJIX2oxQ6KIP YdBIOtbVCSr2TJnf+g/Zb5nOj1Qk/43DCi9IOTtEk9GmPc1BxcBFcm0FOxwBtquy1V+/ hCtmjOYG5s0NOuiJr1CjChY5dpPGbVguefxJdNHxFnpPfdtpgfjJABPtrH/O/9L2x8nf gvpA== X-Forwarded-Encrypted: i=1; AHgh+RpnvAP43fZ2vvmZCzIsCTMg4RMsN3VCcl/ceGK09hkqr2JetSHKUo0RqtF1M8boqTSBAgQhXNF/gXfvJQU=@vger.kernel.org X-Gm-Message-State: AOJu0Yx4Lx9enYTA80+4UMct03ViLut4EjufBSJij9ScQWrIFu10spNR FCh2nQdbDCfH83RpUqb/MUUk0elNbbLuJ0z6wpDuGq8b5VPY2C9pF0Uj X-Gm-Gg: AfdE7cnl/A7v6adGuhUXgFWIpTKba61ZpH3oumCcd/iKPeFtKdh/I7PxNCN1XpUxaCU y4vg/DA92R+AS906RCzbHoV3Ggk7a5A1NDTX5qmutHtiMZsEVVWrCdM7+ZARob/iQfaHUdi3JxP e52F4TKoDIkV3ZJlNj6F+tlSRkxFsUue8Tfff1uBG5+EibVfHHa1SIw5+VvCQt+mKsW9nLpXuNd VQOeHnjf96FEDJ8h+y8OdEUQpBapt5ZiNhHiY5RTKaUaLUwYpGHXMk19uONhUYBeolxVOKCL66Y 6NG/B8SfCuJd5JRb8r2XjafZlXmcS22Hwv/K/ffstpnngDHn6Ck+b1AFxT+WCXcdo0yrvTnEBWW //N59zTAYnJAzVL8d3QHHLeryEgMTpsmg7nslYXNlRj20/Jd9yw7P8uLQ2RgnHrd/QVJS8Jdtgy G0+LCXkzOTh2DjhfrpWqRDY0QT2P1Lv0jthiiKbERghF0CnHAgeTY= X-Received: by 2002:a05:620a:4012:b0:92e:d63b:455b with SMTP id af79cd13be357-92ef2b12452mr1436818585a.6.1784053964792; Tue, 14 Jul 2026 11:32:44 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id af79cd13be357-92ee5b88a38sm1484900685a.12.2026.07.14.11.32.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:32:44 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 09/13] mm/kwatch: add probe lifecycle runtime Date: Wed, 15 Jul 2026 02:32:26 +0800 Message-ID: <20260714183226.12756-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Open and close the watch window with a kretprobe on the target function: the entry handler tracks per-task nesting depth and, when the configured depth is reached, resolves the watch expression and arms a watchpoint; the exit handler disarms it. An optional kprobe at func_offset arms mid-function instead of at entry. Functions running in a real NMI(-like) context are rejected once, at function entry, by comparing the NMI nesting count against the one NMI-like layer that int3-based kprobe delivery itself adds; a companion kprobe with a post_handler pins the probe point so jump optimization cannot change the delivery mechanism after it is sampled. Rejections are counted and exposed to the control plane. A global epoch versioning scheme invalidates stale per-task state across reconfigurations, and a per-CPU mute flag keeps window management quiet while a CPU rewrites its own debug registers. Signed-off-by: Jinchao Wang --- mm/kwatch/Makefile | 2 +- mm/kwatch/probe.c | 263 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 mm/kwatch/probe.c diff --git a/mm/kwatch/Makefile b/mm/kwatch/Makefile index b2bc3003c89b..f04673cc5b1c 100644 --- a/mm/kwatch/Makefile +++ b/mm/kwatch/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_KWATCH) +=3D kwatch.o =20 -kwatch-y :=3D deref.o task_ctx.o hwbp.o +kwatch-y :=3D deref.o task_ctx.o hwbp.o probe.o diff --git a/mm/kwatch/probe.c b/mm/kwatch/probe.c new file mode 100644 index 000000000000..af6e0af45c10 --- /dev/null +++ b/mm/kwatch/probe.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include + +#include "kwatch.h" +#define TRAMPOLINE_CHECK_DEPTH 16 +static DEFINE_PER_CPU(bool, kwatch_probe_cpu_muted); + +struct kwatch_probe_ctx { + struct kprobe kp; + struct kretprobe rp; + struct kprobe pin_kp; + const struct kwatch_config *cfg; + bool rp_via_int3; + + u32 epoch; +}; + +static struct kwatch_probe_ctx kwatch_probe_ctx; +static atomic_long_t kwatch_nmi_rejected; + +unsigned long kwatch_probe_nmi_rejected(void) +{ + return atomic_long_read(&kwatch_nmi_rejected); +} + +/* + * True if the probed function itself runs in an NMI-like context. + * int3-based kprobe delivery adds one NMI-like layer of its own; + * delivery is pinned at registration so the subtraction stays exact. + */ +static bool kwatch_probed_ctx_in_nmi(bool via_int3) +{ + return (preempt_count() & NMI_MASK) > (via_int3 ? NMI_OFFSET : 0); +} + +static void kwatch_pin_post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + /* a post_handler pins the probepoint: no jump optimization */ +} + +bool kwatch_probe_validate_hit(struct pt_regs *regs, + struct task_struct *arm_tsk) +{ + struct kwatch_tsk_ctx *ctx =3D kwatch_tsk_ctx_get(false); + + if (unlikely(!ctx)) + return true; + + if (arm_tsk !=3D current || + ctx->depth !=3D kwatch_probe_ctx.cfg->depth + 1) + return true; + + return false; +} + +void kwatch_probe_mute(bool mute) +{ + __this_cpu_write(kwatch_probe_cpu_muted, mute); +} + +static inline bool kwatch_probe_is_muted(void) +{ + return __this_cpu_read(kwatch_probe_cpu_muted); +} + +enum kwatch_probe_position { + KWATCH_PROBE_POSITION_ENTRY, + KWATCH_PROBE_POSITION_ACTIVE, + KWATCH_PROBE_POSITION_EXIT +}; + +static bool kwatch_tsk_ctx_check(enum kwatch_probe_position pos) +{ + struct kwatch_tsk_ctx *ctx =3D kwatch_tsk_ctx_get(true); + u32 epoch; + + if (unlikely(!ctx)) + return false; + + /* Pairs with smp_store_release() in kwatch_probe_start/stop() */ + epoch =3D smp_load_acquire(&kwatch_probe_ctx.epoch); + + if (unlikely(ctx->epoch !=3D epoch)) + kwatch_tsk_ctx_reset(ctx, epoch); + + if (unlikely(!epoch)) + return false; + + switch (pos) { + case KWATCH_PROBE_POSITION_ENTRY: + ctx->depth++; + return true; + case KWATCH_PROBE_POSITION_ACTIVE: + return true; + case KWATCH_PROBE_POSITION_EXIT: + if (unlikely(ctx->depth =3D=3D 0)) { + kwatch_tsk_ctx_put(); + return false; + } + + ctx->depth--; + if (ctx->depth =3D=3D 0) { + kwatch_tsk_ctx_put(); + return false; + } + return true; + } + return false; +} + +static int kwatch_activate_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kwatch_tsk_ctx *ctx =3D kwatch_tsk_ctx_get(false); + unsigned long watch_addr; + u16 watch_len; + + if (unlikely(!ctx)) + return 0; + + if (unlikely(kwatch_probe_is_muted())) + return 0; + + if (unlikely(!kwatch_tsk_ctx_check(KWATCH_PROBE_POSITION_ACTIVE))) + return 0; + + if (ctx->depth !=3D kwatch_probe_ctx.cfg->depth + 1 || ctx->wp) + return 0; + + if (kwatch_deref_resolve(kwatch_probe_ctx.cfg, regs, &watch_addr, + &watch_len)) + return 0; + + if (kwatch_hwbp_get(&ctx->wp)) + return 0; + + kwatch_hwbp_arm(ctx->wp, watch_addr, watch_len); + return 0; +} + +static int kwatch_lifecycle_entry(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + /* + * Single policy point: the target function's context is judged once + * here. A rejected invocation never increments depth, so the offset + * kprobe path inherits the verdict through the depth check. + */ + if (unlikely(kwatch_probed_ctx_in_nmi(kwatch_probe_ctx.rp_via_int3))) { + atomic_long_inc(&kwatch_nmi_rejected); + return 1; /* NMI context is unsupported: no window, no return hook */ + } + + if (!kwatch_tsk_ctx_check(KWATCH_PROBE_POSITION_ENTRY)) + return 0; + + if (kwatch_probe_ctx.cfg->func_offset =3D=3D 0) + kwatch_activate_handler(NULL, regs); + + return 0; +} + +static int kwatch_lifecycle_exit(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct kwatch_tsk_ctx *ctx =3D kwatch_tsk_ctx_get(false); + + if (unlikely(!ctx)) + return 0; + + if (!kwatch_tsk_ctx_check(KWATCH_PROBE_POSITION_EXIT)) + return 0; + + if (ctx->depth =3D=3D kwatch_probe_ctx.cfg->depth) { + struct kwatch_watchpoint *wp =3D xchg(&ctx->wp, NULL); + + if (wp) + kwatch_hwbp_put(wp); + } + + return 0; +} + +int kwatch_probe_start(struct kwatch_config *cfg) +{ + static u32 next_epoch; + u32 current_epoch; + int ret; + + /* + * Lockless check to prevent concurrent starts. Strictly serialized + * by the control plane mutex, but serves as a sanity check. + */ + if (smp_load_acquire(&kwatch_probe_ctx.epoch) !=3D 0) + return -EBUSY; + + memset(&kwatch_probe_ctx, 0, sizeof(kwatch_probe_ctx)); + kwatch_probe_ctx.cfg =3D cfg; + + /* + * Pin the entry probepoint before the kretprobe registers, so its + * delivery (int3 vs ftrace) can never change under jump optimization. + * register_kretprobe() clears kp.post_handler, hence the companion. + */ + kwatch_probe_ctx.pin_kp.symbol_name =3D cfg->func_name; + kwatch_probe_ctx.pin_kp.post_handler =3D kwatch_pin_post_handler; + ret =3D register_kprobe(&kwatch_probe_ctx.pin_kp); + if (ret < 0) + return ret; + + kwatch_probe_ctx.rp.entry_handler =3D kwatch_lifecycle_entry; + kwatch_probe_ctx.rp.handler =3D kwatch_lifecycle_exit; + kwatch_probe_ctx.rp.kp.symbol_name =3D cfg->func_name; + + ret =3D register_kretprobe(&kwatch_probe_ctx.rp); + if (ret < 0) { + unregister_kprobe(&kwatch_probe_ctx.pin_kp); + return ret; + } + kwatch_probe_ctx.rp_via_int3 =3D !kprobe_ftrace(&kwatch_probe_ctx.rp.kp); + + if (cfg->func_offset) { + kwatch_probe_ctx.kp.symbol_name =3D cfg->func_name; + kwatch_probe_ctx.kp.offset =3D cfg->func_offset; + kwatch_probe_ctx.kp.pre_handler =3D kwatch_activate_handler; + + ret =3D register_kprobe(&kwatch_probe_ctx.kp); + if (ret) { + unregister_kretprobe(&kwatch_probe_ctx.rp); + unregister_kprobe(&kwatch_probe_ctx.pin_kp); + return ret; + } + } + + current_epoch =3D ++next_epoch; + if (unlikely(!current_epoch)) + current_epoch =3D ++next_epoch; + + /* Pairs with smp_load_acquire() in kwatch_tsk_ctx_check() */ + smp_store_release(&kwatch_probe_ctx.epoch, current_epoch); + + return 0; +} + +void kwatch_probe_stop(void) +{ + if (!kwatch_probe_ctx.epoch) + return; + + /* Pairs with smp_load_acquire() in kwatch_tsk_ctx_check() */ + smp_store_release(&kwatch_probe_ctx.epoch, 0); + + if (kwatch_probe_ctx.cfg->func_offset > 0) + unregister_kprobe(&kwatch_probe_ctx.kp); + + unregister_kretprobe(&kwatch_probe_ctx.rp); + unregister_kprobe(&kwatch_probe_ctx.pin_kp); +} --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qk1-f180.google.com (mail-qk1-f180.google.com [209.85.222.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E87F13D813C for ; Tue, 14 Jul 2026 18:33:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053990; cv=none; b=HWfhJKlYlp4S5ygN6O/R6uJ7yFuA/RjM2l7wXr0jaQjiLAP8j89H1UTe2ogCu5bIsmlT4AjlnsyA3+Os3XIDQhUrFIG5q+p2PsligTdv18ng3C1VcLOu4OH3TJ/UIrfgnuGpGc3xfW3X9JDcecrohlT/uS1UNugRYLZ3o9/sysU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784053990; c=relaxed/simple; bh=HtjHQF0w4jDf5jPZ2VynT9uUeex8aV3wyoJGoZkJo4Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ttbgoo6Ni2uNslWFZU65XJD2081m+LHqI3141qMyjY/zHN4N72rbeueIVvtrEdpKJrZezS0DVh7cnZnsCTsn15UJ0/gS3mDxCVnoldNnb9bExFcRvVoUSnkFBC848/9H0h3mK1monQ0YycMMmoDfnyJJSwRQj9WRNjiz1li91OE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=dETxNDqx; arc=none smtp.client-ip=209.85.222.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dETxNDqx" Received: by mail-qk1-f180.google.com with SMTP id af79cd13be357-92e5c92c389so247197985a.3 for ; Tue, 14 Jul 2026 11:33:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784053988; x=1784658788; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=88qx9YixiLkFD/vh8cYDKX7z/SdNlgRZ6Zz58PjlN0k=; b=dETxNDqxbHw0FOIZOXFfiIEdLJG9fczIlEfMsIYl4L11w87iwra4wFviXb+E5+9Tb6 Ay4Qj/8qa30SiDz0s9pi9SOfYl/QYzSrTJlEkaDYBNkGBa9XIoRxZ9RpgHGbXxXrwYDV cKKYgySsHnJM9cVT+7YpN/3EBL8LvrK6gwUSb/EWSAIHYjvdb0o41Vd/Jh6RZT6WHQVk 85ffKaWkfPT8SfsV+JLcGK4/KO+PemSss7PTRJM9hY0GTf7oCqkF9DBzVBGcYn/jq29/ mujz6yjLQup/HeovE77FFsUV2U7roGkdXhrJveLQjS0EHxcAmo87Bmn0y7nazv+rL1eu IDPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784053988; x=1784658788; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=88qx9YixiLkFD/vh8cYDKX7z/SdNlgRZ6Zz58PjlN0k=; b=o6i8GHGXFQpOmGSWgRrfoBw3ZwUp2Uc6jPDH4VQQEnAJPdoF3TzlCXTaRbcFqpmRG7 BlqvVc5IwTrD/fGYiYReL46ch5fYzY18Gq5MnkEsJ86xoNlqqqvcOCB4ajZgRDNLgVjv 8J8t7CVM/fS/HvrCB+cEtHyeQD26aRTy7hE8a9TQc5+5FrbWT7XXrB0Qy8KfvOwOtgaB Tbp5P+R0S3OHTX6GSLbl1G5dVdD2TLhqiHdR8U+0mbvW/p0nXjlAaIEoOhDTD5+shdg4 GejiZ5dKltGjuSuDeoyzT+bgCjmUlJyjKuuhWIjkHjQJYMJC5zqpmrFfSP0G/24cO7eh w3GQ== X-Forwarded-Encrypted: i=1; AHgh+RosjqptFmSvwQOZ/GrKEduF/2nrLJ6MbJTFFp8svmpA/1GcOjtHilbSCPOiGKyLuwR9u8tWTx0eFWopVVI=@vger.kernel.org X-Gm-Message-State: AOJu0Yxstabjsy1Qn3kqCFRE5UhNCU33lgYoKXCRLV3+de90eKGM6ZBI ElOZqCCHEJOOi5np09VGhxzwNhmZyu6cK85cDkjNV2J5fObkmJAIjW/i X-Gm-Gg: AfdE7ckXEMIv5vNf0iflh9/PrMLhhT7/CkoXDgFtWdiKL0nW1mVOz9Bv2yrBoKGmUhM CjGBf1GjTwd7fvWWY2VkwfhSVvftDzfYDc4PU9xo5ec6i4k7ajk1Xy5lSkHX4BtfSZCgrWPebrX r41H4HsdlUWQZ7obk3S/hGhNm8laIwRVLk8PQNuOTBwIjYh+p3Ncv5IuMXs/ysjvJk/MZsxRXSz O+rY6L4BXgp72+z2vqxSyq5MfH95iVM9KcINXiJpwbWgjCuNibQ4dcmi+iY7qC6Lwll2E742iiY I+WYVPx199XVcbOCOqPyltRF4rOpAdo3I10mr1i3y7SFatwo4pJre10Q6VgoQjas83/S8Iown3s t85sHknUgkvE/LXDfOZca4LZKsnp/eHe9F4V9uxRtuiAlJBc1Ki0XulLdxfHGgQpwFfE5EC4bZb 8eoZn5SiJaGc1CbLmqZIDpQqwW2H7uYTemdGAmPiOXTyv/JIuxqA5nXkRrgfQdlg== X-Received: by 2002:a05:620a:2623:b0:92e:7ba3:740d with SMTP id af79cd13be357-93086c1dfb1mr389163985a.68.1784053987420; Tue, 14 Jul 2026 11:33:07 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id af79cd13be357-92ee5d6c28bsm1566578185a.46.2026.07.14.11.33.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:33:06 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 10/13] mm/kwatch: add anchor thread for global watchpoints Date: Wed, 15 Jul 2026 02:32:53 +0800 Message-ID: <20260714183253.12848-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- mm/kwatch/Makefile | 2 +- mm/kwatch/anchor.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 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) +=3D kwatch.o =20 -kwatch-y :=3D deref.o task_ctx.o hwbp.o probe.o +kwatch-y :=3D 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..11da6aff9413 --- /dev/null +++ b/mm/kwatch/anchor.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#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) +{ + wait_event_timeout(kwatch_anchor_wq, kthread_should_stop(), + duration_sec * HZ); +} + +static int kwatch_anchor_thread_fn(void *data) +{ + unsigned long duration =3D (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_uninterruptible(HZ); + + return 0; +} + +int kwatch_anchor_start(u16 duration) +{ + kwatch_anchor_tsk =3D kthread_run(kwatch_anchor_thread_fn, + (void *)(unsigned long)duration, + "kwatch_anchor"); + if (IS_ERR(kwatch_anchor_tsk)) { + int ret =3D PTR_ERR(kwatch_anchor_tsk); + + kwatch_anchor_tsk =3D NULL; + return ret; + } + return 0; +} + +void kwatch_anchor_stop(void) +{ + if (kwatch_anchor_tsk) { + kthread_stop(kwatch_anchor_tsk); + kwatch_anchor_tsk =3D NULL; + } +} + +void kwatch_anchor_cancel_work(void) +{ + cancel_work_sync(&kwatch_auto_stop_work); +} --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qt1-f182.google.com (mail-qt1-f182.google.com [209.85.160.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D34EB43CECA for ; Tue, 14 Jul 2026 18:33:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784054008; cv=none; b=LecLweMq+T5uDsWCZ+qmv4iwC7WLuevjipjliys0tjmmfocAhBdvYMyhtzEtqWjd+XAflhnwzkqyI173dR9PznwGWDLHtppaGSAMZbvOvXsBk04dOTH312rSPNORLyfwZYdFE/17CkS4BTevJn8fxCApmoe3wde5ft70+/6NMk4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784054008; c=relaxed/simple; bh=LZkXuS1KQM4u6yZNSGgAkuzTyvZnzYtMW4OZU5Ks2aM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TJeWfIKEAbDplllOv4jZIWl4PcGRKJ8RLO0Q86M1RaO0bP2d9balgEQiIJJhQW0LOVOr2N4XnFYQ8e5QuVbm+XZyaeryI9i7uAJ4Z7VoUU2qk5Fgt/5eiS9uINM7XfY6LyvKG6gppCfDAEB5pRavvdsbn0a3R+NHCqHpWkkwUvw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=nQc+V4K7; arc=none smtp.client-ip=209.85.160.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nQc+V4K7" Received: by mail-qt1-f182.google.com with SMTP id d75a77b69052e-51c2cce930cso12026851cf.0 for ; Tue, 14 Jul 2026 11:33:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784054005; x=1784658805; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Wwi2vZyFk7t6QSsln2rKKiHEkjJToMmeoCsJaB6hu68=; b=nQc+V4K70eQXnzr69GlU0dkZ0AFtoToUnseJ6w8JOpm+2JIa/Mz0Q9gUmrhACHRYy+ f9wHfMrmFPOHE1Z76dqNElNIYn4Ky3oWsEczFYOjuW56en4Ekt57OFevWeViKZGcrgei fm2dApmvN+NbRCEiweDvC2rHAwULF5KdIanRwca+oJIrd+5iH59VRRBk7T4g+IET2fhg hqN6oeGySe9V/zNWhc2HTr2vtzLf3maR7W7z74166u0T1wekElL6f4gIy7fipFnU8NiH LskLhQxAsYDTsCbtyjSQgsE7btnDC983uPN9LxVl/uGmLWvphjjg7TP4CBhd8DGiy/uj XfLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784054005; x=1784658805; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Wwi2vZyFk7t6QSsln2rKKiHEkjJToMmeoCsJaB6hu68=; b=S7JuZBh8cPDjk4rORmO/FYYH/NCdpRxLOPL3tQrWG8U3Qs8vuMIl4abLPgvwqnxAGV rhifYnc6rclVXq6KRCl6VR6ebVB8ESFdDi28vG4kgjJyuZ20D28dvR9gX/5u5gvkl6Pa 9rAv1ip/lNhA21UZi31RRSyoTH17MqwrnBu3AKpxbrUJxlvD04lMoVIVa3j9GHHuJbdB i3Ifaf3KCspT9H/MNIR8qB3gREgKPu5fqCsru2CjVAQ1u7SNDmTd7cR5IeqqTmUuje7q KoZWm84r74iihIhBvIHCZQ9s08q9KZarNDxr6Tm2Z3e2Z8P+vX53AjFMl2MRxzQQGmr8 PC/A== X-Forwarded-Encrypted: i=1; AHgh+RrqK9F0Mi3ZWHJ5Pf23M1nE/a/0KuV/LI6qCKD+ZdOhY1H1gY4vOt5uz1wxXLuOdSB1FZC3DMCYuNnA+/4=@vger.kernel.org X-Gm-Message-State: AOJu0YxMRG5btTBoc4iZSSiaCo2e0qxl5gvxe8OUfXFfXPN8XYlFJsAY 8IoB5lUW3ME24XL8/Z7vvexX1xZVvnbUM8g7ZkAMx8IuDM1MpnF/AR8x X-Gm-Gg: AfdE7ckLlJgL6B5o6d72FaMBMA0FsccIbn2oRFm2ZVgDXOyKzNKqI1najRAO0S4HNTm yI29Nx7hUoH9F04ZJY6exeF6HMKXntQFm0I/Bir+Dc7pKwJzVn7/x3LrNuSy8riMYrLsnxInRQC ddHzYuvDV+dJNllXE6FLyxVYiuCyewP5lZV5DoOauAbnsmprYkUe1cHnFSpc4IgXc3ap8NhRAhr ZoRQ+xCtxjPjauRy5cN2yf0B8vewO5Jv8zZSMQAlRrSxR0udoHgPoSyXoxouOO7nrAURItoe3Hw HRCSE72tpggOk1UjYgejJ2+IEyVILQLxKOOGv9ShO3Q2+imuP01sBb6PyQ9hCjnifi1umCsVYoC LqxnnP62FX0tJz92Wy+O6r/pg1EvyaeRD72UNHQw7DghKSP38UaEGqNWViMG2btvOjT4FxplBHP 13lnAqerXMkjnHQ6lTt2qMqQjOtlpHb0zfSPuht8+o7JQiaWy2R8k= X-Received: by 2002:a05:622a:53c7:b0:51c:7b12:120b with SMTP id d75a77b69052e-51cbf2ff3f6mr153433161cf.73.1784054005304; Tue, 14 Jul 2026 11:33:25 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-903083910d1sm144098546d6.12.2026.07.14.11.33.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:33:24 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 11/13] mm/kwatch: add debugfs control plane Date: Wed, 15 Jul 2026 02:33:16 +0800 Message-ID: <20260714183316.12964-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Wire the pieces together behind a single debugfs file, /sys/kernel/debug/kwatch/config. Writing a key=3Dvalue configuration string stops any active session and starts a new one; reading shows the active configuration and the nmi_rejected counter. An open-count guard keeps the file single-open and a mutex serializes start/stop/auto-stop against each other. Add the Kconfig entry and hook mm/kwatch into the mm build. KWatch can be built in or as a module; symbol-name watch expressions need the built-in flavour (kallsyms_lookup_name is not exported). Signed-off-by: Jinchao Wang --- MAINTAINERS | 8 ++ mm/Kconfig | 1 + mm/Makefile | 1 + mm/kwatch/Kconfig | 17 +++ mm/kwatch/Makefile | 2 +- mm/kwatch/core.c | 325 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 mm/kwatch/Kconfig create mode 100644 mm/kwatch/core.c diff --git a/MAINTAINERS b/MAINTAINERS index 7cc4bca5a2c5..b6371f92fe5c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14578,6 +14578,14 @@ S: Supported T: git git://git.kernel.org/pub/scm/virt/kvm/kvm.git F: arch/x86/kvm/xen.* =20 +KWATCH +M: Jinchao Wang +L: linux-mm@kvack.org +S: Maintained +F: Documentation/dev-tools/kwatch.rst +F: include/trace/events/kwatch.h +F: mm/kwatch/ + L3MDEV M: David Ahern L: netdev@vger.kernel.org diff --git a/mm/Kconfig b/mm/Kconfig index 9e0ca4824905..cac75a46e21a 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -1510,5 +1510,6 @@ config LAZY_MMU_MODE_KUNIT_TEST If unsure, say N. =20 source "mm/damon/Kconfig" +source "mm/kwatch/Kconfig" =20 endmenu diff --git a/mm/Makefile b/mm/Makefile index eff9f9e7e061..80c688330358 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_PAGE_POISONING) +=3D page_poison.o obj-$(CONFIG_KASAN) +=3D kasan/ obj-$(CONFIG_KFENCE) +=3D kfence/ obj-$(CONFIG_KMSAN) +=3D kmsan/ +obj-$(CONFIG_KWATCH) +=3D kwatch/ obj-$(CONFIG_FAILSLAB) +=3D failslab.o obj-$(CONFIG_FAIL_PAGE_ALLOC) +=3D fail_page_alloc.o obj-$(CONFIG_MEMTEST) +=3D memtest.o diff --git a/mm/kwatch/Kconfig b/mm/kwatch/Kconfig new file mode 100644 index 000000000000..b1c37a829dd5 --- /dev/null +++ b/mm/kwatch/Kconfig @@ -0,0 +1,17 @@ +config KWATCH + tristate "Kernel Watch Framework" + depends on PERF_EVENTS && HAVE_HW_BREAKPOINT && DEBUG_FS + depends on HAVE_REINSTALL_HW_BREAKPOINT + select KPROBES + select KRETPROBES + select STACKTRACE + help + A generalized hardware-assisted memory monitor utility. + It provides a low-overhead, real-time trigger mechanism to monitor + kernel memory safely in atomic contexts using hardware breakpoints. + + KWatch is designed to catch silent memory corruptions, stack + overwrites, and complex Heisenbugs by synchronously trapping the + exact instruction causing the illegal access. + + If unsure, say N. diff --git a/mm/kwatch/Makefile b/mm/kwatch/Makefile index b196c794619a..02d7917602f1 100644 --- a/mm/kwatch/Makefile +++ b/mm/kwatch/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_KWATCH) +=3D kwatch.o =20 -kwatch-y :=3D deref.o task_ctx.o hwbp.o probe.o anchor.o +kwatch-y :=3D core.o deref.o task_ctx.o hwbp.o probe.o anchor.o diff --git a/mm/kwatch/core.c b/mm/kwatch/core.c new file mode 100644 index 000000000000..548d0cdd0812 --- /dev/null +++ b/mm/kwatch/core.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "kwatch.h" + +static struct kwatch_config kwatch_config; +static bool watching_active; + +static struct dentry *dbgfs_dir; +static struct dentry *dbgfs_config; +static DEFINE_MUTEX(kwatch_dbgfs_mutex); +static atomic_t dbgfs_config_busy =3D ATOMIC_INIT(0); + +static int kwatch_start_watching(void) +{ + int ret; + + if (!strlen(kwatch_config.func_name)) { + if (kwatch_config.duration > 0) { + strscpy(kwatch_config.func_name, "kwatch_global_anchor", + sizeof(kwatch_config.func_name)); + } else { + pr_err("func_name or duration is required\n"); + return -EINVAL; + } + } else if (kwatch_config.duration > 0 && + strcmp(kwatch_config.func_name, "kwatch_global_anchor")) { + pr_warn("duration is ignored when watching a specific function\n"); + } + + if (kwatch_config.access_type > 3) { + pr_err("Invalid access_type (must be 0-3)\n"); + return -EINVAL; + } + + ret =3D kwatch_hwbp_prealloc(kwatch_config.max_watch, + kwatch_config.access_type); + if (ret) { + pr_err("kwatch_hwbp_prealloc ret: %d\n", ret); + return ret; + } + + ret =3D kwatch_tsk_ctx_prealloc(kwatch_config.max_concurrency); + if (ret) { + kwatch_hwbp_free(); + return ret; + } + + ret =3D kwatch_probe_start(&kwatch_config); + if (ret) { + pr_err("kwatch_probe_start ret: %d\n", ret); + kwatch_tsk_ctx_free(); + kwatch_hwbp_free(); + return ret; + } + + if (!strcmp(kwatch_config.func_name, "kwatch_global_anchor")) { + ret =3D kwatch_anchor_start(kwatch_config.duration); + if (ret) { + kwatch_probe_stop(); + synchronize_rcu(); + kwatch_tsk_ctx_release_wps(); + kwatch_hwbp_free(); + kwatch_tsk_ctx_free(); + return ret; + } + } + + watching_active =3D true; + return 0; +} + +static void kwatch_stop_watching(void) +{ + watching_active =3D false; + + kwatch_anchor_stop(); + /* after kthread_stop: the dead thread cannot re-mark expiry */ + kwatch_anchor_clear_expired(); + + kwatch_probe_stop(); + synchronize_rcu(); + kwatch_tsk_ctx_release_wps(); + /* + * Waits for disarm IPIs and unregisters breakpoints: no #DB can + * reach the ctx pool once this returns. + */ + kwatch_hwbp_free(); + kwatch_tsk_ctx_free(); +} + +void kwatch_auto_stop(void) +{ + mutex_lock(&kwatch_dbgfs_mutex); + /* the expired check neutralizes work items from torn-down sessions */ + if (watching_active && kwatch_anchor_has_expired()) { + kwatch_stop_watching(); + pr_info("watch duration expired, stopped watching\n"); + } + mutex_unlock(&kwatch_dbgfs_mutex); +} + +static int kwatch_config_parse(char *buf, struct kwatch_config *cfg) +{ + char *token, *key, *val; + int ret =3D 0; + + memset(cfg, 0, sizeof(*cfg)); + cfg->max_concurrency =3D 256; + cfg->max_watch =3D 4; + cfg->watch_len =3D 8; + cfg->access_type =3D 0; + + while ((token =3D strsep(&buf, " \t\n")) !=3D NULL) { + if (!*token) + continue; + key =3D strsep(&token, "=3D"); + val =3D token; + if (!key || !val) + return -EINVAL; + + if (!strcmp(key, "func_name")) { + strscpy(cfg->func_name, val, sizeof(cfg->func_name)); + } else if (!strcmp(key, "func_offset")) { + ret =3D kstrtou16(val, 0, &cfg->func_offset); + } else if (!strcmp(key, "depth")) { + ret =3D kstrtou16(val, 0, &cfg->depth); + } else if (!strcmp(key, "max_concurrency")) { + ret =3D kstrtou16(val, 0, &cfg->max_concurrency); + } else if (!strcmp(key, "max_watch")) { + ret =3D kstrtou16(val, 0, &cfg->max_watch); + } else if (!strcmp(key, "access_type")) { + ret =3D kstrtouint(val, 0, &cfg->access_type); + } else if (!strcmp(key, "watch_len")) { + ret =3D kstrtou16(val, 0, &cfg->watch_len); + if (!ret && cfg->watch_len !=3D 1 && + cfg->watch_len !=3D 2 && cfg->watch_len !=3D 4 && + cfg->watch_len !=3D 8) + ret =3D -EINVAL; + } else if (!strcmp(key, "duration")) { + ret =3D kstrtou16(val, 0, &cfg->duration); + } else if (!strcmp(key, "watch_expr")) { + strscpy(cfg->watch_expr, val, sizeof(cfg->watch_expr)); + ret =3D kwatch_deref_parse(cfg, val); + } + + if (ret) + return ret; + } + return 0; +} + +static int kwatch_dbgfs_open(struct inode *inode, struct file *file) +{ + if (atomic_cmpxchg(&dbgfs_config_busy, 0, 1)) + return -EBUSY; + return 0; +} + +static int kwatch_dbgfs_release(struct inode *inode, struct file *file) +{ + atomic_set(&dbgfs_config_busy, 0); + return 0; +} + +static ssize_t kwatch_dbgfs_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *out_buf; + size_t len =3D 0; + ssize_t ret; + + out_buf =3D kzalloc(MAX_CONFIG_STR_LEN, GFP_KERNEL); + if (!out_buf) + return -ENOMEM; + + if (watching_active) { + len +=3D scnprintf(out_buf + len, MAX_CONFIG_STR_LEN - len, + "func_name=3D%s\n" + "func_offset=3D%u\n" + "depth=3D%u\n" + "duration=3D%u\n" + "max_concurrency=3D%u\n" + "max_watch=3D%u\n" + "access_type=3D%u\n" + "watch_len=3D%u\n", + kwatch_config.func_name, + kwatch_config.func_offset, kwatch_config.depth, + kwatch_config.duration, + kwatch_config.max_concurrency, + kwatch_config.max_watch, + kwatch_config.access_type, + kwatch_config.watch_len); + + if (kwatch_config.base =3D=3D KWATCH_BASE_GLOBAL_SYM) { + len +=3D scnprintf(out_buf + len, MAX_CONFIG_STR_LEN - len, + "sym_addr=3D0x%lx\n", kwatch_config.sym_addr); + } + + len +=3D scnprintf(out_buf + len, MAX_CONFIG_STR_LEN - len, + "watch_expr=3D%s\n" + "nmi_rejected=3D%lu\n", + kwatch_config.watch_expr, + kwatch_probe_nmi_rejected()); + } else { + len =3D scnprintf(out_buf, MAX_CONFIG_STR_LEN, "not watching\n"); + } + + ret =3D simple_read_from_buffer(user_buf, count, ppos, out_buf, len); + kfree(out_buf); + return ret; +} + +static ssize_t kwatch_dbgfs_write(struct file *file, const char __user *bu= ffer, + size_t count, loff_t *ppos) +{ + char *input_alloc; + char *parse_str; + int ret; + + if (count =3D=3D 0 || count >=3D MAX_CONFIG_STR_LEN) + return -EINVAL; + + input_alloc =3D memdup_user_nul(buffer, count); + if (IS_ERR(input_alloc)) + return PTR_ERR(input_alloc); + + mutex_lock(&kwatch_dbgfs_mutex); + + if (watching_active) + kwatch_stop_watching(); + + parse_str =3D strim(input_alloc); + + if (!strlen(parse_str)) { + ret =3D -EINVAL; + goto out; + } + + ret =3D kwatch_config_parse(parse_str, &kwatch_config); + if (ret) { + pr_err("Failed to parse config %d\n", ret); + goto out; + } + + ret =3D kwatch_start_watching(); + if (ret) { + pr_err("Failed to start watching with %d\n", ret); + goto out; + } + + ret =3D count; + +out: + mutex_unlock(&kwatch_dbgfs_mutex); + kfree(input_alloc); + return ret; +} + +static const struct file_operations kwatch_fops =3D { + .owner =3D THIS_MODULE, + .open =3D kwatch_dbgfs_open, + .release =3D kwatch_dbgfs_release, + .read =3D kwatch_dbgfs_read, + .write =3D kwatch_dbgfs_write, +}; + +static int __init kwatch_init(void) +{ + int ret =3D 0; + + memset(&kwatch_config, 0, sizeof(kwatch_config)); + + dbgfs_dir =3D debugfs_create_dir("kwatch", NULL); + if (IS_ERR(dbgfs_dir)) { + ret =3D PTR_ERR(dbgfs_dir); + goto err_dir; + } + + dbgfs_config =3D debugfs_create_file("config", 0600, dbgfs_dir, NULL, + &kwatch_fops); + if (IS_ERR(dbgfs_config)) { + ret =3D PTR_ERR(dbgfs_config); + goto err_file; + } + + pr_info("module loaded\n"); + return 0; + +err_file: + debugfs_remove_recursive(dbgfs_dir); + dbgfs_dir =3D NULL; +err_dir: + return ret; +} +module_init(kwatch_init); + +static void __exit kwatch_exit(void) +{ + mutex_lock(&kwatch_dbgfs_mutex); + if (watching_active) + kwatch_stop_watching(); + mutex_unlock(&kwatch_dbgfs_mutex); + + /* the anchor thread is dead: nothing can schedule new work now */ + kwatch_anchor_cancel_work(); + + debugfs_remove_recursive(dbgfs_dir); + dbgfs_dir =3D NULL; + + pr_info("kwatch unloaded\n"); +} +module_exit(kwatch_exit); + +MODULE_AUTHOR("Jinchao Wang "); +MODULE_DESCRIPTION("Kernel watchpoint"); +MODULE_LICENSE("GPL"); --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qt1-f181.google.com (mail-qt1-f181.google.com [209.85.160.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 11383336EC0 for ; Tue, 14 Jul 2026 18:33:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784054030; cv=none; b=lfuWNWqkNAPSepQm3Fr7bd4quN4ou70caQEf+kGGI/9p9su0s1Dwjczfw9M/eJLyqyKnzkWYCmaedQ6UaNaOFx1oKNJvG7hvGaAFR+7M5LmnbN7LO0BDe0010wp+7LQkHKyFZPYfwuBJ9cItCa/1booA/b/aiJabMQUFXcbUEMA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784054030; c=relaxed/simple; bh=Gepv4EN5M9EKzOk4KnO+Ro5NR/FnPnIzfUYyZrSuJpA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AQ9TwtnHdkjzpTuz9hvO/jV7OtcaxgZjtkPmBiugIcdEKMtN29OoT70Xk716YfevJGDYJJFC4iNDd7AX6G1zWs8MzLBXmgWD7l7ZbWysPSOAu953IK9gXuodYVfOrxJA7ryMu2b2Mg8bjH5htbhvYkCkwPfHKjNrCSvzuzDAogk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=J7zLcB3w; arc=none smtp.client-ip=209.85.160.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="J7zLcB3w" Received: by mail-qt1-f181.google.com with SMTP id d75a77b69052e-51c4436d02cso7305451cf.1 for ; Tue, 14 Jul 2026 11:33:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784054028; x=1784658828; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=GxRwnz/utPYkA9CVNI0v4sXDVqY3MfYAVIwDwIjJvYI=; b=J7zLcB3w08njHNfoJmJs+2F+DeHDSpqslYrgpytSGPrghruJaV2i2VKhG1yKkNleNT afZdrcAc4+Tz/yXgm5g1HEES+W/BGhXxOxlhiZytrc4Nk9vrFd09IyfPtnNuNDnNCjZg 66/SMchkIcHCjBlXfXafuUEdrXQrjmjfty3a2wOGWBTOP86vWnpjGXytYFMClYDws2tv V9Gskvi4vKycPkEvXarcQzMgjGw6uSD/F2aH0JrLbMtNlHDk6Ud4PJWaXlZAVeObYa0j 70rybAhXbKLrctSKv3eV+AJ8xNp2kHOX3zCvM3xmp4+zzzBZ1SGxF7ntvlVBYYDe5fHj pl9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784054028; x=1784658828; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=GxRwnz/utPYkA9CVNI0v4sXDVqY3MfYAVIwDwIjJvYI=; b=lfBQ1Wene/1KaOao12aX7gnCD+FknBZ4omBTIBcnmlMP72XHAJDQvrlFBL8jsowGz+ Jfjy1d8qfShbpRliL7ljkFuC/sa5+ISg/JKGxn3JIBsBomD03oh4tExWCO4BDmFPKTJf iVSSjKf3QvK82U7/kPHP5jyt8k8i1AVO9DMlHxheM6uqbMD/RHytze6TBYem7EP+pTvq Byl9CZ4Ldfql1phwpKn+8Z7F58hPAq3iL7ruA5+kI1jg4ZGyNIPs0rOQFmRowU7ewonn LVvUPZ5cxaembV2d8wyiA+znJE050sO7yjq5Z7AMTiv7VFFQj2DhZgz+gsOl0hFywV3A GyfQ== X-Forwarded-Encrypted: i=1; AHgh+RqoI0zZFhqdWa9xE6Jws4rm3csiljygAvCKClEnKXn8K6DbWjtZFa9zPX++v/jG+SQy64wcp5v1fnzYmJ4=@vger.kernel.org X-Gm-Message-State: AOJu0YzSbSjmNCyiW0eGAxi9FgdecHUC0Y7RjjSuxF/SuNWsNCfPs7jt Li9c58Ygv+WyK1feFsk+tm36QcoaQPlRBxEUVycbpZc0ipkCW50cNnx3 X-Gm-Gg: AfdE7ckdQfmyiuivwyVMWnSjhxvGojtE81nKKcIrFe4jfyiD8pVexMUagle6rQ7eL4v LlQJua5qd2L8EWC7SxxyEvkIERa64+hfPXyf87tCoink9c+co1p+1CGz82/ohcTryyc+FXcXQLG 3bj54xCTTX4w9gSgQ+UigUkejc7FvZ5GJ1NLC3WZf4Jiyo8Uv4LEhc/qapyqqRd9ImHObBQ8Rx1 Ic5p22dFIP+llC5pQELYm9+Oqzk0vq6PpwvnPJn2g/irSkoF5ViBRWgx0iJjlBMWHizJn+NFwL0 3iTnm6ub1Dwy8WgyrNvojiRiYxiBnaTepb97N1fCoBNlQW6krHNp8VhChQR4JB/E+GcjUXUTK/9 yaKDjpdq2rwD2n+FgPmEHNhd94osRS/XiLtjPbJvHyVbTwk+wbus+U1mLTCx9ghSuC6koLNKJFI 6TECHUJ0z5j1hLkUuqcjf5g/w96Ab82pjx0J0GJbdxV9ykVY51unA= X-Received: by 2002:a05:622a:993:b0:51b:fe7a:4eb8 with SMTP id d75a77b69052e-51cbf0e8c9emr148137331cf.35.1784054027768; Tue, 14 Jul 2026 11:33:47 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ffd7c1d22bsm173718936d6.32.2026.07.14.11.33.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:33:46 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 12/13] mm/kwatch: add KUnit tests for the watch expression parser Date: Wed, 15 Jul 2026 02:33:39 +0800 Message-ID: <20260714183339.13044-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Cover base anchors (stack, argN, absolute address), positive and negative offsets, dereference chains, and rejection of malformed expressions (missing offsets, bad argument index, junk offsets). Signed-off-by: Jinchao Wang --- mm/kwatch/.kunitconfig | 9 +++ mm/kwatch/Kconfig | 10 +++ mm/kwatch/Makefile | 1 + mm/kwatch/deref_test.c | 137 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 mm/kwatch/.kunitconfig create mode 100644 mm/kwatch/deref_test.c diff --git a/mm/kwatch/.kunitconfig b/mm/kwatch/.kunitconfig new file mode 100644 index 000000000000..7e977ddf0da1 --- /dev/null +++ b/mm/kwatch/.kunitconfig @@ -0,0 +1,9 @@ +CONFIG_KUNIT=3Dy +CONFIG_KWATCH=3Dy +CONFIG_KWATCH_KUNIT_TEST=3Dy +CONFIG_PERF_EVENTS=3Dy +CONFIG_HAVE_HW_BREAKPOINT=3Dy +CONFIG_HAVE_REINSTALL_HW_BREAKPOINT=3Dy +CONFIG_KPROBES=3Dy +CONFIG_KRETPROBES=3Dy +CONFIG_PRINTK=3Dy diff --git a/mm/kwatch/Kconfig b/mm/kwatch/Kconfig index b1c37a829dd5..74083040a1a3 100644 --- a/mm/kwatch/Kconfig +++ b/mm/kwatch/Kconfig @@ -15,3 +15,13 @@ config KWATCH exact instruction causing the illegal access. =20 If unsure, say N. + +config KWATCH_KUNIT_TEST + bool "KUnit tests for KWatch" if !KUNIT_ALL_TESTS + depends on KWATCH && KUNIT + default KUNIT_ALL_TESTS + help + Enable KUnit tests for the KWatch kernel module. + This suite tests the core parsing logic, the pointer-chasing + finite state machine, and edge cases involving complex watchpoint + expressions. If unsure, say N. diff --git a/mm/kwatch/Makefile b/mm/kwatch/Makefile index 02d7917602f1..1d223d73b461 100644 --- a/mm/kwatch/Makefile +++ b/mm/kwatch/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_KWATCH) +=3D kwatch.o =20 kwatch-y :=3D core.o deref.o task_ctx.o hwbp.o probe.o anchor.o +kwatch-$(CONFIG_KWATCH_KUNIT_TEST) +=3D deref_test.o diff --git a/mm/kwatch/deref_test.c b/mm/kwatch/deref_test.c new file mode 100644 index 000000000000..094b7afeb235 --- /dev/null +++ b/mm/kwatch/deref_test.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include "kwatch.h" +#include + +static void kwatch_test_parse_deref_chain(struct kunit *test) +{ + struct kwatch_config cfg; + int ret; + + // Test 1: stack + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "stack"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_STACK); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 1); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], 0); + + // Test 2: arg1 + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg1"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_ARG1); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 1); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], 0); + + // Test 3: arg6+8 + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg6+8"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_ARG6); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 1); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], 8); + + // Test 4: arg2-16 + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg2-16"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_ARG2); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 1); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], -16); + + // Test 5: arg3->8 + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg3->8"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_ARG3); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 2); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], 0); + KUNIT_EXPECT_EQ(test, cfg.offsets[1], 8); + + // Test 6: arg4+8->16 + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg4+8->16"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_ARG4); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 2); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], 8); + KUNIT_EXPECT_EQ(test, cfg.offsets[1], 16); + + // Test 7: arg5-8->-16 + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg5-8->-16"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_ARG5); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 2); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], -8); + KUNIT_EXPECT_EQ(test, cfg.offsets[1], -16); + + // Test 8: stack->0->8 + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "stack->0->8"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_STACK); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 3); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], 0); + KUNIT_EXPECT_EQ(test, cfg.offsets[1], 0); + KUNIT_EXPECT_EQ(test, cfg.offsets[2], 8); + + // Test 9: arg1->+8 + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg1->+8"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_ARG1); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 2); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], 0); + KUNIT_EXPECT_EQ(test, cfg.offsets[1], 8); + + // Test 9.1: arg1-> (implicit 0 should fail) + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg1->"); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + // Test 9.2: stack->->8 (implicit 0 should fail) + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "stack->->8"); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + // Test 10: Invalid base + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "invalid_base"); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + // Test 11: Invalid offset + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg1+abc"); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + // Test 12: Invalid arg + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "arg7"); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + // Test 13: Absolute address + memset(&cfg, 0, sizeof(cfg)); + ret =3D kwatch_deref_parse(&cfg, "0xffffffff81000000+8"); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, cfg.base, KWATCH_BASE_ABS_ADDR); + KUNIT_EXPECT_EQ(test, cfg.sym_addr, 0xffffffff81000000UL); + KUNIT_EXPECT_EQ(test, cfg.offset_count, 1); + KUNIT_EXPECT_EQ(test, cfg.offsets[0], 8); +} + +static struct kunit_case kwatch_deref_test_cases[] =3D { + KUNIT_CASE(kwatch_test_parse_deref_chain), + {} +}; + +static struct kunit_suite kwatch_deref_test_suite =3D { + .name =3D "kwatch_deref", + .test_cases =3D kwatch_deref_test_cases, +}; + +kunit_test_suite(kwatch_deref_test_suite); + +MODULE_DESCRIPTION("KUnit tests for the KWatch watch expression parser"); +MODULE_LICENSE("GPL"); --=20 2.53.0 From nobody Sat Jul 25 18:53:40 2026 Received: from mail-qv1-f42.google.com (mail-qv1-f42.google.com [209.85.219.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2657643F4BE for ; Tue, 14 Jul 2026 18:34:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784054058; cv=none; b=ovZXt7WxqvgvTGWdk4DfIZOCYhXuUC7WFfLA2ffca3g+7JlDT6pE0v5RTRcSNK/kPH1DZDEDJ+joRisW6I0JaV9acG5Nml41tbH+xajNg2J6xSkyIwftrWZqs+mU4GvSu/g7FjnmiByh8NgRkR+T1fCuJo+Pu+1i8fvS+dwVo8E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784054058; c=relaxed/simple; bh=8cvRcaXstSBGACqv7boyM3j+NafF3PmGmAgbjgb8q7U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XjLhFlTolhrQ6XccZOl5gU8SHZ0bGDPiAIE8mUhxN7gfjz4fHZVTCxa/acguicmqtsvttvuCNsTyexSSTqV6SJFwfUZPDbmut0bbh7bN0otSyyJDZiUJ7Z+7qgkVE4XlzPJRDqTYDrISVLbsS/GTz4CA8UI+/EP9BjL1PNsNM/s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=foCLSVWX; arc=none smtp.client-ip=209.85.219.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="foCLSVWX" Received: by mail-qv1-f42.google.com with SMTP id 6a1803df08f44-90327237340so9742666d6.1 for ; Tue, 14 Jul 2026 11:34:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784054056; x=1784658856; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ZmmWwuvjQogVDd2VSBmXRJatjjzPeEuGhwyiPs8S0po=; b=foCLSVWXdPwvHUeaP27bBBA6R4RmiWlhOM2uSSf6ntCmhrE7CqfT/pZVCfBzTcD2hd wvF2siy4RsrAO6RvPJiIE64AeWOhyJhG140FOTW7RhrEqSXIUOQawMgUVDqdNRN16Lrv 3PyQhPy3JdKmVFRgT1FtMsIDzvKIcBnQ7HHaqQhqGBNMguQBdvuCQ1hlBCK4TjNe1G6B sJ3fMsWNlgjXO4lVmZWjmoh5AYZ6ufLjltGW0pJMmEwTiGwTy1JGjq1rhP/389d5a583 cgs8KiTBj0W6VV0Js8A9Kmm+JXCtcpWmv6SqItwqtjxQ6qFP2mC2o2bDoL52igvYsd0S 6J6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784054056; x=1784658856; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=ZmmWwuvjQogVDd2VSBmXRJatjjzPeEuGhwyiPs8S0po=; b=aOOB0gin0rJ2v4qVk2Ph+Pzdl6mw5uFPlhWW35R51Et1lZH0lqrI1yb6YlxSh51DI8 JDCQGIZJ4W/dFzUa9c+d5Ri7HN2mJHPPIOTcPUsETVXY3YDvzcFrE01dncfJFboXrp8Q JD/EjHjx4cr7S72eGPt1SjQ2mgUgQtHNf7uZgjKtR7ZSpKka01F3sdjE+/6z8gjMSf6i f9TZXtkTRZjjiQWsAn7cjxu+N0XVlOSUKUUuaIin/HJDtZXXmym5PU4Tk/Mx5pcRYWdr fM1QmBLaahrt+DSrA8n2mc2ISFBjPZB6ZzKJ3zwVSKqv/2S1PUPQYGDvXF8Sq8sZttyD PcSg== X-Forwarded-Encrypted: i=1; AHgh+RqsbiefqXbJ6XGSk+TCfwh9QbANP/b/5Kyo8epy4+yfRLpkd35nFKuO3ooYxzHmxjuxyaLBK7X22+FEW+E=@vger.kernel.org X-Gm-Message-State: AOJu0YwSON2a4xodfG/UDjuGJvgcUm2uJtwwiyXCGd9ZbqqGQc6jyHyj LvU5oF90uIBQ8u5TLdaun780sIhl4sD8vHfaJm6wAK6NSNyY3rk+SvaV X-Gm-Gg: AfdE7cmHWkcGVXb4Cum+9ERDbieD4+9M9rOG1oQeLMUwD5mYQExV8Mxc9VkVbTBMBC0 TNQqoZrl/cvU5Fvapz1SU1kKx8Qxf0YrzXhHAClmNOi1rPWX1aDVKhPA+CK/aFD5OCpGIJBhsKZ XHN0I73UaRU1CEo3Cvmfd8ytNyTKWFGCNII41Ne5Y1waFLzMxqoYoO0uI5E6zb6BrzVGo2yBIIa 7IdxZMo24Ubxc8eeFUf2ehWIuB1TW9NuGth5R/XElrX7xyZ+rqlqS/zFgJYxWWwPy6nG1zmfAV4 JD8t2NjpVM8kn+Qg68+Ulw9mbFGRw/DaBDiezgg7P3PW9HVIA6xYPnH2+MT4TlrT1eLw3VL/ba8 nw4veKjeB/7Id1ZHe5D8Ec7FVA9onFBD8twtH8Arw9CtEKE+az+og9wJYtMY1N5pY1z+T79xzRo n5gmCgcJB5IKyiTOM8nBpQCSt4zo2qdSrvFXCbgApNYVadLQF+EVs= X-Received: by 2002:a05:6214:19ce:b0:8f1:77b7:9bdc with SMTP id 6a1803df08f44-90413e0a5bfmr163102536d6.1.1784054055852; Tue, 14 Jul 2026 11:34:15 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ffd80fd82csm174180826d6.35.2026.07.14.11.34.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 11:34:14 -0700 (PDT) From: Jinchao Wang To: Andrew Morton , Peter Zijlstra , Thomas Gleixner , Steven Rostedt , Masami Hiramatsu Cc: Ingo Molnar , Borislav Petkov , Dave Hansen , "H . Peter Anvin" , x86@kernel.org, Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Mathieu Desnoyers , David Hildenbrand , Jonathan Corbet , Matthew Wilcox , linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-doc@vger.kernel.org, Jinchao Wang Subject: [RFC PATCH 13/13] Documentation/dev-tools: document KWatch Date: Wed, 15 Jul 2026 02:33:56 +0800 Message-ID: <20260714183356.13109-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714182243.10687-1-wangjinchao600@gmail.com> References: <20260714182243.10687-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Describe what KWatch is for, how it compares with KASAN and KFENCE, the debugfs configuration interface, the watch expression syntax, how to read hits from the trace buffer (including after a crash), and the current limitations. Signed-off-by: Jinchao Wang --- Documentation/dev-tools/index.rst | 1 + Documentation/dev-tools/kwatch.rst | 193 +++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 Documentation/dev-tools/kwatch.rst diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/in= dex.rst index 59cbb77b33ff..f4c748da63db 100644 --- a/Documentation/dev-tools/index.rst +++ b/Documentation/dev-tools/index.rst @@ -30,6 +30,7 @@ Documentation/process/debugging/index.rst ubsan kmemleak kcsan + kwatch lkmm/index kfence kselftest diff --git a/Documentation/dev-tools/kwatch.rst b/Documentation/dev-tools/k= watch.rst new file mode 100644 index 000000000000..8ead0beb06b6 --- /dev/null +++ b/Documentation/dev-tools/kwatch.rst @@ -0,0 +1,193 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +KWatch - Kernel Memory Watchpoint Tool +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Overview +=3D=3D=3D=3D=3D=3D=3D=3D + +KWatch is a runtime-configurable debugging tool for locating kernel memory +corruption. It arms hardware breakpoints (watchpoints) on a target address +while a chosen function is executing, and reports the exact instruction th= at +touches the watched memory, together with a stack trace, through a +tracepoint. + +Unlike shadow-memory sanitizers, KWatch does not detect invalid accesses in +general; it answers a narrower but common question during corruption hunts: +"who writes to this address?". This includes in-bounds logical overwrites +that KASAN cannot see, because the rogue writer modifies valid memory +through a valid pointer, just at the wrong time or with the wrong data. + +Comparison with other tools: + +* KASAN detects out-of-bounds and use-after-free accesses, but reports the + symptom (the invalid access), not the writer that corrupted the data + earlier. It requires a rebuild and has significant CPU and memory + overhead, and its redzones perturb memory layout, which can hide + timing-sensitive bugs. +* KFENCE is a low-overhead sampling detector for slab objects; it cannot be + pointed at one specific address. +* Hardware breakpoints via kgdb or perf can watch an address, but only a + fixed one, system-wide, for the whole run. KWatch resolves the address + dynamically at function entry (for example "argument 2 of this function, + plus offset 8, dereferenced once") and disarms it again at function exit, + so short-lived and per-invocation objects can be watched too. + +KWatch has near-zero overhead while armed: the watched function pays for +one kprobe/kretprobe pair plus programming of the debug registers; the rest +of the system runs at full speed. + +Requirements +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +* ``CONFIG_KWATCH=3Dy`` or ``m``. The Kconfig symbol depends on + ``CONFIG_PERF_EVENTS``, ``CONFIG_DEBUG_FS`` and an architecture that + provides ``HAVE_REINSTALL_HW_BREAKPOINT`` (currently x86 only). +* Resolving symbol names in watch expressions requires ``CONFIG_KWATCH=3Dy= `` + (built-in); a module can only watch absolute hexadecimal addresses. + +Usage +=3D=3D=3D=3D=3D + +KWatch is configured through a single debugfs file:: + + /sys/kernel/debug/kwatch/config + +Writing a configuration string starts a watch session (stopping any previo= us +one); reading the file shows the active configuration and hit-rejection +counters. The configuration is a whitespace-separated list of ``key=3Dvalu= e`` +tokens: + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D +Key Meaning +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D +``func_name`` Function whose execution opens the watch window. +``func_offset`` Instruction offset inside ``func_name`` at which the + watchpoint is armed (default 0 =3D function entry). +``watch_expr`` Expression describing the address to watch (see below). +``watch_len`` Watched length in bytes: 1, 2, 4 or 8 (default 8). +``access_type`` 0 =3D write (default), 1 =3D read, 2 =3D read/write, + 3 =3D execute. +``depth`` Recursion depth at which the window opens (default 0). +``max_watch`` Number of hardware watchpoints to preallocate + (default 4). +``max_concurrency`` Maximum number of tasks concurrently inside the watch + window (default 256). +``duration`` For global watches: seconds until automatic stop. +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D + +Watch expressions +----------------- + +The address to watch is computed at function entry from:: + + watch_expr=3D{base}[+-offset][->[+-]offset]... + +* ``base`` is one of: + + - ``arg1`` ... ``arg6``: a function argument (register calling + convention), + - ``stack``: the kernel stack pointer at the probe point, + - an absolute hexadecimal address, e.g. ``0xffffffff81234567``, + - a global symbol name (built-in KWatch only). + +* ``+offset`` / ``-offset`` adjusts the current address. +* ``->offset`` loads the pointer stored at the current address (via + ``get_kernel_nofault()``) and then applies the offset. Up to four chain + elements are supported; offsets must be explicit (``->`` alone is + rejected). + +Given:: + + struct some_struct { + struct some_struct *ptr; /* offset 0 */ + int num; /* offset 8 */ + }; + + void target_function(struct some_struct *arg1); + +typical expressions are: + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Expression Watches +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +``watch_expr=3Darg1`` ``&arg1->ptr`` (the pointer field itself) +``watch_expr=3Darg1+8`` ``&arg1->num`` +``watch_expr=3Darg1->0`` ``&arg1->ptr->ptr`` (one dereference) +``watch_expr=3Darg1->8`` ``&arg1->ptr->num`` +``watch_expr=3D0xffff...+8`` absolute address plus 8 +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Example: catch whoever overwrites ``arg1->num`` of a function while that +function runs:: + + echo "func_name=3Dtarget_function watch_expr=3Darg1+8 watch_len=3D4" \ + > /sys/kernel/debug/kwatch/config + +Watching global variables +------------------------- + +A global variable has no natural function window. When ``duration`` is +given without ``func_name``, KWatch starts an internal anchor kernel thread +that sleeps inside a dummy function, and uses that function as the window:: + + echo "watch_expr=3Djiffies_wobble duration=3D60 watch_len=3D8" \ + > /sys/kernel/debug/kwatch/config + +The session tears itself down when the duration expires. + +Reading hits +------------ + +Hits are emitted as the ``kwatch:kwatch_hit`` tracepoint, which is safe in +NMI-like contexts where printk is not. Each event carries the timestamp, +the instruction pointer, the watched address and a short stack trace:: + + echo 1 > /sys/kernel/debug/tracing/events/kwatch/kwatch_hit/enable + cat /sys/kernel/debug/tracing/trace_pipe + +If the corruption crashes the machine, the ring buffer can still be +recovered: + +* ``echo 1 > /proc/sys/kernel/ftrace_dump_on_oops`` (or the + ``ftrace_dump_on_oops`` boot parameter) dumps the buffer to the console + on an oops. +* With kdump, the buffer is present in the vmcore and can be read with + ``crash> trace``. +* ``CONFIG_PSTORE_FTRACE`` persists it across reboots on supported + platforms. + +Limitations +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +* Functions that run in a genuine NMI(-like) context are rejected at + function entry; rejected invocations never open a watch window and are + counted in the ``nmi_rejected`` field of the config file. Watching + functions reachable from NMI handlers is out of scope. +* The number of concurrent watchpoints is bounded by the CPU's debug + registers (typically 4). +* If the target address cannot be resolved at arming time (for example a + ``get_kernel_nofault()`` failure on a swapped or unmapped page), the + watchpoint is not armed for that invocation. +* Offsets in watch expressions are static; dynamic indexing such as + ``arg1->ptr[arg2]`` is not supported. +* arm64 is not yet supported: stepping over a hit that has a custom + overflow handler needs a generic mechanism in the arch code, which is + planned as a follow-up series. + +Implementation notes +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The implementation lives in ``mm/kwatch/`` and is split into a control +plane (``core.c``, the debugfs interface), an execution plane (``probe.c`` +and ``deref.c``: kprobe/kretprobe window management and address +resolution), and a resource plane (``hwbp.c`` and ``task_ctx.c``). + +Hardware watchpoints are preallocated as perf events on every CPU and +re-pointed at hit time with ``modify_wide_hw_breakpoint_local()``, a new +hw_breakpoint API that updates the breakpoint on the local CPU without +releasing its slot; other CPUs are updated by asynchronous IPIs. Per-task +window state is kept in a fixed-size, lockless open-addressing array +claimed with ``cmpxchg()``, so the hit path performs no allocation and +takes no locks, which keeps it safe in atomic and NMI-like contexts. --=20 2.53.0