Linux Documentation
 help / color / mirror / Atom feed
* Re: [PATCH 2/5] docs: fix repeated word 'that' across documentation
From: David Howells @ 2026-05-08 19:43 UTC (permalink / raw)
  To: Adrien Reynard
  Cc: Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Jonathan Corbet, Shuah Khan, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, David Howells,
	Paulo Alcantara, Masami Hiramatsu,
	open list:READ-COPY UPDATE (RCU), open list:DOCUMENTATION,
	open list, open list:DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS,
	open list:FILESYSTEMS [NETFS LIBRARY],
	open list:FILESYSTEMS [NETFS LIBRARY], open list:TRACING
In-Reply-To: <20260508163759.16231-1-reynard.adrien.08@gmail.com>

Adrien Reynard <reynard.adrien.08@gmail.com> wrote:

> -  three states, we know that that CPU has exited any previous RCU

This is arguably correct.  The two 'that' words are functionally different.
If you look at another language, say Hungarian, they are different words
(e.g. 'hogy' vs 'az').

David


^ permalink raw reply

* [PATCH v2] killswitch: add per-function short-circuit mitigation primitive
From: Sasha Levin @ 2026-05-08 19:57 UTC (permalink / raw)
  To: corbet, akpm
  Cc: skhan, linux-doc, linux-kernel, linux-kselftest, gregkh,
	Sasha Levin

When a kernel (security) issue goes public, fleets stay exposed until a patched
kernel is built, distributed, and rebooted into.

For many such issues the simplest mitigation is to stop calling the buggy
function. Killswitch provides that. An admin writes:

    echo "engage af_alg_sendmsg -1" \
        > /sys/kernel/security/killswitch/control

After this, af_alg_sendmsg() returns -EPERM on every call without
running its body. The mitigation takes effect immediately, and is dropped on
the next reboot -- by which point a patched kernel is hopefully in place.

A lot of recent kernel issues sit in code paths most installs only have enabled
to support a relative minority of users: AF_ALG, ksmbd, nf_tables, vsock, ax25,
and friends.

For most users, the cost of "this socket family stops working for the day" is
much smaller than the cost of running a known vulnerable kernel until the fix
land.

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
Changes since v1:
  - Add LOCKDOWN_KILLSWITCH (Jon)
  - Convert ks_attr.refcnt from refcount_t to kref. (Greg)
  - Document why ks_attr.retval is atomic_long_t. (Greg)

 Documentation/admin-guide/index.rst           |   1 +
 Documentation/admin-guide/killswitch.rst      | 159 ++++
 Documentation/admin-guide/tainted-kernels.rst |   8 +
 MAINTAINERS                                   |  11 +
 include/linux/killswitch.h                    |  19 +
 include/linux/panic.h                         |   3 +-
 include/linux/security.h                      |   1 +
 init/Kconfig                                  |   2 +
 kernel/Kconfig.killswitch                     |  31 +
 kernel/Makefile                               |   1 +
 kernel/killswitch.c                           | 858 ++++++++++++++++++
 kernel/panic.c                                |   1 +
 lib/Kconfig.debug                             |  13 +
 lib/Makefile                                  |   1 +
 lib/test_killswitch.c                         |  85 ++
 security/security.c                           |   1 +
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/killswitch/.gitignore |   1 +
 tools/testing/selftests/killswitch/Makefile   |   8 +
 .../selftests/killswitch/cve_31431_test.c     | 162 ++++
 .../selftests/killswitch/killswitch_test.sh   | 147 +++
 21 files changed, 1513 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/admin-guide/killswitch.rst
 create mode 100644 include/linux/killswitch.h
 create mode 100644 kernel/Kconfig.killswitch
 create mode 100644 kernel/killswitch.c
 create mode 100644 lib/test_killswitch.c
 create mode 100644 tools/testing/selftests/killswitch/.gitignore
 create mode 100644 tools/testing/selftests/killswitch/Makefile
 create mode 100644 tools/testing/selftests/killswitch/cve_31431_test.c
 create mode 100755 tools/testing/selftests/killswitch/killswitch_test.sh

diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst
index cd28dfe91b060..ca37dd70f108d 100644
--- a/Documentation/admin-guide/index.rst
+++ b/Documentation/admin-guide/index.rst
@@ -70,6 +70,7 @@ problems and bugs in particular.
    bug-hunting
    bug-bisect
    tainted-kernels
+   killswitch
    ramoops
    dynamic-debug-howto
    init
diff --git a/Documentation/admin-guide/killswitch.rst b/Documentation/admin-guide/killswitch.rst
new file mode 100644
index 0000000000000..cb967ec348fdc
--- /dev/null
+++ b/Documentation/admin-guide/killswitch.rst
@@ -0,0 +1,159 @@
+.. SPDX-License-Identifier: GPL-2.0
+..
+.. Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+
+============
+Killswitch
+============
+
+Killswitch lets a privileged operator make a chosen kernel function
+return a fixed value without executing its body, as a temporary
+mitigation for a security bug while a real fix is being prepared.
+
+The function returns the operator-supplied value and nothing else
+runs in its place. There is no allowlist, no return-type check; if
+the kprobe layer accepts the symbol, killswitch engages it. Once
+engaged, the change is in effect on every CPU until ``disengage`` is
+written or the system reboots.
+
+Configuration
+=============
+
+``CONFIG_KILLSWITCH``
+  Enables the feature. Depends on ``SECURITYFS``, ``KPROBES`` (with
+  ftrace support), and ``FUNCTION_ERROR_INJECTION``.
+
+The interface
+=============
+
+::
+
+    /sys/kernel/security/killswitch/
+        engaged                 RO  currently-engaged functions
+        control                 WO  command sink
+        taint                   RO  0 or 1
+        fn/<name>/              per-function directory, created on engage
+            retval              RW  return value
+            hits                RO  per-cpu summed call count
+
+Three commands are accepted by ``control``::
+
+    engage <symbol> <retval>
+    disengage <symbol>
+    disengage_all
+
+Each engage and disengage emits a single ``KERN_WARNING`` line to
+dmesg with the symbol, retval, hit count (on disengage), and the
+operator's identity (uid/auid/sessionid/comm, or ``source=cmdline``).
+
+Engagement is rejected when:
+
+* the symbol is unknown, in a non-traceable section, on the kprobe
+  blacklist, or otherwise refused by ``register_kprobe`` (the error
+  from the kprobe layer is logged and returned to userspace);
+* the symbol is already engaged (``-EBUSY``);
+* the operator does not hold ``CAP_SYS_ADMIN``.
+
+Whatever value the operator writes is what the function returns.
+Writing the wrong type or wrong value lands in the caller as-is.
+
+Boot parameter
+==============
+
+``killswitch=fn1=<val>,fn2=<val>,...``
+
+Parsed early; engagements are applied at the end of kernel init
+once the kprobe subsystem is up. Parse failures emit a warning and
+skip the offending entry; they never panic.
+
+Useful for fleet rollout: when an issue drops, ship the mitigation
+in the bootloader / PXE config and roll the fleet through reboots
+while the real fix is being prepared.
+
+Tainting
+========
+
+The first successful engagement (runtime or boot-time) sets
+``TAINT_KILLSWITCH`` (bit 20, char ``H``). The taint persists across
+``disengage`` until reboot, so an oops on a killswitch-modified
+kernel is identifiable from the banner: ``Tainted: ... H`` tells a
+maintainer to consult ``engaged`` before further triage.
+
+Module unload
+=============
+
+If a module containing an engaged target is unloaded, killswitch
+auto-disengages the entry and emits a ``KERN_WARNING`` so the loss
+of mitigation is visible. Reloading the module does not silently
+re-arm the killswitch; the operator re-engages explicitly.
+
+Choosing the right target
+=========================
+
+A function that *looks* skippable may be relied on by callers for a
+side effect (a lock the caller releases, a refcount the caller
+drops, a scatterlist the caller consumes). The rule of thumb:
+
+  Pick the **highest-level** entry point that contains the bug.
+
+That gives callers no chance to dereference half-initialised state
+from a function whose body was skipped. Two illustrative examples
+from ``crypto/af_alg.c``:
+
+Anti-pattern: ``af_alg_count_tsgl``
+-----------------------------------
+
+``af_alg_count_tsgl()`` returns ``unsigned int`` (the number of TX
+SG entries). Engaging it with retval ``0`` causes the caller in
+``algif_aead.c`` to allocate a 1-entry scatterlist (its
+``if (!entries) entries = 1`` guard) and then walk the *real* TX
+SGL into that undersized destination via ``af_alg_pull_tsgl``,
+producing out-of-bounds writes. **Killswitching here introduces a
+worse bug than the one being mitigated.**
+
+Anti-pattern: ``af_alg_pull_tsgl``
+----------------------------------
+
+``af_alg_pull_tsgl()`` returns ``void``, so any retval is accepted.
+But its caller depends on the per-request SGL being filled in.
+Skipping the body leaves the per-request SGL with NULL pages; the
+next-stage ``memcpy_sglist`` dereferences them and the kernel
+oopses.
+
+Correct pattern: ``af_alg_sendmsg``
+-----------------------------------
+
+``af_alg_sendmsg()`` is the highest-level entry into the AF_ALG
+send path. Engaging it with retval ``-EPERM`` causes every send
+attempt to return -EPERM to userspace; no caller ever sees
+half-initialised state, and any AF_ALG-reachable bug downstream of
+``sendmsg`` is unreachable until the killswitch is disengaged.
+
+The canonical pattern: pick a syscall-handler-shaped function whose
+return value already encodes "this operation didn't happen", and
+let userspace handle the error as it would any other failed
+syscall.
+
+Safety notes
+============
+
+* In-flight calls during ``write()`` to ``control`` may run either
+  the original body or the override. The override is ``return X``,
+  which has no preconditions to violate.
+* SMP visibility comes from ``text_poke_bp()``. ``write()`` to
+  ``control`` returns only after every CPU sees the new path.
+* The ftrace ops unregister waits for in-flight pre-handlers, so
+  freeing the engagement attribute on disengage is safe.
+* Inline functions, freed ``__init`` symbols, and anything compiled
+  away cannot be killswitched. ``register_kprobe`` rejects them
+  with whatever error the kprobe layer chooses.
+
+Diagnostics
+===========
+
+Per-call hits are aggregated in a per-cpu counter readable at
+``/sys/kernel/security/killswitch/fn/<name>/hits``. Per-hit logging
+is not provided to avoid log storms on hot paths.
+
+A ``KILLSWITCH`` entry appears in the kernel taint vector once any
+engagement succeeds (also visible as ``H`` in the oops banner).
diff --git a/Documentation/admin-guide/tainted-kernels.rst b/Documentation/admin-guide/tainted-kernels.rst
index 9ead927a37c0f..71a6e3364eddc 100644
--- a/Documentation/admin-guide/tainted-kernels.rst
+++ b/Documentation/admin-guide/tainted-kernels.rst
@@ -102,6 +102,7 @@ Bit  Log  Number  Reason that got the kernel tainted
  17  _/T  131072  kernel was built with the struct randomization plugin
  18  _/N  262144  an in-kernel test has been run
  19  _/J  524288  userspace used a mutating debug operation in fwctl
+ 20  _/H 1048576  killswitch override engaged (function short-circuited)
 ===  ===  ======  ========================================================
 
 Note: The character ``_`` is representing a blank in this table to make reading
@@ -189,3 +190,10 @@ More detailed explanation for tainting
  19) ``J`` if userspace opened /dev/fwctl/* and performed a FWTCL_RPC_DEBUG_WRITE
      to use the devices debugging features. Device debugging features could
      cause the device to malfunction in undefined ways.
+
+ 20) ``H`` if the killswitch primitive (see
+     Documentation/admin-guide/killswitch.rst) has been engaged on at least
+     one function. The kernel is no longer running its source: at least one
+     function has been short-circuited to return a fixed value. The taint
+     persists across ``disengage`` until the next reboot — once the running
+     image has been modified, oops triage must reflect that.
diff --git a/MAINTAINERS b/MAINTAINERS
index 882214b0e7db5..61851ef1d9b1c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14347,6 +14347,17 @@ F:	lib/Kconfig.kmsan
 F:	mm/kmsan/
 F:	scripts/Makefile.kmsan
 
+KILLSWITCH (function short-circuit mitigation)
+M:	Sasha Levin <sashal@kernel.org>
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+F:	Documentation/admin-guide/killswitch.rst
+F:	include/linux/killswitch.h
+F:	kernel/Kconfig.killswitch
+F:	kernel/killswitch.c
+F:	lib/test_killswitch.c
+F:	tools/testing/selftests/killswitch/
+
 KPROBES
 M:	Naveen N Rao <naveen@kernel.org>
 M:	"David S. Miller" <davem@davemloft.net>
diff --git a/include/linux/killswitch.h b/include/linux/killswitch.h
new file mode 100644
index 0000000000000..c18515bec09f1
--- /dev/null
+++ b/include/linux/killswitch.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ */
+#ifndef _LINUX_KILLSWITCH_H
+#define _LINUX_KILLSWITCH_H
+
+#ifdef CONFIG_KILLSWITCH
+int killswitch_engage(const char *symbol, long retval);
+int killswitch_disengage(const char *symbol);
+bool killswitch_is_engaged(const char *symbol);
+#else
+static inline int killswitch_engage(const char *symbol, long retval)
+{ return -ENOSYS; }
+static inline int killswitch_disengage(const char *symbol) { return -ENOSYS; }
+static inline bool killswitch_is_engaged(const char *symbol) { return false; }
+#endif
+
+#endif /* _LINUX_KILLSWITCH_H */
diff --git a/include/linux/panic.h b/include/linux/panic.h
index f1dd417e54b29..6699261a61f13 100644
--- a/include/linux/panic.h
+++ b/include/linux/panic.h
@@ -88,7 +88,8 @@ static inline void set_arch_panic_timeout(int timeout, int arch_default_timeout)
 #define TAINT_RANDSTRUCT		17
 #define TAINT_TEST			18
 #define TAINT_FWCTL			19
-#define TAINT_FLAGS_COUNT		20
+#define TAINT_KILLSWITCH		20
+#define TAINT_FLAGS_COUNT		21
 #define TAINT_FLAGS_MAX			((1UL << TAINT_FLAGS_COUNT) - 1)
 
 struct taint_flag {
diff --git a/include/linux/security.h b/include/linux/security.h
index 41d7367cf4036..038027c33ba1a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -146,6 +146,7 @@ enum lockdown_reason {
 	LOCKDOWN_DBG_WRITE_KERNEL,
 	LOCKDOWN_RTAS_ERROR_INJECTION,
 	LOCKDOWN_XEN_USER_ACTIONS,
+	LOCKDOWN_KILLSWITCH,
 	LOCKDOWN_INTEGRITY_MAX,
 	LOCKDOWN_KCORE,
 	LOCKDOWN_KPROBES,
diff --git a/init/Kconfig b/init/Kconfig
index 2937c4d308aec..5368dd4b5c65b 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2278,6 +2278,8 @@ config ASN1
 
 source "kernel/Kconfig.locks"
 
+source "kernel/Kconfig.killswitch"
+
 config ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
 	bool
 
diff --git a/kernel/Kconfig.killswitch b/kernel/Kconfig.killswitch
new file mode 100644
index 0000000000000..067d41087e8da
--- /dev/null
+++ b/kernel/Kconfig.killswitch
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Killswitch: per-function short-circuit mitigation primitive.
+#
+# Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+#
+
+config KILLSWITCH
+	bool "Killswitch: short-circuit a kernel function as a CVE mitigation"
+	depends on SECURITYFS
+	depends on KPROBES && HAVE_KPROBES_ON_FTRACE
+	depends on HAVE_FUNCTION_ERROR_INJECTION
+	select FUNCTION_ERROR_INJECTION
+	help
+	  Provide an admin-facing mechanism to make a chosen kernel function
+	  return a fixed value without executing its body, as a temporary
+	  mitigation for a security bug before a real fix is available.
+
+	  Operators write "engage <symbol> <retval> [reason]" to
+	  /sys/kernel/security/killswitch/control. The function entry is
+	  redirected via a kprobe whose pre-handler sets the chosen return
+	  value and short-circuits the call. There is no allowlist,
+	  denylist, or return-type validation: if the kprobe layer accepts
+	  the symbol the engagement proceeds, otherwise its error is
+	  returned to userspace.
+
+	  This is *not* livepatch: there is no replacement implementation,
+	  the function simply returns the chosen value. Engaging a killswitch
+	  taints the kernel (TAINT_KILLSWITCH, 'H'). Requires CAP_SYS_ADMIN.
+
+	  If unsure, say N.
diff --git a/kernel/Makefile b/kernel/Makefile
index 6785982013dce..b3e408d9f275e 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_GCOV_KERNEL) += gcov/
 obj-$(CONFIG_KCOV) += kcov.o
 obj-$(CONFIG_KPROBES) += kprobes.o
 obj-$(CONFIG_FAIL_FUNCTION) += fail_function.o
+obj-$(CONFIG_KILLSWITCH) += killswitch.o
 obj-$(CONFIG_KGDB) += debug/
 obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
 obj-$(CONFIG_LOCKUP_DETECTOR) += watchdog.o
diff --git a/kernel/killswitch.c b/kernel/killswitch.c
new file mode 100644
index 0000000000000..50993923898d3
--- /dev/null
+++ b/kernel/killswitch.c
@@ -0,0 +1,858 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Per-function short-circuit mitigation.
+ *
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * Engaging a killswitch installs a kprobe at the function's entry
+ * whose pre-handler sets the return register and skips the body via
+ * override_function_with_return().  Operator interface lives at
+ * /sys/kernel/security/killswitch/.
+ */
+
+#include <linux/audit.h>
+#include <linux/capability.h>
+#include <linux/cred.h>
+#include <linux/ctype.h>
+#include <linux/error-injection.h>
+#include <linux/init.h>
+#include <linux/killswitch.h>
+#include <linux/kprobes.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/panic.h>
+#include <linux/percpu.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+#include <linux/security.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/uidgid.h>
+
+struct ks_attr {
+	struct list_head	list;
+	struct kprobe		kp;
+	/* atomic so a writer racing an in-flight call can't tear the long. */
+	atomic_long_t		retval;
+	/* false once disengaged; per-fn file ops then return -EIDRM. */
+	bool			engaged;
+	unsigned long __percpu	*hits;
+	struct dentry		*dir;
+	/* engaged_list holds one ref; each open per-fn fd holds one. */
+	struct kref		refcnt;
+};
+
+static DEFINE_MUTEX(ks_lock);
+static LIST_HEAD(ks_engaged_list);
+static struct dentry *ks_root_dir;
+static struct dentry *ks_fn_dir;	/* parent for per-fn directories */
+
+/* ------------------------------------------------------------------ *
+ * Pre-handler: the actual override                                   *
+ * ------------------------------------------------------------------ */
+
+static int ks_kprobe_pre_handler(struct kprobe *kp, struct pt_regs *regs)
+{
+	struct ks_attr *attr = container_of(kp, struct ks_attr, kp);
+
+	this_cpu_inc(*attr->hits);
+	regs_set_return_value(regs, (unsigned long)atomic_long_read(&attr->retval));
+	override_function_with_return(regs);
+	return 1;
+}
+NOKPROBE_SYMBOL(ks_kprobe_pre_handler);
+
+/* Defined non-NULL so the kprobe layer keeps the IPMODIFY ops. */
+static void ks_kprobe_post_handler(struct kprobe *kp, struct pt_regs *regs,
+				   unsigned long flags)
+{
+}
+
+/* ------------------------------------------------------------------ *
+ * Attribute lifecycle                                                *
+ * ------------------------------------------------------------------ */
+
+static struct ks_attr *ks_attr_lookup(const char *symbol)
+{
+	struct ks_attr *attr;
+
+	list_for_each_entry(attr, &ks_engaged_list, list)
+		if (!strcmp(attr->kp.symbol_name, symbol))
+			return attr;
+	return NULL;
+}
+
+static unsigned long ks_attr_hits(const struct ks_attr *attr)
+{
+	unsigned long total = 0;
+	int cpu;
+
+	for_each_possible_cpu(cpu)
+		total += *per_cpu_ptr(attr->hits, cpu);
+	return total;
+}
+
+static void ks_attr_destroy(struct ks_attr *attr)
+{
+	if (!attr)
+		return;
+	free_percpu(attr->hits);
+	kfree(attr->kp.symbol_name);
+	kfree(attr);
+}
+
+static void ks_attr_kref_release(struct kref *kref)
+{
+	ks_attr_destroy(container_of(kref, struct ks_attr, refcnt));
+}
+
+static void ks_attr_get(struct ks_attr *attr)
+{
+	kref_get(&attr->refcnt);
+}
+
+static void ks_attr_put(struct ks_attr *attr)
+{
+	kref_put(&attr->refcnt, ks_attr_kref_release);
+}
+
+static struct ks_attr *ks_attr_alloc(const char *symbol)
+{
+	struct ks_attr *attr;
+
+	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+	if (!attr)
+		return NULL;
+
+	attr->kp.symbol_name = kstrdup(symbol, GFP_KERNEL);
+	if (!attr->kp.symbol_name)
+		goto err;
+
+	attr->hits = alloc_percpu(unsigned long);
+	if (!attr->hits)
+		goto err;
+
+	attr->kp.pre_handler = ks_kprobe_pre_handler;
+	attr->kp.post_handler = ks_kprobe_post_handler;
+	INIT_LIST_HEAD(&attr->list);
+	kref_init(&attr->refcnt);
+	return attr;
+
+err:
+	ks_attr_destroy(attr);
+	return NULL;
+}
+
+/* ------------------------------------------------------------------ *
+ * Securityfs: per-fn attribute files                                 *
+ * ------------------------------------------------------------------ */
+
+/*
+ * Look up by symbol name (the parent dentry's basename) under
+ * ks_lock and confirm attr->dir is the file's parent dentry.  This
+ * binds the fd to the engagement it was opened against and avoids
+ * dereferencing inode->i_private, which a racing disengage may have
+ * freed.  d_parent is stable for the open's lifetime via the file's
+ * dentry reference.
+ */
+static int ks_attr_open(struct inode *inode, struct file *file)
+{
+	struct dentry *parent = file->f_path.dentry->d_parent;
+	const char *name = parent->d_name.name;
+	struct ks_attr *attr;
+
+	mutex_lock(&ks_lock);
+	attr = ks_attr_lookup(name);
+	if (attr && attr->dir == parent)
+		ks_attr_get(attr);
+	else
+		attr = NULL;
+	mutex_unlock(&ks_lock);
+	if (!attr)
+		return -ENOENT;
+	file->private_data = attr;
+	return 0;
+}
+
+static int ks_attr_release(struct inode *inode, struct file *file)
+{
+	ks_attr_put(file->private_data);
+	file->private_data = NULL;
+	return 0;
+}
+
+/* Caller must hold ks_lock. */
+static int ks_attr_check_live(const struct ks_attr *attr)
+{
+	return attr->engaged ? 0 : -EIDRM;
+}
+
+static ssize_t ks_retval_read(struct file *file, char __user *ubuf,
+			      size_t count, loff_t *ppos)
+{
+	struct ks_attr *attr = file->private_data;
+	char buf[32];
+	long val;
+	int ret, len;
+
+	mutex_lock(&ks_lock);
+	ret = ks_attr_check_live(attr);
+	val = atomic_long_read(&attr->retval);
+	mutex_unlock(&ks_lock);
+	if (ret)
+		return ret;
+	len = scnprintf(buf, sizeof(buf), "%ld\n", val);
+	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static ssize_t ks_retval_write(struct file *file, const char __user *ubuf,
+			       size_t count, loff_t *ppos)
+{
+	struct ks_attr *attr = file->private_data;
+	char buf[32];
+	long val;
+	int ret;
+
+	if (count >= sizeof(buf))
+		return -EINVAL;
+	if (copy_from_user(buf, ubuf, count))
+		return -EFAULT;
+	buf[count] = '\0';
+	strim(buf);
+
+	ret = kstrtol(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&ks_lock);
+	ret = ks_attr_check_live(attr);
+	if (!ret)
+		atomic_long_set(&attr->retval, val);
+	mutex_unlock(&ks_lock);
+
+	return ret ? ret : count;
+}
+
+static const struct file_operations ks_retval_fops = {
+	.open		= ks_attr_open,
+	.release	= ks_attr_release,
+	.read		= ks_retval_read,
+	.write	= ks_retval_write,
+	.llseek	= default_llseek,
+};
+
+static ssize_t ks_hits_read(struct file *file, char __user *ubuf,
+			    size_t count, loff_t *ppos)
+{
+	struct ks_attr *attr = file->private_data;
+	char buf[32];
+	unsigned long hits;
+	int ret, len;
+
+	mutex_lock(&ks_lock);
+	ret = ks_attr_check_live(attr);
+	hits = ks_attr_hits(attr);
+	mutex_unlock(&ks_lock);
+	if (ret)
+		return ret;
+	len = scnprintf(buf, sizeof(buf), "%lu\n", hits);
+	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations ks_hits_fops = {
+	.open		= ks_attr_open,
+	.release	= ks_attr_release,
+	.read		= ks_hits_read,
+	.llseek		= default_llseek,
+};
+
+static int ks_create_attr_dir(struct ks_attr *attr)
+{
+	struct dentry *d;
+
+	attr->dir = securityfs_create_dir(attr->kp.symbol_name, ks_fn_dir);
+	if (IS_ERR(attr->dir))
+		return PTR_ERR(attr->dir);
+
+	/* ks_attr_open looks the attr up by name; i_private is unused. */
+	d = securityfs_create_file("retval", 0600, attr->dir,
+				   NULL, &ks_retval_fops);
+	if (IS_ERR(d))
+		goto err;
+	d = securityfs_create_file("hits", 0400, attr->dir,
+				   NULL, &ks_hits_fops);
+	if (IS_ERR(d))
+		goto err;
+	return 0;
+err:
+	securityfs_remove(attr->dir);
+	attr->dir = NULL;
+	return PTR_ERR(d);
+}
+
+/* ------------------------------------------------------------------ *
+ * Engage / disengage                                                 *
+ * ------------------------------------------------------------------ */
+
+static int __ks_engage(const char *symbol, long retval, bool from_cmdline)
+{
+	struct ks_attr *attr;
+	int ret;
+
+	if (!symbol || !*symbol)
+		return -EINVAL;
+
+	if (!from_cmdline) {
+		ret = security_locked_down(LOCKDOWN_KILLSWITCH);
+		if (ret)
+			return ret;
+	}
+
+	mutex_lock(&ks_lock);
+
+	if (ks_attr_lookup(symbol)) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+	attr = ks_attr_alloc(symbol);
+	if (!attr) {
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+
+	atomic_long_set(&attr->retval, retval);
+
+	ret = register_kprobe(&attr->kp);
+	if (ret) {
+		pr_warn("killswitch: register_kprobe(%s) failed: %d\n",
+			symbol, ret);
+		ks_attr_put(attr);
+		goto out_unlock;
+	}
+
+	ret = ks_create_attr_dir(attr);
+	if (ret) {
+		unregister_kprobe(&attr->kp);
+		ks_attr_put(attr);
+		goto out_unlock;
+	}
+
+	list_add_tail(&attr->list, &ks_engaged_list);
+	attr->engaged = true;
+	add_taint(TAINT_KILLSWITCH, LOCKDEP_STILL_OK);
+
+	if (from_cmdline) {
+		pr_warn("killswitch: engage %s=%ld source=cmdline\n",
+			symbol, retval);
+	} else {
+		pr_warn("killswitch: engage %s=%ld uid=%u auid=%u ses=%u comm=%s\n",
+			symbol, retval,
+			from_kuid(&init_user_ns, current_uid()),
+			from_kuid(&init_user_ns, audit_get_loginuid(current)),
+			audit_get_sessionid(current),
+			current->comm);
+	}
+	ret = 0;
+
+out_unlock:
+	mutex_unlock(&ks_lock);
+	return ret;
+}
+
+int killswitch_engage(const char *symbol, long retval)
+{
+	return __ks_engage(symbol, retval, false);
+}
+
+static int __ks_disengage(const char *symbol)
+{
+	struct ks_attr *attr;
+	unsigned long hits;
+	int ret = 0;
+
+	mutex_lock(&ks_lock);
+	attr = ks_attr_lookup(symbol);
+	if (!attr) {
+		ret = -ENOENT;
+		goto out_unlock;
+	}
+
+	unregister_kprobe(&attr->kp);
+	attr->engaged = false;
+	list_del(&attr->list);
+	hits = ks_attr_hits(attr);
+	securityfs_remove(attr->dir);
+
+	pr_warn("killswitch: disengage %s hits=%lu uid=%u auid=%u ses=%u comm=%s\n",
+		symbol, hits,
+		from_kuid(&init_user_ns, current_uid()),
+		from_kuid(&init_user_ns, audit_get_loginuid(current)),
+		audit_get_sessionid(current),
+		current->comm);
+
+	/* unregister_kprobe() already waited out in-flight pre-handlers. */
+	ks_attr_put(attr);
+
+out_unlock:
+	mutex_unlock(&ks_lock);
+	return ret;
+}
+
+int killswitch_disengage(const char *symbol)
+{
+	return __ks_disengage(symbol);
+}
+
+bool killswitch_is_engaged(const char *symbol)
+{
+	bool engaged;
+
+	mutex_lock(&ks_lock);
+	engaged = ks_attr_lookup(symbol) != NULL;
+	mutex_unlock(&ks_lock);
+	return engaged;
+}
+
+static void ks_disengage_all_locked(void)
+{
+	struct ks_attr *attr, *n;
+
+	list_for_each_entry_safe(attr, n, &ks_engaged_list, list) {
+		unregister_kprobe(&attr->kp);
+		attr->engaged = false;
+		list_del(&attr->list);
+		securityfs_remove(attr->dir);
+		pr_warn("killswitch: disengage %s hits=%lu (disengage_all)\n",
+			attr->kp.symbol_name, ks_attr_hits(attr));
+		ks_attr_put(attr);
+	}
+}
+
+/* ------------------------------------------------------------------ *
+ * Module unload: drop engagements on functions in the going module   *
+ * ------------------------------------------------------------------ */
+
+static int ks_module_notify(struct notifier_block *nb, unsigned long action,
+			    void *data)
+{
+	struct module *mod = data;
+	struct ks_attr *attr, *n;
+
+	if (action != MODULE_STATE_GOING)
+		return NOTIFY_DONE;
+
+	mutex_lock(&ks_lock);
+	list_for_each_entry_safe(attr, n, &ks_engaged_list, list) {
+		if (!attr->kp.addr ||
+		    __module_address((unsigned long)attr->kp.addr) != mod)
+			continue;
+
+		pr_warn("killswitch: %s mitigation lost: module %s unloading; re-engage after reload if still needed\n",
+			attr->kp.symbol_name, mod->name);
+		unregister_kprobe(&attr->kp);
+		attr->engaged = false;
+		list_del(&attr->list);
+		securityfs_remove(attr->dir);
+		ks_attr_put(attr);
+	}
+	mutex_unlock(&ks_lock);
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block ks_module_nb = {
+	.notifier_call = ks_module_notify,
+};
+
+/* ------------------------------------------------------------------ *
+ * Top-level securityfs files: control / engaged / taint              *
+ * ------------------------------------------------------------------ */
+
+static int ks_engaged_show(struct seq_file *m, void *v)
+{
+	struct ks_attr *attr;
+
+	mutex_lock(&ks_lock);
+	list_for_each_entry(attr, &ks_engaged_list, list) {
+		seq_printf(m, "%s retval=%ld hits=%lu\n",
+			   attr->kp.symbol_name,
+			   atomic_long_read(&attr->retval),
+			   ks_attr_hits(attr));
+	}
+	mutex_unlock(&ks_lock);
+	return 0;
+}
+
+static int ks_engaged_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ks_engaged_show, NULL);
+}
+
+static const struct file_operations ks_engaged_fops = {
+	.open		= ks_engaged_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static ssize_t ks_taint_read(struct file *file, char __user *ubuf,
+			     size_t count, loff_t *ppos)
+{
+	char buf[4];
+	int len;
+
+	len = scnprintf(buf, sizeof(buf), "%d\n",
+			test_taint(TAINT_KILLSWITCH) ? 1 : 0);
+	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations ks_taint_fops = {
+	.open	= simple_open,
+	.read	= ks_taint_read,
+	.llseek	= default_llseek,
+};
+
+/*
+ * control: parse one of:
+ *   engage <symbol> <retval>
+ *   disengage <symbol>
+ *   disengage_all
+ */
+static ssize_t ks_control_write(struct file *file, const char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char *buf, *cur, *verb, *sym, *retstr;
+	long retval = 0;
+	int ret;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (count == 0 || count > 4096)
+		return -EINVAL;
+
+	buf = memdup_user_nul(ubuf, count);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	cur = strim(buf);
+	verb = strsep(&cur, " \t\n");
+	if (!verb || !*verb) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!strcmp(verb, "disengage_all")) {
+		mutex_lock(&ks_lock);
+		ks_disengage_all_locked();
+		mutex_unlock(&ks_lock);
+		ret = count;
+		goto out;
+	}
+
+	sym = strsep(&cur, " \t\n");
+	if (!sym || !*sym) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!strcmp(verb, "disengage")) {
+		ret = __ks_disengage(sym);
+		ret = ret ? ret : count;
+		goto out;
+	}
+
+	if (strcmp(verb, "engage")) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	retstr = strsep(&cur, " \t\n");
+	if (!retstr || !*retstr) {
+		ret = -EINVAL;
+		goto out;
+	}
+	if (kstrtol(retstr, 0, &retval)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = killswitch_engage(sym, retval);
+	if (!ret)
+		ret = count;
+
+out:
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations ks_control_fops = {
+	.open	= simple_open,
+	.write	= ks_control_write,
+	.llseek	= noop_llseek,
+};
+
+/* ------------------------------------------------------------------ *
+ * Boot parameter:                                                    *
+ *   killswitch=fn1=-1:reason,fn2=0,fn3=void                          *
+ * ------------------------------------------------------------------ */
+
+#define KS_BOOT_BUF 1024
+static char ks_boot_buf[KS_BOOT_BUF] __initdata;
+static bool ks_boot_present __initdata;
+
+static int __init ks_boot_setup(char *str)
+{
+	if (!str)
+		return 0;
+	strscpy(ks_boot_buf, str, sizeof(ks_boot_buf));
+	ks_boot_present = true;
+	return 1;
+}
+__setup("killswitch=", ks_boot_setup);
+
+static void __init ks_apply_boot_params(void)
+{
+	char *cur, *tok;
+	long retval;
+
+	if (!ks_boot_present)
+		return;
+
+	cur = ks_boot_buf;
+	while ((tok = strsep(&cur, ",")) != NULL) {
+		char *eq, *sym, *retstr;
+
+		if (!*tok)
+			continue;
+		eq = strchr(tok, '=');
+		if (!eq) {
+			pr_warn("killswitch: cmdline missing '=': %s\n", tok);
+			continue;
+		}
+		*eq++ = '\0';
+		sym = tok;
+		retstr = eq;
+
+		if (kstrtol(retstr, 0, &retval)) {
+			pr_warn("killswitch: cmdline bad retval %s=%s\n",
+				sym, retstr);
+			continue;
+		}
+
+		if (__ks_engage(sym, retval, true))
+			pr_warn("killswitch: cmdline engage %s failed\n", sym);
+	}
+}
+
+/* ------------------------------------------------------------------ *
+ * Init                                                               *
+ * ------------------------------------------------------------------ */
+
+static int __init killswitch_init(void)
+{
+	struct dentry *d;
+
+	ks_root_dir = securityfs_create_dir("killswitch", NULL);
+	if (IS_ERR(ks_root_dir))
+		return PTR_ERR(ks_root_dir);
+
+	d = securityfs_create_file("control", 0200, ks_root_dir,
+				   NULL, &ks_control_fops);
+	if (IS_ERR(d))
+		goto err;
+	d = securityfs_create_file("engaged", 0444, ks_root_dir,
+				   NULL, &ks_engaged_fops);
+	if (IS_ERR(d))
+		goto err;
+	d = securityfs_create_file("taint", 0444, ks_root_dir,
+				   NULL, &ks_taint_fops);
+	if (IS_ERR(d))
+		goto err;
+
+	ks_fn_dir = securityfs_create_dir("fn", ks_root_dir);
+	if (IS_ERR(ks_fn_dir)) {
+		d = ks_fn_dir;
+		goto err;
+	}
+
+	register_module_notifier(&ks_module_nb);
+	ks_apply_boot_params();
+
+	pr_info("killswitch: ready (sysfs at /sys/kernel/security/killswitch/)\n");
+	return 0;
+
+err:
+	securityfs_remove(ks_root_dir);
+	return PTR_ERR(d);
+}
+late_initcall(killswitch_init);
+
+/* ------------------------------------------------------------------ *
+ * KUnit tests                                                        *
+ * ------------------------------------------------------------------ */
+
+#if IS_ENABLED(CONFIG_KUNIT)
+#include <kunit/test.h>
+
+/* Non-static so kallsyms resolves them without CONFIG_KALLSYMS_ALL. */
+int ks_kunit_target_int(int x);
+void *ks_kunit_target_ptr(int x);
+
+/* noipa keeps the call out-of-line and uneliminated. */
+__attribute__((__noipa__)) int ks_kunit_target_int(int x)
+{
+	return x + 1;
+}
+
+__attribute__((__noipa__)) void *ks_kunit_target_ptr(int x)
+{
+	return ERR_PTR(-EIO);
+}
+
+static int ks_kunit_init(struct kunit *test)
+{
+	if (security_locked_down(LOCKDOWN_KILLSWITCH))
+		kunit_skip(test, "integrity lockdown blocks killswitch_engage()");
+	return 0;
+}
+
+static int ks_kunit_init_lockdown(struct kunit *test)
+{
+	if (!security_locked_down(LOCKDOWN_KILLSWITCH))
+		kunit_skip(test, "requires lockdown=integrity");
+	return 0;
+}
+
+static void ks_disengage_quiet(const char *sym)
+{
+	if (killswitch_is_engaged(sym))
+		killswitch_disengage(sym);
+}
+
+static void ks_test_engage_int(struct kunit *test)
+{
+	int ret;
+
+	ret = killswitch_engage("ks_kunit_target_int", -EPERM);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	KUNIT_EXPECT_EQ(test, ks_kunit_target_int(7), -EPERM);
+	KUNIT_EXPECT_EQ(test, killswitch_disengage("ks_kunit_target_int"), 0);
+	KUNIT_EXPECT_EQ(test, ks_kunit_target_int(7), 8);
+}
+
+static void ks_test_double_engage(struct kunit *test)
+{
+	KUNIT_ASSERT_EQ(test,
+		killswitch_engage("ks_kunit_target_int", 0), 0);
+	KUNIT_EXPECT_EQ(test,
+		killswitch_engage("ks_kunit_target_int", 0), -EBUSY);
+	ks_disengage_quiet("ks_kunit_target_int");
+}
+
+static void ks_test_disengage_unknown(struct kunit *test)
+{
+	KUNIT_EXPECT_EQ(test,
+		killswitch_disengage("ks_kunit_target_int"), -ENOENT);
+}
+
+static void ks_test_pointer_target(struct kunit *test)
+{
+	long retval = (long)(unsigned long)ERR_PTR(-EACCES);
+
+	KUNIT_ASSERT_EQ(test,
+		killswitch_engage("ks_kunit_target_ptr", retval), 0);
+	KUNIT_EXPECT_TRUE(test, IS_ERR(ks_kunit_target_ptr(0)));
+	KUNIT_EXPECT_EQ(test, PTR_ERR(ks_kunit_target_ptr(0)), -EACCES);
+	ks_disengage_quiet("ks_kunit_target_ptr");
+}
+
+static void ks_test_taint_set(struct kunit *test)
+{
+	KUNIT_ASSERT_EQ(test,
+		killswitch_engage("ks_kunit_target_int", 0), 0);
+	KUNIT_EXPECT_TRUE(test, test_taint(TAINT_KILLSWITCH));
+	ks_disengage_quiet("ks_kunit_target_int");
+	/* taint must persist even after disengage */
+	KUNIT_EXPECT_TRUE(test, test_taint(TAINT_KILLSWITCH));
+}
+
+static void ks_test_hits_counter(struct kunit *test)
+{
+	struct ks_attr *attr;
+	int i;
+
+	KUNIT_ASSERT_EQ(test,
+		killswitch_engage("ks_kunit_target_int", 0), 0);
+
+	for (i = 0; i < 17; i++)
+		(void)ks_kunit_target_int(i);
+
+	mutex_lock(&ks_lock);
+	attr = ks_attr_lookup("ks_kunit_target_int");
+	KUNIT_EXPECT_NOT_NULL(test, attr);
+	if (attr)
+		KUNIT_EXPECT_EQ(test, ks_attr_hits(attr), 17UL);
+	mutex_unlock(&ks_lock);
+
+	ks_disengage_quiet("ks_kunit_target_int");
+}
+
+static struct kunit_case ks_kunit_cases[] = {
+	KUNIT_CASE(ks_test_engage_int),
+	KUNIT_CASE(ks_test_double_engage),
+	KUNIT_CASE(ks_test_disengage_unknown),
+	KUNIT_CASE(ks_test_pointer_target),
+	KUNIT_CASE(ks_test_taint_set),
+	KUNIT_CASE(ks_test_hits_counter),
+	{}
+};
+
+static struct kunit_suite ks_kunit_suite = {
+	.name = "killswitch",
+	.init = ks_kunit_init,
+	.test_cases = ks_kunit_cases,
+};
+
+/*
+ * Lockdown suite. Skipped unless the kernel was booted with
+ * lockdown=integrity (or higher). Run together with
+ * killswitch=ks_kunit_target_int=... on the same cmdline to also
+ * exercise the cmdline-bypass and disengage-under-lockdown paths.
+ */
+static void ks_test_lockdown_runtime_engage(struct kunit *test)
+{
+	KUNIT_EXPECT_EQ(test,
+		killswitch_engage("ks_kunit_target_int", 0), -EPERM);
+}
+
+static void ks_test_lockdown_cmdline_disengage(struct kunit *test)
+{
+	if (!killswitch_is_engaged("ks_kunit_target_int"))
+		kunit_skip(test,
+			   "requires killswitch=ks_kunit_target_int=... on cmdline");
+	KUNIT_EXPECT_EQ(test,
+		killswitch_disengage("ks_kunit_target_int"), 0);
+}
+
+static struct kunit_case ks_kunit_lockdown_cases[] = {
+	KUNIT_CASE(ks_test_lockdown_runtime_engage),
+	KUNIT_CASE(ks_test_lockdown_cmdline_disengage),
+	{}
+};
+
+static struct kunit_suite ks_kunit_lockdown_suite = {
+	.name = "killswitch_lockdown",
+	.init = ks_kunit_init_lockdown,
+	.test_cases = ks_kunit_lockdown_cases,
+};
+
+kunit_test_suites(&ks_kunit_suite, &ks_kunit_lockdown_suite);
+
+#endif /* CONFIG_KUNIT */
+
diff --git a/kernel/panic.c b/kernel/panic.c
index 20feada5319d4..8ee174c7b7dd0 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -825,6 +825,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = {
 	TAINT_FLAG(RANDSTRUCT,			'T', ' '),
 	TAINT_FLAG(TEST,			'N', ' '),
 	TAINT_FLAG(FWCTL,			'J', ' '),
+	TAINT_FLAG(KILLSWITCH,			'H', ' '),
 };
 
 #undef TAINT_FLAG
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 8ff5adcfe1e0a..5770639c7b0ea 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3349,6 +3349,19 @@ config TEST_HMM
 
 	  If unsure, say N.
 
+config TEST_KILLSWITCH
+	tristate "Test module for the killswitch mitigation primitive"
+	depends on KILLSWITCH && DEBUG_FS
+	depends on m
+	help
+	  Build a module that exposes a deliberately-vulnerable function
+	  ks_test_vuln() and a debugfs trigger /sys/kernel/debug/test_killswitch/fire.
+	  The killswitch selftest in tools/testing/selftests/killswitch/
+	  uses this to confirm engaging a killswitch suppresses the BUG()
+	  the function would otherwise hit.
+
+	  If unsure, say N.
+
 config TEST_FREE_PAGES
 	tristate "Test freeing pages"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index f33a24bf1c19a..d763225340674 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o
 obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o
 obj-$(CONFIG_TEST_MEMINIT) += test_meminit.o
 obj-$(CONFIG_TEST_LOCKUP) += test_lockup.o
+obj-$(CONFIG_TEST_KILLSWITCH) += test_killswitch.o
 obj-$(CONFIG_TEST_HMM) += test_hmm.o
 obj-$(CONFIG_TEST_FREE_PAGES) += test_free_pages.o
 obj-$(CONFIG_TEST_REF_TRACKER) += test_ref_tracker.o
diff --git a/lib/test_killswitch.c b/lib/test_killswitch.c
new file mode 100644
index 0000000000000..cc2584ad652ff
--- /dev/null
+++ b/lib/test_killswitch.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test target for the killswitch selftest.  ks_test_vuln() returns
+ * -EBADMSG on a magic input, standing in for "the buggy path runs
+ * and produces a bad outcome".  Engaging killswitch on this function
+ * with retval 0 is the mitigation.
+ *
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+#define KS_TEST_MAGIC	0xC0FFEEL
+
+int ks_test_vuln(long magic);
+
+/*
+ * Returns -EBADMSG on the magic input -- stands in for "the buggy
+ * path runs and produces a bad outcome".  Engaging a killswitch on
+ * this function with retval 0 represents the mitigation: even on
+ * the magic input, callers see success because the body never runs.
+ *
+ * noipa prevents inlining/IPA so the call actually reaches the
+ * kprobe-instrumented entry point.
+ */
+noinline int ks_test_vuln(long magic)
+{
+	if (magic == KS_TEST_MAGIC)
+		return -EBADMSG;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ks_test_vuln);
+
+static struct dentry *ks_test_dir;
+
+static ssize_t ks_test_fire_write(struct file *file, const char __user *ubuf,
+				  size_t count, loff_t *ppos)
+{
+	char buf[32];
+	long magic;
+	int ret;
+
+	if (count == 0 || count >= sizeof(buf))
+		return -EINVAL;
+	if (copy_from_user(buf, ubuf, count))
+		return -EFAULT;
+	buf[count] = '\0';
+
+	ret = kstrtol(strim(buf), 0, &magic);
+	if (ret)
+		return ret;
+
+	ret = ks_test_vuln(magic);
+	return ret ? ret : count;
+}
+
+static const struct file_operations ks_test_fire_fops = {
+	.write	= ks_test_fire_write,
+	.open	= simple_open,
+	.llseek	= noop_llseek,
+};
+
+static int __init test_killswitch_init(void)
+{
+	ks_test_dir = debugfs_create_dir("test_killswitch", NULL);
+	debugfs_create_file("fire", 0200, ks_test_dir, NULL,
+			    &ks_test_fire_fops);
+	pr_info("test_killswitch: loaded (magic=0x%lx)\n", KS_TEST_MAGIC);
+	return 0;
+}
+module_init(test_killswitch_init);
+
+static void __exit test_killswitch_exit(void)
+{
+	debugfs_remove_recursive(ks_test_dir);
+}
+module_exit(test_killswitch_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Deliberately-vulnerable target for killswitch selftest");
diff --git a/security/security.c b/security/security.c
index 4e999f0236516..bf700abc911a9 100644
--- a/security/security.c
+++ b/security/security.c
@@ -62,6 +62,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
 	[LOCKDOWN_DBG_WRITE_KERNEL] = "use of kgdb/kdb to write kernel RAM",
 	[LOCKDOWN_RTAS_ERROR_INJECTION] = "RTAS error injection",
 	[LOCKDOWN_XEN_USER_ACTIONS] = "Xen guest user action",
+	[LOCKDOWN_KILLSWITCH] = "engaging a killswitch",
 	[LOCKDOWN_INTEGRITY_MAX] = "integrity",
 	[LOCKDOWN_KCORE] = "/proc/kcore access",
 	[LOCKDOWN_KPROBES] = "use of kprobes",
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 6e59b8f63e416..04c3f8c5ff229 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -53,6 +53,7 @@ TARGETS += ipc
 TARGETS += ir
 TARGETS += kcmp
 TARGETS += kexec
+TARGETS += killswitch
 TARGETS += kselftest_harness
 TARGETS += kvm
 TARGETS += landlock
diff --git a/tools/testing/selftests/killswitch/.gitignore b/tools/testing/selftests/killswitch/.gitignore
new file mode 100644
index 0000000000000..cbf204ce18615
--- /dev/null
+++ b/tools/testing/selftests/killswitch/.gitignore
@@ -0,0 +1 @@
+cve_31431_test
diff --git a/tools/testing/selftests/killswitch/Makefile b/tools/testing/selftests/killswitch/Makefile
new file mode 100644
index 0000000000000..daf5d001e66c8
--- /dev/null
+++ b/tools/testing/selftests/killswitch/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+TEST_GEN_PROGS := cve_31431_test
+TEST_PROGS := killswitch_test.sh
+
+CFLAGS += -O2 -g -std=gnu99 -Wall $(KHDR_INCLUDES)
+
+include ../lib.mk
diff --git a/tools/testing/selftests/killswitch/cve_31431_test.c b/tools/testing/selftests/killswitch/cve_31431_test.c
new file mode 100644
index 0000000000000..1ff817c51d881
--- /dev/null
+++ b/tools/testing/selftests/killswitch/cve_31431_test.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AF_ALG AEAD round-trip prober.  The killswitch selftest uses this
+ * to demonstrate that engaging a killswitch on af_alg_sendmsg
+ * neuters AF_ALG operations (sendmsg returns -EPERM), mitigating
+ * any AF_ALG-reachable bug whose exploit primitive runs from the
+ * send path.
+ *
+ * Exit codes:
+ *   0  AEAD round-trip succeeded (function intact)
+ *   1  AEAD round-trip refused (mitigation engaged)
+ *   2  setup error (no AF_ALG, missing aead/gcm(aes), etc.) -> SKIP
+ *
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <linux/if_alg.h>
+
+#define KEY_LEN		16
+#define IV_LEN		12
+#define AAD_LEN		16
+#define PT_LEN		64
+#define TAG_LEN		16
+#define EXPECTED_LEN	(AAD_LEN + PT_LEN + TAG_LEN)
+
+#ifndef AF_ALG
+#define AF_ALG		38
+#endif
+#ifndef SOL_ALG
+#define SOL_ALG		279
+#endif
+
+int main(void)
+{
+	struct sockaddr_alg sa = {
+		.salg_family = AF_ALG,
+		.salg_type   = "aead",
+		.salg_name   = "gcm(aes)",
+	};
+	unsigned char key[KEY_LEN] = { 0 };
+	unsigned char iv[IV_LEN]   = { 0 };
+	unsigned char buf[1024]    = { 0 };
+	struct msghdr msg = { 0 };
+	struct iovec iov;
+	struct cmsghdr *cmsg;
+	struct af_alg_iv *aiv;
+	char cbuf[256] = { 0 };
+	int *p_op, *p_assoclen;
+	int sk, opfd;
+	ssize_t n;
+
+	sk = socket(AF_ALG, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		fprintf(stderr, "AF_ALG socket: %s -- skip\n", strerror(errno));
+		return 2;
+	}
+	if (bind(sk, (struct sockaddr *)&sa, sizeof(sa))) {
+		fprintf(stderr, "bind aead/gcm(aes): %s -- skip\n",
+			strerror(errno));
+		close(sk);
+		return 2;
+	}
+	if (setsockopt(sk, SOL_ALG, ALG_SET_KEY, key, KEY_LEN)) {
+		fprintf(stderr, "ALG_SET_KEY: %s -- skip\n", strerror(errno));
+		close(sk);
+		return 2;
+	}
+	if (setsockopt(sk, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, TAG_LEN)) {
+		fprintf(stderr, "ALG_SET_AEAD_AUTHSIZE: %s -- skip\n",
+			strerror(errno));
+		close(sk);
+		return 2;
+	}
+
+	opfd = accept(sk, NULL, 0);
+	if (opfd < 0) {
+		fprintf(stderr, "accept: %s -- skip\n", strerror(errno));
+		close(sk);
+		return 2;
+	}
+
+	/* control message: ENCRYPT op + IV + assoclen */
+	msg.msg_control    = cbuf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(int))
+			   + CMSG_SPACE(sizeof(*aiv) + IV_LEN)
+			   + CMSG_SPACE(sizeof(int));
+
+	cmsg = CMSG_FIRSTHDR(&msg);
+	cmsg->cmsg_level = SOL_ALG;
+	cmsg->cmsg_type  = ALG_SET_OP;
+	cmsg->cmsg_len   = CMSG_LEN(sizeof(int));
+	p_op = (int *)CMSG_DATA(cmsg);
+	*p_op = ALG_OP_ENCRYPT;
+
+	cmsg = CMSG_NXTHDR(&msg, cmsg);
+	cmsg->cmsg_level = SOL_ALG;
+	cmsg->cmsg_type  = ALG_SET_IV;
+	cmsg->cmsg_len   = CMSG_LEN(sizeof(*aiv) + IV_LEN);
+	aiv = (struct af_alg_iv *)CMSG_DATA(cmsg);
+	aiv->ivlen = IV_LEN;
+	memcpy(aiv->iv, iv, IV_LEN);
+
+	cmsg = CMSG_NXTHDR(&msg, cmsg);
+	cmsg->cmsg_level = SOL_ALG;
+	cmsg->cmsg_type  = ALG_SET_AEAD_ASSOCLEN;
+	cmsg->cmsg_len   = CMSG_LEN(sizeof(int));
+	p_assoclen = (int *)CMSG_DATA(cmsg);
+	*p_assoclen = AAD_LEN;
+
+	/* AAD || plaintext */
+	memset(buf, 0xaa, AAD_LEN);
+	memset(buf + AAD_LEN, 0x55, PT_LEN);
+	iov.iov_base = buf;
+	iov.iov_len  = AAD_LEN + PT_LEN;
+	msg.msg_iov    = &iov;
+	msg.msg_iovlen = 1;
+
+	n = sendmsg(opfd, &msg, 0);
+	if (n < 0) {
+		/*
+		 * sendmsg refused: this is exactly the killswitch
+		 * af_alg_sendmsg=-EPERM mitigation outcome.  Distinct
+		 * exit code from setup failure so the test script can
+		 * tell them apart.
+		 */
+		fprintf(stderr, "sendmsg: %s -- mitigation engaged?\n",
+			strerror(errno));
+		close(opfd); close(sk);
+		return 1;
+	}
+
+	/* recv: AAD echoed, plus ciphertext + tag */
+	memset(buf, 0, sizeof(buf));
+	n = read(opfd, buf, EXPECTED_LEN);
+	close(opfd); close(sk);
+
+	if (n == 0) {
+		printf("AEAD returned 0 bytes -- killswitch mitigation engaged\n");
+		return 1;
+	}
+	if (n != EXPECTED_LEN) {
+		fprintf(stderr,
+			"AEAD short read: got %zd, expected %d -- mitigated?\n",
+			n, EXPECTED_LEN);
+		return 1;
+	}
+
+	/* sanity: ciphertext (after AAD) shouldn't equal the plaintext bytes */
+	if (memcmp(buf + AAD_LEN, buf + AAD_LEN + 1, PT_LEN - 1) == 0) {
+		fprintf(stderr, "AEAD output looks unencrypted\n");
+		return 2;
+	}
+
+	printf("AEAD round-trip OK (%zd bytes)\n", n);
+	return 0;
+}
diff --git a/tools/testing/selftests/killswitch/killswitch_test.sh b/tools/testing/selftests/killswitch/killswitch_test.sh
new file mode 100755
index 0000000000000..7bfb821ce437f
--- /dev/null
+++ b/tools/testing/selftests/killswitch/killswitch_test.sh
@@ -0,0 +1,147 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# End-to-end killswitch selftest.  Drives the test_killswitch module
+# through an engage/disengage cycle and confirms each transition
+# behaves as expected.  Also runs the AF_ALG mitigation proof.
+#
+# Requirements (see Documentation/admin-guide/killswitch.rst):
+#   - CONFIG_KILLSWITCH=y
+#   - CONFIG_TEST_KILLSWITCH=m
+#   - run as root (CAP_SYS_ADMIN)
+#
+# Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+#
+
+set -u
+
+KS=/sys/kernel/security/killswitch
+TRIG=/sys/kernel/debug/test_killswitch/fire
+
+NOMOD=0
+SKIP_RC=4
+N=0
+FAIL=0
+
+ksft_pass() { N=$((N+1));    echo "ok $N - $*"; }
+ksft_fail() { N=$((N+1)); FAIL=$((FAIL+1)); echo "not ok $N - $*"; }
+ksft_skip() { echo "ok 1 - SKIP $*"; echo "1..1"; exit $SKIP_RC; }
+
+[[ $EUID -eq 0 ]] || ksft_skip "must be root"
+[[ -d $KS    ]] || ksft_skip "$KS not present (CONFIG_KILLSWITCH disabled?)"
+
+if ! modprobe test_killswitch 2>/dev/null; then
+	NOMOD=1
+fi
+[[ -e $TRIG ]] || ksft_skip "$TRIG missing (test_killswitch.ko not installed?)"
+
+cleanup() {
+	echo "disengage_all" > $KS/control 2>/dev/null || true
+	[[ $NOMOD -eq 0 ]] && rmmod test_killswitch 2>/dev/null || true
+}
+trap cleanup EXIT
+
+# --- pre-engage: bad path runs, write fails with EBADMSG ---
+if echo 0xC0FFEE > $TRIG 2>/dev/null; then
+	ksft_fail "pre-engage: write should have failed (-EBADMSG)"
+else
+	[[ $? -ne 0 ]] && ksft_pass "pre-engage: bad path returns error" \
+	             || ksft_fail "pre-engage: unexpected outcome"
+fi
+
+# --- engage ---
+echo "engage ks_test_vuln 0 ksft" > $KS/control
+grep -q "^ks_test_vuln" $KS/engaged \
+	&& ksft_pass "engage: ks_test_vuln in engaged list" \
+	|| ksft_fail "engage: missing from engaged list"
+
+[[ $(cat $KS/taint) == 1 ]] \
+	&& ksft_pass "engage: taint set" \
+	|| ksft_fail "engage: taint not set"
+
+[[ -d $KS/fn/ks_test_vuln ]] \
+	&& ksft_pass "engage: per-fn dir created" \
+	|| ksft_fail "engage: per-fn dir missing"
+
+# --- post-engage: BUG suppressed; write returns successfully ---
+if echo 0xC0FFEE > $TRIG 2>/dev/null; then
+	ksft_pass "post-engage: BUG suppressed, write succeeded"
+else
+	ksft_fail "post-engage: write should succeed"
+fi
+
+[[ $(cat $KS/fn/ks_test_vuln/hits) -ge 1 ]] \
+	&& ksft_pass "post-engage: hits counter incremented" \
+	|| ksft_fail "post-engage: hits counter did not move"
+
+# --- retval rewrite is a plain write (no validation) ---
+echo 7 > $KS/fn/ks_test_vuln/retval
+[[ $(cat $KS/fn/ks_test_vuln/retval) == 7 ]] \
+	&& ksft_pass "retval rewrite round-trips" \
+	|| ksft_fail "retval rewrite failed"
+
+# --- engage on a kprobe-rejected function fails ---
+# warn_thunk_thunk is in /sys/kernel/debug/kprobes/blacklist;
+# register_kprobe() refuses it.
+KP_REJECT=warn_thunk_thunk
+if echo "engage $KP_REJECT 0 ksft" > $KS/control 2>/dev/null; then
+	ksft_fail "register_kprobe should have rejected $KP_REJECT"
+	echo "disengage $KP_REJECT" > $KS/control
+else
+	ksft_pass "register_kprobe refuses blacklisted target"
+fi
+
+# --- disengage ---
+echo "disengage ks_test_vuln" > $KS/control
+[[ -z "$(cat $KS/engaged)" ]] \
+	&& ksft_pass "disengage: engaged list empty" \
+	|| ksft_fail "disengage: engaged list not empty"
+
+[[ ! -d $KS/fn/ks_test_vuln ]] \
+	&& ksft_pass "disengage: per-fn dir removed" \
+	|| ksft_fail "disengage: per-fn dir still present"
+
+[[ $(cat $KS/taint) == 1 ]] \
+	&& ksft_pass "disengage: taint persists" \
+	|| ksft_fail "disengage: taint should persist"
+
+# --- post-disengage: bad path active again ---
+if echo 0xC0FFEE > $TRIG 2>/dev/null; then
+	ksft_fail "post-disengage: write should fail again"
+else
+	ksft_pass "post-disengage: bad path active again"
+fi
+
+# ---- CVE-2026-31431 mitigation proof (AF_ALG aead via af_alg_sendmsg) ----
+# Skip the whole block if AF_ALG / AEAD machinery isn't compiled in.
+if [[ -x $(dirname "$0")/cve_31431_test ]]; then
+	CVE=$(dirname "$0")/cve_31431_test
+	$CVE >/dev/null 2>&1 && PRE=$? || PRE=$?
+	if [[ $PRE -eq 0 ]]; then
+		ksft_pass "cve-31431: pre-engage AEAD round-trip OK"
+
+		echo "engage af_alg_sendmsg -1 CVE-2026-31431" > $KS/control
+		$CVE >/dev/null 2>&1 && POST=$? || POST=$?
+		if [[ $POST -eq 1 ]]; then
+			ksft_pass "cve-31431: post-engage AEAD refused (mitigated)"
+		else
+			ksft_fail "cve-31431: post-engage exit=$POST (expected 1)"
+		fi
+
+		HITS=$(cat $KS/fn/af_alg_sendmsg/hits 2>/dev/null || echo 0)
+		[[ $HITS -ge 1 ]] && ksft_pass "cve-31431: hits=$HITS recorded" \
+			|| ksft_fail "cve-31431: hits not recorded"
+
+		echo "disengage af_alg_sendmsg" > $KS/control
+		$CVE >/dev/null 2>&1 && POST2=$? || POST2=$?
+		[[ $POST2 -eq 0 ]] && ksft_pass "cve-31431: post-disengage restored" \
+			|| ksft_fail "cve-31431: post-disengage exit=$POST2"
+	elif [[ $PRE -eq 2 ]]; then
+		echo "# SKIP cve-31431 (AF_ALG/AEAD not available)"
+	else
+		ksft_fail "cve-31431: pre-engage exit=$PRE"
+	fi
+fi
+
+echo "1..$N"
+exit $((FAIL > 0))
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH 0/5] mm: support zswap-backed anonymous large folio swapin
From: fujunjie @ 2026-05-08 20:18 UTC (permalink / raw)
  To: Andrew Morton, Chris Li, Kairui Song, Johannes Weiner, Nhat Pham,
	Yosry Ahmed
  Cc: linux-mm, linux-kernel, linux-doc, Jonathan Corbet,
	David Hildenbrand, Ryan Roberts, Barry Song, Baolin Wang,
	Chengming Zhou, Baoquan He, Lorenzo Stoakes

Hi,

This RFC explores anonymous large folio swapin when a contiguous swap
range is backed consistently by zswap.

Large folio swapout to zswap is already supported by storing each base
page in the folio as a separate zswap entry. The anonymous synchronous
swapin path has remained order-0 once zswap has ever been enabled:
zswap_load() rejected large folios, and alloc_swap_folio() avoided large
folio allocation to protect against mixed backend ranges.

This RFC keeps the scope intentionally conservative. It does not try to
read one large folio from mixed zswap and disk backends, and it does not
change shmem swapin. Shmem still has its existing zswap fallback and is
left for later discussion. For anonymous swapin, the backend rule is made
explicit:

- a range fully absent from zswap can keep using the disk backend
- a range fully present in zswap can be decompressed into a large folio
- a mixed zswap/non-zswap range falls back to order-0 swapin

The series adds a zswap range query helper, teaches zswap_load() to
decompress all-zswap large folios one base page at a time, accounts mTHP
swpin for zswap-loaded large folios, retries synchronous large-folio
insertion races with order-0 swapin, and removes the anonymous
zswap-never-enabled restriction once mixed ranges are filtered.

I tested the series with a full bzImage build using CONFIG_ZSWAP=y,
CONFIG_ZRAM=y, CONFIG_MEMCG=y and CONFIG_THP_SWAP=y.

The QEMU/KVM runs covered both the fully-zswap path and the mixed-backend
fallback path. In the all-zswap run, a 512MiB anonymous mapping was faulted
as 8192 64KiB groups, reclaimed into zswap, and faulted back. Reclaim
reported mthp64_zswpout=8192 and zswpout=131072. Refault then reported
mthp64_swpin=8192 and zswpin=131072, and pagemap/kpageflags showed 8192
order-4 THP groups in the mapping.

In the mixed-backend run, the workload used a 64MiB anonymous mapping
split into 1024 64KiB groups. After shrinker debugfs wrote back exactly
one zswap base-page entry, refault left 1023 order-4 THP groups and one
order-0 mixed group. The kernel stats matched that shape:
mthp64_swpin=1023, zswpin=16383 and zswpwb=1.

CONFIG_SHRINKER_DEBUG is only a test aid for making that one zswap
writeback deterministic; it is not required by the implementation.

Nhat Pham's active Virtual Swap Space series is adjacent work. It moves
swap cache and zswap entry state into a virtual swap descriptor, and lists
mixed backing THP swapin as a future use case. This RFC is independent and
works with the current swap/zswap infrastructure, but may need rebasing if
VSS lands first.

Feedback would be especially helpful on:

1. whether it makes sense to support all-zswap large folio swapin first,
   while keeping mixed zswap/disk ranges on the order-0 fallback path
2. whether a follow-up for mixed zswap/disk large folio swapin would be
   useful after this RFC

Thanks.

---

fujunjie (5):
  mm: zswap: decompress into a folio subpage
  mm: zswap: add a zswap entry batch helper
  mm: zswap: load fully stored large folios
  mm: swap: fall back to order-0 after large swapin races
  mm: swap: allow zswap-backed large folio swapin

 Documentation/admin-guide/mm/transhuge.rst |   4 +-
 include/linux/zswap.h                      |   9 ++
 mm/memory.c                                |  67 ++++++++-----
 mm/swap_state.c                            |  23 +++--
 mm/zswap.c                                 | 111 ++++++++++++++++-----
 5 files changed, 154 insertions(+), 60 deletions(-)


base-commit: 917719c412c48687d4a176965d1fa35320ec457c
-- 
2.34.1


^ permalink raw reply

* [RFC PATCH 1/5] mm: zswap: decompress into a folio subpage
From: fujunjie @ 2026-05-08 20:20 UTC (permalink / raw)
  To: Andrew Morton, Chris Li, Kairui Song, Johannes Weiner, Nhat Pham,
	Yosry Ahmed
  Cc: linux-mm, linux-kernel, linux-doc, Jonathan Corbet,
	David Hildenbrand, Ryan Roberts, Barry Song, Baolin Wang,
	Chengming Zhou, Baoquan He, Lorenzo Stoakes
In-Reply-To: <tencent_8B437BE4F586C162950BF71954316C1EDB05@qq.com>

zswap_decompress() always writes to offset 0 of the target folio. That
is sufficient while zswap only loads order-0 folios, but large folio
swapin needs to fill each base page from its own zswap entry.

Pass the base-page index to zswap_decompress() and use it for the kmap
and scatterlist output offsets. Existing callers pass index 0, so this
is a preparatory change with no intended behavior change.

Signed-off-by: fujunjie <fujunjie1@qq.com>
---
 mm/zswap.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/mm/zswap.c b/mm/zswap.c
index 4b5149173b0e..afe38dfc5a29 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -921,12 +921,14 @@ static bool zswap_compress(struct page *page, struct zswap_entry *entry,
 	return comp_ret == 0 && alloc_ret == 0;
 }
 
-static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio)
+static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio,
+			     unsigned long index)
 {
 	struct zswap_pool *pool = entry->pool;
 	struct scatterlist input[2]; /* zsmalloc returns an SG list 1-2 entries */
 	struct scatterlist output;
 	struct crypto_acomp_ctx *acomp_ctx;
+	size_t offset = index * PAGE_SIZE;
 	int ret = 0, dlen;
 
 	acomp_ctx = raw_cpu_ptr(pool->acomp_ctx);
@@ -939,14 +941,14 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio)
 
 		WARN_ON_ONCE(input->length != PAGE_SIZE);
 
-		dst = kmap_local_folio(folio, 0);
+		dst = kmap_local_folio(folio, offset);
 		memcpy_from_sglist(dst, input, 0, PAGE_SIZE);
 		dlen = PAGE_SIZE;
 		kunmap_local(dst);
 		flush_dcache_folio(folio);
 	} else {
 		sg_init_table(&output, 1);
-		sg_set_folio(&output, folio, PAGE_SIZE, 0);
+		sg_set_folio(&output, folio, PAGE_SIZE, offset);
 		acomp_request_set_params(acomp_ctx->req, input, &output,
 					 entry->length, PAGE_SIZE);
 		ret = crypto_acomp_decompress(acomp_ctx->req);
@@ -1034,7 +1036,7 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
 		goto out;
 	}
 
-	if (!zswap_decompress(entry, folio)) {
+	if (!zswap_decompress(entry, folio, 0)) {
 		ret = -EIO;
 		goto out;
 	}
@@ -1611,7 +1613,7 @@ int zswap_load(struct folio *folio)
 	if (!entry)
 		return -ENOENT;
 
-	if (!zswap_decompress(entry, folio)) {
+	if (!zswap_decompress(entry, folio, 0)) {
 		folio_unlock(folio);
 		return -EIO;
 	}
-- 
2.34.1


^ permalink raw reply related

* [RFC PATCH 4/5] mm: swap: fall back to order-0 after large swapin races
From: fujunjie @ 2026-05-08 20:20 UTC (permalink / raw)
  To: Andrew Morton, Chris Li, Kairui Song, Johannes Weiner, Nhat Pham,
	Yosry Ahmed
  Cc: linux-mm, linux-kernel, linux-doc, Jonathan Corbet,
	David Hildenbrand, Ryan Roberts, Barry Song, Baolin Wang,
	Chengming Zhou, Baoquan He, Lorenzo Stoakes
In-Reply-To: <tencent_8B437BE4F586C162950BF71954316C1EDB05@qq.com>

swapin_folio() documents that a large folio insertion race returns NULL
so the caller can fall back to order-0 swapin. do_swap_page() currently
turns that NULL into VM_FAULT_OOM if the PTE is unchanged, which is
harsher than necessary and gets in the way of rejecting large folio
ranges for backend reasons.

Move the synchronous swapin sequence into a helper and retry with an
order-0 folio when a large folio cannot be inserted into the swap cache.
Count the event as an mTHP swapin fallback before dropping the failed
large allocation.

Signed-off-by: fujunjie <fujunjie1@qq.com>
---
 mm/memory.c | 50 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 39 insertions(+), 11 deletions(-)

diff --git a/mm/memory.c b/mm/memory.c
index ea6568571131..84e3b77b8293 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -4757,6 +4757,44 @@ static struct folio *alloc_swap_folio(struct vm_fault *vmf)
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
+static struct folio *swapin_synchronous_folio(swp_entry_t entry,
+					      struct vm_fault *vmf)
+{
+	struct folio *swapcache, *folio;
+	bool large;
+	int order;
+
+	folio = alloc_swap_folio(vmf);
+	if (!folio)
+		return NULL;
+
+	large = folio_test_large(folio);
+	order = folio_order(folio);
+
+	/*
+	 * folio is charged, so swapin can only fail due to raced swapin and
+	 * return NULL.
+	 */
+	swapcache = swapin_folio(entry, folio);
+	if (swapcache == folio)
+		return folio;
+
+	if (!swapcache && large)
+		count_mthp_stat(order, MTHP_STAT_SWPIN_FALLBACK);
+	folio_put(folio);
+	if (swapcache || !large)
+		return swapcache;
+
+	folio = __alloc_swap_folio(vmf);
+	if (!folio)
+		return NULL;
+
+	swapcache = swapin_folio(entry, folio);
+	if (swapcache != folio)
+		folio_put(folio);
+	return swapcache;
+}
+
 /* Sanity check that a folio is fully exclusive */
 static void check_swap_exclusive(struct folio *folio, swp_entry_t entry,
 				 unsigned int nr_pages)
@@ -4860,17 +4898,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
 		swap_update_readahead(folio, vma, vmf->address);
 	if (!folio) {
 		if (data_race(si->flags & SWP_SYNCHRONOUS_IO)) {
-			folio = alloc_swap_folio(vmf);
-			if (folio) {
-				/*
-				 * folio is charged, so swapin can only fail due
-				 * to raced swapin and return NULL.
-				 */
-				swapcache = swapin_folio(entry, folio);
-				if (swapcache != folio)
-					folio_put(folio);
-				folio = swapcache;
-			}
+			folio = swapin_synchronous_folio(entry, vmf);
 		} else {
 			folio = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE, vmf);
 		}
-- 
2.34.1


^ permalink raw reply related

* [RFC PATCH 5/5] mm: swap: allow zswap-backed large folio swapin
From: fujunjie @ 2026-05-08 20:20 UTC (permalink / raw)
  To: Andrew Morton, Chris Li, Kairui Song, Johannes Weiner, Nhat Pham,
	Yosry Ahmed
  Cc: linux-mm, linux-kernel, linux-doc, Jonathan Corbet,
	David Hildenbrand, Ryan Roberts, Barry Song, Baolin Wang,
	Chengming Zhou, Baoquan He, Lorenzo Stoakes
In-Reply-To: <tencent_8B437BE4F586C162950BF71954316C1EDB05@qq.com>

alloc_swap_folio() has been falling back to order-0 in the anonymous
synchronous swapin path whenever zswap was ever enabled, because a large
folio range could contain a mixture of zswap and non-zswap entries and
zswap_load() could not handle large folios.

zswap_load() can now load a range that is fully present in zswap, and
zswap_entry_batch() can identify mixed zswap ranges. Use that check
alongside the existing zeromap and swapcache checks when selecting a large
folio for anonymous swapin, and recheck before inserting a large folio into
the swap cache while holding the swap cluster lock.

With mixed zswap ranges rejected and the insertion-race fallback in place,
remove the blanket zswap_never_enabled() fallback from the anonymous swapin
path so all-zswap and all-disk anonymous ranges can use mTHP swapin. Shmem
keeps its existing zswap fallback and is outside this RFC.

Signed-off-by: fujunjie <fujunjie1@qq.com>
---
 mm/memory.c     | 21 ++++++---------------
 mm/swap_state.c | 23 +++++++++++++++--------
 2 files changed, 21 insertions(+), 23 deletions(-)

diff --git a/mm/memory.c b/mm/memory.c
index 84e3b77b8293..0be249108de1 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -78,6 +78,7 @@
 #include <linux/sched/sysctl.h>
 #include <linux/pgalloc.h>
 #include <linux/uaccess.h>
+#include <linux/zswap.h>
 
 #include <trace/events/kmem.h>
 
@@ -4635,13 +4636,11 @@ static bool can_swapin_thp(struct vm_fault *vmf, pte_t *ptep, int nr_pages)
 	if (swap_pte_batch(ptep, nr_pages, pte) != nr_pages)
 		return false;
 
-	/*
-	 * swap_read_folio() can't handle the case a large folio is hybridly
-	 * from different backends. And they are likely corner cases. Similar
-	 * things might be added once zswap support large folios.
-	 */
+	/* swap_read_folio() can't handle hybrid backend large folios. */
 	if (unlikely(swap_zeromap_batch(entry, nr_pages, NULL) != nr_pages))
 		return false;
+	if (unlikely(zswap_entry_batch(entry, nr_pages, NULL) != nr_pages))
+		return false;
 	if (unlikely(non_swapcache_batch(entry, nr_pages) != nr_pages))
 		return false;
 
@@ -4690,14 +4689,6 @@ static struct folio *alloc_swap_folio(struct vm_fault *vmf)
 	if (unlikely(userfaultfd_armed(vma)))
 		goto fallback;
 
-	/*
-	 * A large swapped out folio could be partially or fully in zswap. We
-	 * lack handling for such cases, so fallback to swapping in order-0
-	 * folio.
-	 */
-	if (!zswap_never_enabled())
-		goto fallback;
-
 	entry = softleaf_from_pte(vmf->orig_pte);
 	/*
 	 * Get a list of all the (large) orders below PMD_ORDER that are enabled
@@ -4772,8 +4763,8 @@ static struct folio *swapin_synchronous_folio(swp_entry_t entry,
 	order = folio_order(folio);
 
 	/*
-	 * folio is charged, so swapin can only fail due to raced swapin and
-	 * return NULL.
+	 * folio is charged, so NULL means the large folio could not be
+	 * inserted and needs order-0 fallback.
 	 */
 	swapcache = swapin_folio(entry, folio);
 	if (swapcache == folio)
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 1415a5c54a43..4e58fad5e5f0 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -22,6 +22,7 @@
 #include <linux/vmalloc.h>
 #include <linux/huge_mm.h>
 #include <linux/shmem_fs.h>
+#include <linux/zswap.h>
 #include "internal.h"
 #include "swap_table.h"
 #include "swap.h"
@@ -207,6 +208,11 @@ static int swap_cache_add_folio(struct folio *folio, swp_entry_t entry,
 		if (swp_tb_is_shadow(old_tb))
 			shadow = swp_tb_to_shadow(old_tb);
 	} while (++ci_off < ci_end);
+	if (unlikely(folio_test_large(folio) &&
+		     zswap_entry_batch(entry, nr_pages, NULL) != nr_pages)) {
+		err = -EAGAIN;
+		goto failed;
+	}
 	__swap_cache_add_folio(ci, folio, entry);
 	swap_cluster_unlock(ci);
 	if (shadowp)
@@ -460,7 +466,8 @@ void swap_update_readahead(struct folio *folio, struct vm_area_struct *vma,
  *
  * Context: Caller must protect the swap device with reference count or locks.
  * Return: Returns the folio being added on success. Returns the existing folio
- * if @entry is already cached. Returns NULL if raced with swapin or swapoff.
+ * if @entry is already cached. Returns NULL if raced with swapin or swapoff,
+ * or if a large folio fails a backend recheck before insertion.
  */
 static struct folio *__swap_cache_prepare_and_add(swp_entry_t entry,
 						  struct folio *folio,
@@ -483,10 +490,10 @@ static struct folio *__swap_cache_prepare_and_add(swp_entry_t entry,
 
 		/*
 		 * Large order allocation needs special handling on
-		 * race: if a smaller folio exists in cache, swapin needs
-		 * to fallback to order 0, and doing a swap cache lookup
-		 * might return a folio that is irrelevant to the faulting
-		 * entry because @entry is aligned down. Just return NULL.
+		 * race or backend recheck failure: swapin needs to fall back
+		 * to order 0, and doing a swap cache lookup might return a
+		 * folio that is irrelevant to the faulting entry because
+		 * @entry is aligned down. Just return NULL.
 		 */
 		if (ret != -EEXIST || folio_test_large(folio))
 			goto failed;
@@ -567,9 +574,9 @@ struct folio *swap_cache_alloc_folio(swp_entry_t entry, gfp_t gfp_mask,
  * with the folio size.
  *
  * Return: returns pointer to @folio on success. If folio is a large folio
- * and this raced with another swapin, NULL will be returned to allow fallback
- * to order 0. Else, if another folio was already added to the swap cache,
- * return that swap cache folio instead.
+ * and it raced with another swapin or failed a backend recheck, NULL will be
+ * returned to allow fallback to order 0. Else, if another folio was already
+ * added to the swap cache, return that swap cache folio instead.
  */
 struct folio *swapin_folio(swp_entry_t entry, struct folio *folio)
 {
-- 
2.34.1


^ permalink raw reply related

* [RFC PATCH 2/5] mm: zswap: add a zswap entry batch helper
From: fujunjie @ 2026-05-08 20:20 UTC (permalink / raw)
  To: Andrew Morton, Chris Li, Kairui Song, Johannes Weiner, Nhat Pham,
	Yosry Ahmed
  Cc: linux-mm, linux-kernel, linux-doc, Jonathan Corbet,
	David Hildenbrand, Ryan Roberts, Barry Song, Baolin Wang,
	Chengming Zhou, Baoquan He, Lorenzo Stoakes
In-Reply-To: <tencent_8B437BE4F586C162950BF71954316C1EDB05@qq.com>

Large folio swapin has to know whether a contiguous swap range is backed
consistently by zswap. A range that is partly in zswap and partly on
disk cannot be read by the existing whole-folio swap_read_folio()
backend selection.

Add zswap_entry_batch(), mirroring the existing zeromap batch query: it
returns how many entries starting at a swap entry share the same zswap
presence, and optionally reports the first entry's state. The
CONFIG_ZSWAP=n stub reports that the whole range is not in zswap.

Signed-off-by: fujunjie <fujunjie1@qq.com>
---
 include/linux/zswap.h |  9 +++++++++
 mm/zswap.c            | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/include/linux/zswap.h b/include/linux/zswap.h
index 30c193a1207e..b9d71f027200 100644
--- a/include/linux/zswap.h
+++ b/include/linux/zswap.h
@@ -27,6 +27,7 @@ struct zswap_lruvec_state {
 unsigned long zswap_total_pages(void);
 bool zswap_store(struct folio *folio);
 int zswap_load(struct folio *folio);
+int zswap_entry_batch(swp_entry_t swp, int max_nr, bool *is_zswap);
 void zswap_invalidate(swp_entry_t swp);
 int zswap_swapon(int type, unsigned long nr_pages);
 void zswap_swapoff(int type);
@@ -49,6 +50,14 @@ static inline int zswap_load(struct folio *folio)
 	return -ENOENT;
 }
 
+static inline int zswap_entry_batch(swp_entry_t swp, int max_nr,
+				    bool *is_zswap)
+{
+	if (is_zswap)
+		*is_zswap = false;
+	return max_nr;
+}
+
 static inline void zswap_invalidate(swp_entry_t swp) {}
 static inline int zswap_swapon(int type, unsigned long nr_pages)
 {
diff --git a/mm/zswap.c b/mm/zswap.c
index afe38dfc5a29..27c14b8edd15 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -234,6 +234,42 @@ static inline struct xarray *swap_zswap_tree(swp_entry_t swp)
 		>> ZSWAP_ADDRESS_SPACE_SHIFT];
 }
 
+/*
+ * Return the number of contiguous swap entries that share the same zswap
+ * presence as @swp. If @is_zswap is not NULL, return @swp's zswap status.
+ *
+ * Context: callers must keep the swap type alive. The result is a snapshot
+ * of zswap xarray presence; callers must tolerate races by rechecking under
+ * the lock that matters for their operation or by falling back safely.
+ */
+int zswap_entry_batch(swp_entry_t swp, int max_nr, bool *is_zswap)
+{
+	pgoff_t offset = swp_offset(swp);
+	bool first;
+	int i;
+
+	if (zswap_never_enabled()) {
+		if (is_zswap)
+			*is_zswap = false;
+		return max_nr;
+	}
+
+	first = !!xa_load(swap_zswap_tree(swp), offset);
+	if (is_zswap)
+		*is_zswap = first;
+
+	for (i = 1; i < max_nr; i++) {
+		swp_entry_t entry = swp_entry(swp_type(swp), offset + i);
+		bool present;
+
+		present = !!xa_load(swap_zswap_tree(entry), offset + i);
+		if (present != first)
+			return i;
+	}
+
+	return max_nr;
+}
+
 #define zswap_pool_debug(msg, p)			\
 	pr_debug("%s pool %s\n", msg, (p)->tfm_name)
 
-- 
2.34.1


^ permalink raw reply related

* [RFC PATCH 3/5] mm: zswap: load fully stored large folios
From: fujunjie @ 2026-05-08 20:20 UTC (permalink / raw)
  To: Andrew Morton, Chris Li, Kairui Song, Johannes Weiner, Nhat Pham,
	Yosry Ahmed
  Cc: linux-mm, linux-kernel, linux-doc, Jonathan Corbet,
	David Hildenbrand, Ryan Roberts, Barry Song, Baolin Wang,
	Chengming Zhou, Baoquan He, Lorenzo Stoakes
In-Reply-To: <tencent_8B437BE4F586C162950BF71954316C1EDB05@qq.com>

zswap_store() already stores every base page of a large folio as a
separate zswap entry and tears the whole folio back down on store
failure. The load side still rejects any large folio, which forces the
swapin path to avoid mTHP swapin once zswap has ever been enabled.

Use zswap_entry_batch() to distinguish three cases: the whole range is
absent from zswap and should fall through to the disk backend, the whole
range is present and can be decompressed one base page at a time, or the
range is mixed and must be treated as an invalid large-folio backend
selection.

After all entries decompress successfully, mark the folio uptodate and
dirty, account the mTHP swpin stat once for the folio, account one ZSWPIN
event per base page, and invalidate each zswap entry because the
swapcache folio becomes authoritative.

Signed-off-by: fujunjie <fujunjie1@qq.com>
---
 Documentation/admin-guide/mm/transhuge.rst |  4 +-
 mm/zswap.c                                 | 65 ++++++++++++++--------
 2 files changed, 45 insertions(+), 24 deletions(-)

diff --git a/Documentation/admin-guide/mm/transhuge.rst b/Documentation/admin-guide/mm/transhuge.rst
index 5fbc3d89bb07..05456906aff6 100644
--- a/Documentation/admin-guide/mm/transhuge.rst
+++ b/Documentation/admin-guide/mm/transhuge.rst
@@ -644,8 +644,8 @@ zswpout
 	piece without splitting.
 
 swpin
-	is incremented every time a huge page is swapped in from a non-zswap
-	swap device in one piece.
+	is incremented every time a huge page is swapped in from swap I/O or
+	zswap in one piece.
 
 swpin_fallback
 	is incremented if swapin fails to allocate or charge a huge page
diff --git a/mm/zswap.c b/mm/zswap.c
index 27c14b8edd15..863ca1e896ed 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -28,6 +28,7 @@
 #include <crypto/acompress.h>
 #include <crypto/scatterwalk.h>
 #include <linux/zswap.h>
+#include <linux/huge_mm.h>
 #include <linux/mm_types.h>
 #include <linux/page-flags.h>
 #include <linux/swapops.h>
@@ -1614,20 +1615,23 @@ bool zswap_store(struct folio *folio)
  *  NOT marked up-to-date, so that an IO error is emitted (e.g. do_swap_page()
  *  will SIGBUS).
  *
- *  -EINVAL: if the swapped out content was in zswap, but the page belongs
- *  to a large folio, which is not supported by zswap. The folio is unlocked,
- *  but NOT marked up-to-date, so that an IO error is emitted (e.g.
- *  do_swap_page() will SIGBUS).
+ *  -EINVAL: if the folio spans a mix of zswap and non-zswap entries. The
+ *  folio is unlocked, but NOT marked up-to-date, so that an IO error is
+ *  emitted (e.g. do_swap_page() will SIGBUS). Large folio swapin should
+ *  reject such ranges before calling zswap_load().
  *
- *  -ENOENT: if the swapped out content was not in zswap. The folio remains
+ *  -ENOENT: if the swapped out content was not in zswap. For a large folio,
+ *  this means the whole folio range was not in zswap. The folio remains
  *  locked on return.
  */
 int zswap_load(struct folio *folio)
 {
 	swp_entry_t swp = folio->swap;
 	pgoff_t offset = swp_offset(swp);
-	struct xarray *tree = swap_zswap_tree(swp);
 	struct zswap_entry *entry;
+	int nr_pages = folio_nr_pages(folio);
+	bool is_zswap;
+	int index;
 
 	VM_WARN_ON_ONCE(!folio_test_locked(folio));
 	VM_WARN_ON_ONCE(!folio_test_swapcache(folio));
@@ -1635,30 +1639,36 @@ int zswap_load(struct folio *folio)
 	if (zswap_never_enabled())
 		return -ENOENT;
 
-	/*
-	 * Large folios should not be swapped in while zswap is being used, as
-	 * they are not properly handled. Zswap does not properly load large
-	 * folios, and a large folio may only be partially in zswap.
-	 */
-	if (WARN_ON_ONCE(folio_test_large(folio))) {
+	if (zswap_entry_batch(swp, nr_pages, &is_zswap) != nr_pages) {
+		WARN_ON_ONCE(folio_test_large(folio));
 		folio_unlock(folio);
 		return -EINVAL;
 	}
 
-	entry = xa_load(tree, offset);
-	if (!entry)
+	if (!is_zswap)
 		return -ENOENT;
 
-	if (!zswap_decompress(entry, folio, 0)) {
-		folio_unlock(folio);
-		return -EIO;
+	for (index = 0; index < nr_pages; index++) {
+		swp_entry_t entry_swp = swp_entry(swp_type(swp),
+						  offset + index);
+		struct xarray *tree = swap_zswap_tree(entry_swp);
+
+		entry = xa_load(tree, offset + index);
+		if (WARN_ON_ONCE(!entry)) {
+			folio_unlock(folio);
+			return -EINVAL;
+		}
+
+		if (!zswap_decompress(entry, folio, index)) {
+			folio_unlock(folio);
+			return -EIO;
+		}
 	}
 
 	folio_mark_uptodate(folio);
 
-	count_vm_event(ZSWPIN);
-	if (entry->objcg)
-		count_objcg_events(entry->objcg, ZSWPIN, 1);
+	count_mthp_stat(folio_order(folio), MTHP_STAT_SWPIN);
+	count_vm_events(ZSWPIN, nr_pages);
 
 	/*
 	 * We are reading into the swapcache, invalidate zswap entry.
@@ -1668,8 +1678,19 @@ int zswap_load(struct folio *folio)
 	 * compression work.
 	 */
 	folio_mark_dirty(folio);
-	xa_erase(tree, offset);
-	zswap_entry_free(entry);
+
+	for (index = 0; index < nr_pages; index++) {
+		swp_entry_t entry_swp = swp_entry(swp_type(swp),
+						  offset + index);
+		struct xarray *tree = swap_zswap_tree(entry_swp);
+
+		entry = xa_erase(tree, offset + index);
+		if (WARN_ON_ONCE(!entry))
+			continue;
+		if (entry->objcg)
+			count_objcg_events(entry->objcg, ZSWPIN, 1);
+		zswap_entry_free(entry);
+	}
 
 	folio_unlock(folio);
 	return 0;
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH net-next v3 3/8] net: devmem: support TX over NETMEM_TX_NO_DMA devices
From: Jakub Kicinski @ 2026-05-08 20:44 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Bobby Eshleman, Andrew Lunn, David S. Miller, Eric Dumazet,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
	Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
	Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
	Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
	kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan, dw,
	mohsin.bashr, willemb, jiang.kun2, xu.xin16, wang.yaxin, netdev,
	linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
	Stanislav Fomichev, Mina Almasry, Bobby Eshleman
In-Reply-To: <af3593dYeiEeMzC2@devvm7509.cco0.facebook.com>

On Fri, 8 May 2026 08:01:17 -0700 Stanislav Fomichev wrote:
> Since this is a good case, maybe fold it into skb_frags_readable check above?
> 
> 	if (likely(skb_frags_readable() || netmem_tx == NETMEM_TX_NO_DMA))

FWIW I had the same feeling on v2, so probably worth fixing.

^ permalink raw reply

* Re: [PATCH net-next v3 3/8] net: devmem: support TX over NETMEM_TX_NO_DMA devices
From: Jakub Kicinski @ 2026-05-08 20:47 UTC (permalink / raw)
  To: Bobby Eshleman
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi, Yanteng Si,
	Dongliang Mu, Michael Chan, Pavan Chebbi, Joshua Washington,
	Harshitha Ramamurthy, Saeed Mahameed, Tariq Toukan, Mark Bloch,
	Leon Romanovsky, Alexander Duyck, kernel-team, Daniel Borkmann,
	Nikolay Aleksandrov, Shuah Khan, dw, sdf.kernel, mohsin.bashr,
	willemb, jiang.kun2, xu.xin16, wang.yaxin, netdev, linux-doc,
	linux-kernel, linux-rdma, bpf, linux-kselftest,
	Stanislav Fomichev, Mina Almasry, Bobby Eshleman
In-Reply-To: <20260507-tcp-dm-netkit-v3-3-52821445867c@meta.com>

On Thu, 07 May 2026 19:27:48 -0700 Bobby Eshleman wrote:
> +	/* Virtual device (e.g. netkit) the user called bind-tx on. Must be
> +	 * NETMEM_TX_NO_DMA.
> +	 */
> +	struct net_device *vdev;

AI keeps complaining that we don't hold a reference to this dev which 
I think is fine, we're just comparing pointers. Could we maybe make this
a void pointer and mention in the comment that we treat it as "best
effort cookie" (better phrasing welcome).

Or we should wipe these vdev pointers when vdevs disappear, not sure
how hard that'd be (or whether it's worth the extra state).

^ permalink raw reply

* Re: [PATCH v2 2/3] Documentation: security-bugs: explain what is and is not a security bug
From: Shuah Khan @ 2026-05-08 20:52 UTC (permalink / raw)
  To: Willy Tarreau, greg
  Cc: leon, security, Jonathan Corbet, workflows, linux-doc,
	linux-kernel, Greg KH, Shuah Khan
In-Reply-To: <20260503113506.5710-3-w@1wt.eu>

On 5/3/26 05:35, Willy Tarreau wrote:
> The use of automated tools to find bugs in random locations of the kernel
> induces a raise of security reports even if most of them should just be
> reported as regular bugs. This patch is an attempt at drawing a line
> between what qualifies as a security bug and what does not, hoping to
> improve the situation and ease decision on the reporter's side.
> 
> It defers the enumeration to a new file, threat-model.rst, that tries
> to enumerate various classes of issues that are and are not security
> bugs. This should permit to more easily update this file for various
> subsystem-specific rules without having to revisit the security bug
> reporting guide.
> 
> Cc: Greg KH <gregkh@linuxfoundation.org>
> Cc: Leon Romanovsky <leon@kernel.org>
> Suggested-by: Leon Romanovsky <leon@kernel.org>
> Suggested-by: Greg KH <gregkh@linuxfoundation.org>
> Signed-off-by: Willy Tarreau <w@1wt.eu>
> ---
>   Documentation/process/index.rst         |   1 +
>   Documentation/process/security-bugs.rst |  28 +++
>   Documentation/process/threat-model.rst  | 231 ++++++++++++++++++++++++
>   3 files changed, 260 insertions(+)
>   create mode 100644 Documentation/process/threat-model.rst
> 
> diff --git a/Documentation/process/index.rst b/Documentation/process/index.rst
> index dbd6ea16aca70..aa7c959a52b87 100644
> --- a/Documentation/process/index.rst
> +++ b/Documentation/process/index.rst
> @@ -86,6 +86,7 @@ regressions and security problems.
>      debugging/index
>      handling-regressions
>      security-bugs
> +   threat-model
>      cve
>      embargoed-hardware-issues
>   
> diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst
> index 6dc525858125e..3b44464dd9ba7 100644
> --- a/Documentation/process/security-bugs.rst
> +++ b/Documentation/process/security-bugs.rst
> @@ -66,6 +66,34 @@ In addition, the following information are highly desirable:
>       the issue appear. It is useful to share them, as they can be helpful to
>       keep end users protected during the time it takes them to apply the fix.
>   
> +What qualifies as a security bug
> +--------------------------------
> +
> +It is important that most bugs are handled publicly so as to involve the widest
> +possible audience and find the best solution.  By nature, bugs that are handled
> +in closed discussions between a small set of participants are less likely to
> +produce the best possible fix (e.g., risk of missing valid use cases, limited
> +testing abilities).
> +
> +It turns out that the majority of the bugs reported via the security team are
> +just regular bugs that have been improperly qualified as security bugs due to
> +ignorance or misunderstanding of the Linux kernel's threat model described in

"lack of understanding" instead of ignorance?

> +Documentation/process/threat-model.rst, and ought to have been sent through
> +the normal channels described in Documentation/admin-guide/reporting-issues.rst
> +instead.
> +
> +The security list exists for urgent bugs that grant an attacker a capability
> +they are not supposed to have on a correctly configured production system, and
> +can be easily exploited, representing an imminent threat to many users.  Before
> +reporting, consider whether the issue actually crosses a trust boundary on such
> +a system.
> +
> +If you are unsure whether an issue qualifies, err on the side of reporting
> +privately: the security team would rather triage a borderline report than miss
> +a real vulnerability.  Reporting ordinary bugs to the security list, however,
> +does not make them move faster and consumes triage capacity that other reports
> +need.
> +
>   Identifying contacts
>   --------------------
>   
> diff --git a/Documentation/process/threat-model.rst b/Documentation/process/threat-model.rst
> new file mode 100644
> index 0000000000000..8cd46483cd8b5
> --- /dev/null
> +++ b/Documentation/process/threat-model.rst
> @@ -0,0 +1,231 @@
> +.. _threatmodel:
> +
> +The Linux Kernel threat model
> +=============================
> +
> +There are a lot of assumptions regarding what the kernel protects against and
> +what it does not protect against. These assumptions tend to cause confusion for

Could simply say "what it does not" or "what the kernel does and does not protect
against"

> +bug reports (:doc:`security-related ones <security-bugs>` vs
> +:doc:`non-security ones <../admin-guide/reporting-issues>`), and can complicate
> +security enforcement when the responsibilities for some boundaries is not clear
> +between the kernel, distros, administrators and users.
> +
> +This document tries to clarify the responsibilities of the kernel in this
> +domain.
> +
> +The kernel's responsibilities
> +-----------------------------
> +
> +The kernel abstracts access to local hardware resources and to remote systems
> +in a way that allows multiple local users to get a fair share of the available
> +resources granted to them, and, when the underlying hardware permits, to assign
> +a level of confidentiality to their communications and to the data they are
> +processing or storing.
> +
> +The kernel assumes that the underlying hardware behaves according to its
> +specifications. This includes the integrity of the CPU's instruction set, the
> +transparency of the branch prediction unit and the cache units, the consistency
> +of the Memory Management Unit (MMU), the isolation of DMA-capable peripherals
> +(e.g., via IOMMU), state transitions in controllers, ranges of values read from
> +registers, the respect of documented hardware limitations, etc.
> +
> +When hardware fails to maintain its specified isolation (e.g., CPU bugs,
> +side-channels, hardware response to unexpected inputs), the kernel will usually
> +attempt to implement reasonable mitigations. These are best-effort measures
> +intended to reduce the attack surface or elevate the cost of an attack within
> +the limits of the hardware's facilities; they do not constitute a
> +kernel-provided safety guarantee.
> +
> +Users always perform their activities under the authority of an administrator
> +who is able to grant or deny various types of permissions that may affect how
> +users benefit from available resources, or the level of confidentiality of
> +their activities. Administrators may also delegate all or part of their own
> +permissions to some users, particularly via capabilities but not only. All this
> +is performed via configuration (sysctl, file-system permissions etc).
> +
> +The Linux Kernel applies a certain collection of default settings that match
> +its threat model. Distros have their own threat model and will come with their
> +own configuration presets, that the administrator may have to adjust to better
> +suit their expectations (relax or restrict).
> +
> +By default, the Linux Kernel guarantees the following protections when running
> +on common processors featuring privilege levels and memory management units:
> +
> +* **User-based isolation**: an unprivileged user may restrict access to their
> +  own data from other unprivileged users running on the same system. This
> +  includes:
> +
> +  * stored data, via file system permissions
> +  * in-memory data (pages are not accessible by default to other users)
> +  * process activity (ptrace is not permitted to other users)
> +  * inter-process communication (other users may not observe data exchanged via
> +    UNIX domain sockets or other IPC mechanisms).
> +  * network communications within the same or with other systems
> +
> +* **Capability-based protection**:
> +
> +  * users not having the ``CAP_SYS_ADMIN`` capability may not alter the
> +    kernel's configuration, memory nor state, change other users' view of the
> +    file system layout, grant any user capabilities they do not have, nor
> +    affect the system's availability (shutdown, reboot, panic, hang, or making
> +    the system unresponsive via unbounded resource exhaustion).
> +  * users not having the ``CAP_NET_ADMIN`` capability may not alter the network
> +    configuration, intercept nor spoof network communications from other users
> +    nor systems.
> +  * users not having ``CAP_SYS_PTRACE`` may not observe other users' processes
> +    activities.
> +
> +When ``CONFIG_USER_NS`` is set, the kernel also permits unprivileged users to
> +create their own user namespace in which they have all capabilities, but with a
> +number of restrictions (they may not perform actions that have impacts on the
> +initial user namespace, such as changing time, loading modules or mounting
> +block devices). Please refer to ``user_namespaces(7)`` for more details, the
> +possibilities of user namespaces are not covered in this document.
> +
> +The kernel also offers a lot of troubleshooting and debugging facilities, which
> +can constitute attack vectors when placed in wrong hands. While some of them
> +are designed to be accessible to regular local users with a low risk (e.g.
> +kernel logs via ``/proc/kmsg``), some would expose enough information to
> +represent a risk in most places and the decision to expose them is under the
> +administrator's responsibility (perf events, traces), and others are not
> +designed to be accessed by non-privileged users (e.g. debugfs). Access to these
> +facilities by a user who has been explicitly granted permission by an
> +administrator does not constitute a security breach.
> +
> +Bugs that permit to violate the principles above constitute security breaches.
> +However, bugs that permit one violation only once another one was already
> +achieved are only weaknesses. The kernel applies a number of self-protection
> +measures whose purpose is to avoid crossing a security boundary when certain
> +classes of bugs are found, but a failure of these extra protections do not
> +constitute a vulnerability alone.
> +
> +What does not constitute a security bug
> +---------------------------------------
> +
> +In the Linux kernel's threat model, the following classes of problems are
> +**NOT** considered as Linux Kernel security bugs. However, when it is believed
> +that the kernel could do better, they should be reported, so that they can be
> +reviewed and fixed where reasonably possible, but they will be handled as any
> +regular bug:
> +
> +* **Configuration**:
> +
> +  * outdated kernels and particularly end-of-life branches are out of the scope
> +    of the kernel's threat model: administrators are responsible for keeping
> +    their system up to date. For a bug to qualify as a security bug, it must be
> +    demonstrated that it affects actively maintained versions.
> +
> +  * build-level: changes to the kernel configuration that are explicitly
> +    documented as lowering the security level (e.g. ``CONFIG_NOMMU``), or
> +    targeted at developers only.
> +
> +  * OS-level: changes to command line parameters, sysctls, filesystem
> +    permissions, user capabilities, exposure of privileged interfaces, that
> +    explicitly increase exposure by either offering non-default access to
> +    unprivileged users, or reduce the kernel's ability to enforce some
> +    protections or mitigations. Example: write access to procfs or debugfs.
> +
> +  * issues triggered only when using features intended for development or
> +    debugging (e.g., lockdep, KASAN, fault-injection): these features are known
> +    to introduce overhead and potential instability and are not intended for
> +    production use.

Can we call out features and tools (the ones in kernel repo)

sched_ext's Kconfig enables
a few debug options including LOCKDEP

tools/sched_ext/Kconfig:CONFIG_DEBUG_LOCKDEP=y

> +
> +  * loading of explicitly insecure/broken/staging modules, and generally any
> +    using any subsystem marked as experimental or not intended for production
> +    use.
> +
> +  * running out-of-tree modules or unofficial kernel forks; these should be
> +    reported to the relevant vendor.
> +
> +* **Excess of initial privileges**:
> +
> +  * actions performed by a user already possessing the privileges required to
> +    perform that action or modify that state (e.g. ``CAP_SYS_ADMIN``,
> +    ``CAP_NET_ADMIN``, ``CAP_SYS_RAWIO``, ``CAP_SYS_MODULE`` with no further
> +    boundary being crossed).
> +
> +  * actions performed in user namespace without permitting anything in the
> +    initial namespace that was not already permitted to the same user there.

This was a bit hard to parse - examples might help here

> +
> +  * anything performed by the root user in the initial namespace (e.g. kernel
> +    oops when writing to a privileged device).
> +
> +* **Out of production use**:
> +
> +  This covers theoretical/probabilistic attacks that rely on laboratory
> +  conditions with zero system noise, or those requiring an unrealistic number
> +  of attempts (e.g., billions of trials) that would be detected by standard
> +  system monitoring long before success, such as:
> +
> +  * prediction of random numbers that only works in a totally silent
> +    environment (such as IP ID, TCP ports or sequence numbers that can only be
> +    guessed in a lab).
> +
> +  * activity observation and information leaks based on probabilistic
> +    approaches that are prone to measurement noise and not realistically
> +    reproducible on a production system.
> +
> +  * issues that can only be triggered by heavy attacks (e.g. brute force) whose
> +    impact on the system makes it unlikely or impossible to remain undetected
> +    before they succeed (e.g. consuming all memory before succeeding).
> +
> +  * problems seen only under development simulators, emulators, or combinations
> +    that do not exist on real systems at the time of reporting (issues
> +    involving tens of millions of threads, tens of thousands of CPUs,
> +    unrealistic CPU frequencies, RAM sizes or disk capacities, network speeds.
> +
> +  * issues whose reproduction requires hardware modification or emulation,
> +    including fake USB devices that pretend to be another one.
> +
> +  * as well as issues that can be triggered at a cost that is orders of
> +    magnitude higher than the expected benefits (e.g. fully functional keyboard
> +    emulator only to retrieve 7 uninitialized bytes in a structure, or
> +    brute-force method involving millions of connection attempts to guess a
> +    port number).

Can we add a section about problems found using experimental or tools
in development stage?

> +
> +* **Hardening failures**:
> +
> +  * ability to bypass some of the kernel's hardening measures with no
> +    demonstrable exploit path (e.g. ASLR bypass, events timing or probing with
> +    no demonstrable consequence). These are just weaknesses, not
> +    vulnerabilities.
> +
> +  * missing argument checks and failure to report certain errors with no
> +    immediate consequence.
> +
> +* **Random information leaks**:
> +
> +  This concerns information leaks of small data parts that happen to be there
> +  and that cannot be chosen by the attacker, or face access restrictions:
> +
> +  * structure padding reported by syscalls or other interfaces.
> +
> +  * identifiers, partial data, non-terminated strings reported in error
> +    messages.
> +
> +  * Leaks of kernel memory addresses/pointers do not constitute an immediately
> +    exploitable vector and are not security bugs, though they must be reported
> +    and fixed.
> +
> +* **Crafted file system images**:
> +
> +  * bugs triggered by mounting a corrupted or maliciously crafted file system
> +    image are generally not security bugs, as the kernel assumes the underlying
> +    storage media is under the administrator's control, unless the filesystem
> +    driver is specifically documented as being hardened against untrusted media.
> +
> +  * issues that are resolved, mitigated, or detected by running a filesystem
> +    consistency check (fsck) on the image prior to mounting.
> +
> +* **Physical access**:
> +
> +  Issues that require physical access to the machine, hardware modification, or
> +  the use of specialized hardware (e.g., logic analyzers, DMA-attack tools over
> +  PCI-E/Thunderbolt) are out of scope unless the system is explicitly
> +  configured with technologies meant to defend against such attacks
> +  (e.g. IOMMU).
> +
> +* **Functional and performance regressions**:
> +
> +  Any issue that can be mitigated by setting proper permissions and limits
> +  doesn't qualify as a security bug.

Reviewed-by: Shuah Khan <skhan@linuxfoundation.org>

thanks,
-- Shuah


^ permalink raw reply

* Re: [PATCH] killswitch: add per-function short-circuit mitigation primitive
From: Andrew Morton @ 2026-05-08 20:56 UTC (permalink / raw)
  To: Sasha Levin
  Cc: corbet, skhan, linux-doc, linux-kernel, linux-kselftest, gregkh
In-Reply-To: <20260507070547.2268452-1-sashal@kernel.org>

On Thu,  7 May 2026 03:05:45 -0400 Sasha Levin <sashal@kernel.org> wrote:

> When a (security) issue goes public, fleets stay exposed until a patched kernel
> is built, distributed, and rebooted into.
> 
> For many such issues the simplest mitigation is to stop calling the buggy
> function. Killswitch provides that. An admin writes:
> 
>     echo "engage af_alg_sendmsg -1" \
>         > /sys/kernel/security/killswitch/control

It certainly sounds useful, but what would I know.  How do we hunt down
suitable operations people (aka "target audience") to find out how
useful this is to them?

Also,

> 19 files changed, 1451 insertions(+), 1 deletion(-)

wowzers.  I'm looking at samples/livepatch/livepatch-sample.c wondering
"why"?


^ permalink raw reply

* Re: [PATCH v11 02/12] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Borislav Petkov @ 2026-05-08 21:05 UTC (permalink / raw)
  To: Pawan Gupta
  Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
	David Kaplan, Sean Christopherson, Dave Hansen, Peter Zijlstra,
	Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, KP Singh,
	Jiri Olsa, David S. Miller, David Laight, Andy Lutomirski,
	Thomas Gleixner, Ingo Molnar, David Ahern, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend,
	Stanislav Fomichev, Hao Luo, Paolo Bonzini, Jonathan Corbet,
	Jason Baron, Alice Ryhl, Steven Rostedt, Ard Biesheuvel,
	Shuah Khan, linux-kernel, kvm, Asit Mallick, Tao Zhang, bpf,
	netdev, linux-doc
In-Reply-To: <20260422-vmscape-bhb-v11-2-b18e0cf32af4@linux.intel.com>

On Wed, Apr 22, 2026 at 11:15:15PM -0700, Pawan Gupta wrote:
> As a mitigation for BHI, clear_bhb_loop() executes branches that overwrite
> the Branch History Buffer (BHB). On Alder Lake and newer parts this
> sequence is not sufficient because it doesn't clear enough entries. This
> was not an issue because these CPUs use the BHI_DIS_S hardware mitigation
> in the kernel.
> 
> Now with VMSCAPE (BHI variant) it is also required to isolate branch
> history between guests and userspace. Since BHI_DIS_S only protects the
> kernel, the newer CPUs also use IBPB.
> 
> A cheaper alternative to the current IBPB mitigation is clear_bhb_loop().
> But it currently does not clear enough BHB entries to be effective on newer
> CPUs with larger BHB. At boot, dynamically set the loop count of
> clear_bhb_loop() such that it is effective on newer CPUs too.
> 
> Introduce global loop counts, initializing them with appropriate value
> based on the hardware feature X86_FEATURE_BHI_CTRL.
> 
> Suggested-by: Dave Hansen <dave.hansen@linux.intel.com>
> Signed-off-by: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
> ---
>  arch/x86/entry/entry_64.S            |  8 +++++---
>  arch/x86/include/asm/nospec-branch.h |  2 ++
>  arch/x86/kernel/cpu/bugs.c           | 13 +++++++++++++
>  3 files changed, 20 insertions(+), 3 deletions(-)

Simple and clean - that's how I like 'em.

Acked-by: Borislav Petkov (AMD) <bp@alien8.de>

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply

* Re: [PATCH RFC v4 10/44] KVM: guest_memfd: Add support for KVM_SET_MEMORY_ATTRIBUTES2
From: Ackerley Tng @ 2026-05-08 21:21 UTC (permalink / raw)
  To: Sean Christopherson, Michael Roth
  Cc: aik, andrew.jones, binbin.wu, brauner, chao.p.peng, david,
	ira.weiny, jmattson, jthoughton, oupton, pankaj.gupta, qperret,
	rick.p.edgecombe, rientjes, shivankg, steven.price, tabba, willy,
	wyihan, yan.y.zhao, forkloop, pratyush, suzuki.poulose,
	aneesh.kumar, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Vishal Annapurve, Andrew Morton, Chris Li,
	Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Axel Rasmussen, Yuanchu Xie, Wei Xu, Jason Gunthorpe,
	Vlastimil Babka, kvm, linux-kernel, linux-trace-kernel, linux-doc,
	linux-kselftest, linux-mm
In-Reply-To: <af4gJ6xZ3e7UXOuO@google.com>

Sean Christopherson <seanjc@google.com> writes:

>
> [...snip...]
>
>
> Summarizing this week's PUCK call[*]:
>
> Scrap PRESERVE and ZERO, and simply rely on vendor specific semantics.
>
>
> [...snip...]
>

Thanks for the summary! Please see v6 here:

https://lore.kernel.org/all/20260507-gmem-inplace-conversion-v6-0-91ab5a8b19a4@google.com/T/

^ permalink raw reply

* Re: [PATCH net-next v3 3/8] net: devmem: support TX over NETMEM_TX_NO_DMA devices
From: Bobby Eshleman @ 2026-05-08 21:28 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi, Yanteng Si,
	Dongliang Mu, Michael Chan, Pavan Chebbi, Joshua Washington,
	Harshitha Ramamurthy, Saeed Mahameed, Tariq Toukan, Mark Bloch,
	Leon Romanovsky, Alexander Duyck, kernel-team, Daniel Borkmann,
	Nikolay Aleksandrov, Shuah Khan, dw, sdf.kernel, mohsin.bashr,
	willemb, jiang.kun2, xu.xin16, wang.yaxin, netdev, linux-doc,
	linux-kernel, linux-rdma, bpf, linux-kselftest,
	Stanislav Fomichev, Mina Almasry, Bobby Eshleman
In-Reply-To: <20260508134717.4ef87ab6@kernel.org>

On Fri, May 08, 2026 at 01:47:17PM -0700, Jakub Kicinski wrote:
> On Thu, 07 May 2026 19:27:48 -0700 Bobby Eshleman wrote:
> > +	/* Virtual device (e.g. netkit) the user called bind-tx on. Must be
> > +	 * NETMEM_TX_NO_DMA.
> > +	 */
> > +	struct net_device *vdev;
> 
> AI keeps complaining that we don't hold a reference to this dev which 
> I think is fine, we're just comparing pointers. Could we maybe make this
> a void pointer and mention in the comment that we treat it as "best
> effort cookie" (better phrasing welcome).
> 
> Or we should wipe these vdev pointers when vdevs disappear, not sure
> how hard that'd be (or whether it's worth the extra state).

My guess is this would probably be the simplest way?

diff --git a/net/core/devmem.c b/net/core/devmem.c
index 644c286b778f..e28fae14c687 100644
--- a/net/core/devmem.c
+++ b/net/core/devmem.c
@@ -533,3 +533,38 @@ static const struct memory_provider_ops dmabuf_devmem_ops = {
 	.nl_fill		= mp_dmabuf_devmem_nl_fill,
 	.uninstall		= mp_dmabuf_devmem_uninstall,
 };
+
+static int net_devmem_netdev_event(struct notifier_block *nb,
+				   unsigned long event, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	struct net_devmem_dmabuf_binding *binding;
+	unsigned long id;
+
+	if (event != NETDEV_UNREGISTER)
+		return NOTIFY_DONE;
+
+	xa_for_each(&net_devmem_dmabuf_bindings, id, binding) {
+		if (!net_devmem_dmabuf_binding_get(binding))
+			continue;
+		mutex_lock(&binding->lock);
+		if (READ_ONCE(binding->vdev) == dev) {
+			ASSERT_EXCLUSIVE_WRITER(binding->vdev);
+			WRITE_ONCE(binding->vdev, NULL);
+		}
+		mutex_unlock(&binding->lock);
+		net_devmem_dmabuf_binding_put(binding);
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block net_devmem_netdev_nb = {
+	.notifier_call = net_devmem_netdev_event,
+};
+
+static int __init net_devmem_init(void)
+{
+	return register_netdevice_notifier(&net_devmem_netdev_nb);
+}
+subsys_initcall(net_devmem_init);


I'm open to either approach. The void* + comment is good too, IMHO.  For
the notifier, I'd probably want to add a test too ensure sendmsg() kicks
back after the device is removed.

Best,
Bobby

^ permalink raw reply related

* Re: [PATCH] killswitch: add per-function short-circuit mitigation primitive
From: Sasha Levin @ 2026-05-08 21:47 UTC (permalink / raw)
  To: Andrew Morton
  Cc: corbet, skhan, linux-doc, linux-kernel, linux-kselftest, gregkh
In-Reply-To: <20260508135630.a380e3c187b59e4c04e6f358@linux-foundation.org>

On Fri, May 08, 2026 at 01:56:30PM -0700, Andrew Morton wrote:
>On Thu,  7 May 2026 03:05:45 -0400 Sasha Levin <sashal@kernel.org> wrote:
>
>> When a (security) issue goes public, fleets stay exposed until a patched kernel
>> is built, distributed, and rebooted into.
>>
>> For many such issues the simplest mitigation is to stop calling the buggy
>> function. Killswitch provides that. An admin writes:
>>
>>     echo "engage af_alg_sendmsg -1" \
>>         > /sys/kernel/security/killswitch/control
>
>It certainly sounds useful, but what would I know.  How do we hunt down
>suitable operations people (aka "target audience") to find out how
>useful this is to them?

I'm not entierly sure here... If folks have suggestions on folks to loop in,
that'll be great!

>> 19 files changed, 1451 insertions(+), 1 deletion(-)
>
>wowzers.  I'm looking at samples/livepatch/livepatch-sample.c wondering
>"why"?

Yup, a bit chunky, but over half of it is documentation and testing, and the
actual functional code is largely the securityfs interface.

-- 
Thanks,
Sasha

^ permalink raw reply

* Re: [PATCH net-next v3 3/8] net: devmem: support TX over NETMEM_TX_NO_DMA devices
From: Jakub Kicinski @ 2026-05-08 22:27 UTC (permalink / raw)
  To: Bobby Eshleman
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi, Yanteng Si,
	Dongliang Mu, Michael Chan, Pavan Chebbi, Joshua Washington,
	Harshitha Ramamurthy, Saeed Mahameed, Tariq Toukan, Mark Bloch,
	Leon Romanovsky, Alexander Duyck, kernel-team, Daniel Borkmann,
	Nikolay Aleksandrov, Shuah Khan, dw, sdf.kernel, mohsin.bashr,
	willemb, jiang.kun2, xu.xin16, wang.yaxin, netdev, linux-doc,
	linux-kernel, linux-rdma, bpf, linux-kselftest,
	Stanislav Fomichev, Mina Almasry, Bobby Eshleman
In-Reply-To: <af5Vlwb5RctHym8D@devvm29614.prn0.facebook.com>

On Fri, 8 May 2026 14:28:55 -0700 Bobby Eshleman wrote:
> My guess is this would probably be the simplest way?

IDK. Notifiers are so inelegant. Don't we have the same problem with
the main ->dev on Tx binding?

^ permalink raw reply

* Re: [PATCH 4/5] docs: fix repeated prepositions across documentation
From: Andrew Lunn @ 2026-05-08 22:41 UTC (permalink / raw)
  To: Shuah Khan
  Cc: Adrien Reynard, Andrey Ryabinin, Alexander Potapenko,
	Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino,
	Jonathan Corbet, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Richard Weinberger, Anton Ivanov,
	Johannes Berg, open list:KASAN, open list:DOCUMENTATION PROCESS,
	open list:DOCUMENTATION, open list,
	open list:NETWORKING [GENERAL], open list:USER-MODE LINUX (UML)
In-Reply-To: <2b771350-0562-4cb1-b9b3-cc3ce59b1a63@linuxfoundation.org>

On Fri, May 08, 2026 at 11:23:43AM -0600, Shuah Khan wrote:
> On 5/8/26 10:38, Adrien Reynard wrote:
> 
> Missing commit log
> 
> > Signed-off-by: Adrien Reynard <reynard.adrien.08@gmail.com>
> > ---
> >   Documentation/dev-tools/kasan.rst                   | 2 +-
> >   Documentation/networking/switchdev.rst              | 2 +-
> >   Documentation/virt/uml/user_mode_linux_howto_v2.rst | 2 +-
> >   3 files changed, 3 insertions(+), 3 deletions(-)
> > 
> > diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst
> > index 4968b2aa60c8..3a8bd40ad905 100644
> > --- a/Documentation/dev-tools/kasan.rst
> > +++ b/Documentation/dev-tools/kasan.rst
> > @@ -392,7 +392,7 @@ reserved to tag freed memory regions.
> >   If the hardware does not support MTE (pre ARMv8.5), Hardware Tag-Based KASAN
> >   will not be enabled. In this case, all KASAN boot parameters are ignored.
> > -Note that enabling CONFIG_KASAN_HW_TAGS always results in in-kernel TBI being
> > +Note that enabling CONFIG_KASAN_HW_TAGS always results in-kernel TBI being
> 
> This is correct the way it is - no need to change this. "results in in-kernel"
> 
> >   enabled. Even when ``kasan.mode=off`` is provided or when the hardware does not
> >   support MTE (but supports TBI).
> > diff --git a/Documentation/networking/switchdev.rst b/Documentation/networking/switchdev.rst
> > index 2966b7122f05..948bce44ca9b 100644
> > --- a/Documentation/networking/switchdev.rst
> > +++ b/Documentation/networking/switchdev.rst
> > @@ -162,7 +162,7 @@ The switchdev driver can know a particular port's position in the topology by
> >   monitoring NETDEV_CHANGEUPPER notifications.  For example, a port moved into a
> >   bond will see its upper master change.  If that bond is moved into a bridge,
> >   the bond's upper master will change.  And so on.  The driver will track such
> > -movements to know what position a port is in in the overall topology by
> > +movements to know what position a port is in the overall topology by
> 
> This looks fine.

Fine as in the change is correct, or fine in that the original is
correct and the change is wrong?

Because the change is wrong, there should be two in's here. Please
search the archive for an explanation why.

This is the second time in a week i've had to deal with "improvements"
like this actually breaking stuff. Which also shows that the submitter
did not search to see if somebody has tried to fix this once already,
and failed.

Can we get the tool changed to add a warning, something like:

  WARNING: This tool uses very simple pattern matching to look for
  repeated words. It does not understand the complexity of English,
  and will often result in false positive reports. Please assume it is
  wrong until proven otherwise.

       Andrew

^ permalink raw reply

* Re: [PATCH v2 00/14] userfaultfd: working set tracking for VM guest memory
From: Kiryl Shutsemau @ 2026-05-08 22:48 UTC (permalink / raw)
  To: Andrew Morton
  Cc: rppt, peterx, david, ljs, surenb, vbabka, Liam.Howlett, ziy,
	corbet, skhan, seanjc, pbonzini, jthoughton, aarcange, sj,
	usama.arif, linux-mm, linux-kernel, linux-doc, linux-kselftest,
	kvm, kernel-team
In-Reply-To: <20260508103220.aa46427b6f4c5d0247d2afb0@linux-foundation.org>

On Fri, May 08, 2026 at 10:32:20AM -0700, Andrew Morton wrote:
> On Fri,  8 May 2026 16:55:12 +0100 "Kiryl Shutsemau (Meta)" <kas@kernel.org> wrote:
> 
> > This series adds userfaultfd support for tracking the working set of
> > VM guest memory, so a VMM can identify cold pages and evict them to
> > tiered or remote storage.
> > 
> > v1: https://lore.kernel.org/all/20260427114607.4068647-1-kas@kernel.org/
> 
> Thanks.  I'll duck v2 for now, await more review.

Sure.

> > Assisted-by: Claude:claude-opus-4-6
> 
> For my education, and perhaps for others: can you please explain how
> you used Claude in the preparation of this series?

I'm no expert by any means, but here's how I used it here.

For this particular project there was quite a bit of path-finding.
I had a phase where I bounced ideas off Claude. It helped me
understand the problem space better and formulate possible solutions.
Rubber ducking on steroids.

Once it's clear _what_ to do, we formulate a plan on _how_. It also
involves back and forth.

Once the plan was done, I gave the go-ahead on executing it.

Userfaultfd already had a test suite, and it was extended to cover the new
functionality. I have some scripts to build the kernel and run it in a VM.
Claude knows how to use them, so at the end of plan execution I had a
functional feature.

Then the review phase. The most time-consuming and draining part.
I carefully reviewed all patches.

At this stage I use Claude as an editor.

Some of the changes I asked for required substantial rework of the whole
patchset, and I had to start the review from scratch. A good test suite and
build-test harness help to keep the whole thing from falling apart.

It took me quite a few review rounds before I was happy with the result.
Maybe between 8 and 10. I think better instructions can cut this number down.

And I need to rethink how I do the review. Reading the git log in
parallel with examining the code in the editor and giving instructions to
Claude is not very ergonomic. There's room for improvement.

Once I was happy with the patchset to give it Signed-off-by, I ran it
through Chris' review prompts several times, addressing the issues.

I hope it is helpful. I would also be glad if other folks shared their
workflow. There is probably a better way to achieve the same result.
I am new to the game.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov

^ permalink raw reply

* Re: [PATCH 4/5] docs: fix repeated prepositions across documentation
From: Randy Dunlap @ 2026-05-08 22:52 UTC (permalink / raw)
  To: Andrew Lunn, Shuah Khan
  Cc: Adrien Reynard, Andrey Ryabinin, Alexander Potapenko,
	Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino,
	Jonathan Corbet, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Richard Weinberger, Anton Ivanov,
	Johannes Berg, open list:KASAN, open list:DOCUMENTATION PROCESS,
	open list:DOCUMENTATION, open list,
	open list:NETWORKING [GENERAL], open list:USER-MODE LINUX (UML)
In-Reply-To: <3c6cde1a-9ce0-4d63-ba89-820c596cff3e@lunn.ch>



On 5/8/26 3:41 PM, Andrew Lunn wrote:
> On Fri, May 08, 2026 at 11:23:43AM -0600, Shuah Khan wrote:
>> On 5/8/26 10:38, Adrien Reynard wrote:
>>
>> Missing commit log
>>
>>> Signed-off-by: Adrien Reynard <reynard.adrien.08@gmail.com>
>>> ---
>>>   Documentation/dev-tools/kasan.rst                   | 2 +-
>>>   Documentation/networking/switchdev.rst              | 2 +-
>>>   Documentation/virt/uml/user_mode_linux_howto_v2.rst | 2 +-
>>>   3 files changed, 3 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst
>>> index 4968b2aa60c8..3a8bd40ad905 100644
>>> --- a/Documentation/dev-tools/kasan.rst
>>> +++ b/Documentation/dev-tools/kasan.rst
>>> @@ -392,7 +392,7 @@ reserved to tag freed memory regions.
>>>   If the hardware does not support MTE (pre ARMv8.5), Hardware Tag-Based KASAN
>>>   will not be enabled. In this case, all KASAN boot parameters are ignored.
>>> -Note that enabling CONFIG_KASAN_HW_TAGS always results in in-kernel TBI being
>>> +Note that enabling CONFIG_KASAN_HW_TAGS always results in-kernel TBI being
>>
>> This is correct the way it is - no need to change this. "results in in-kernel"
>>
>>>   enabled. Even when ``kasan.mode=off`` is provided or when the hardware does not
>>>   support MTE (but supports TBI).
>>> diff --git a/Documentation/networking/switchdev.rst b/Documentation/networking/switchdev.rst
>>> index 2966b7122f05..948bce44ca9b 100644
>>> --- a/Documentation/networking/switchdev.rst
>>> +++ b/Documentation/networking/switchdev.rst
>>> @@ -162,7 +162,7 @@ The switchdev driver can know a particular port's position in the topology by
>>>   monitoring NETDEV_CHANGEUPPER notifications.  For example, a port moved into a
>>>   bond will see its upper master change.  If that bond is moved into a bridge,
>>>   the bond's upper master will change.  And so on.  The driver will track such
>>> -movements to know what position a port is in in the overall topology by
>>> +movements to know what position a port is in the overall topology by
>>
>> This looks fine.
> 
> Fine as in the change is correct, or fine in that the original is
> correct and the change is wrong?
> 
> Because the change is wrong, there should be two in's here. Please
> search the archive for an explanation why.
> 
> This is the second time in a week i've had to deal with "improvements"
> like this actually breaking stuff. Which also shows that the submitter
> did not search to see if somebody has tried to fix this once already,
> and failed.
> 
> Can we get the tool changed to add a warning, something like:
> 
>   WARNING: This tool uses very simple pattern matching to look for
>   repeated words. It does not understand the complexity of English,
>   and will often result in false positive reports. Please assume it is
>   wrong until proven otherwise.

There was no commit log and no cover letter AFAIK.
Do we know what tool was used?

Adrien, how did you discover these repeated words?

(If it's my script from 2021, I'll gladly update it.)

-- 
~Randy


^ permalink raw reply

* Re: [PATCH net-next v3 3/8] net: devmem: support TX over NETMEM_TX_NO_DMA devices
From: Bobby Eshleman @ 2026-05-08 23:03 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi, Yanteng Si,
	Dongliang Mu, Michael Chan, Pavan Chebbi, Joshua Washington,
	Harshitha Ramamurthy, Saeed Mahameed, Tariq Toukan, Mark Bloch,
	Leon Romanovsky, Alexander Duyck, kernel-team, Daniel Borkmann,
	Nikolay Aleksandrov, Shuah Khan, dw, sdf.kernel, mohsin.bashr,
	willemb, jiang.kun2, xu.xin16, wang.yaxin, netdev, linux-doc,
	linux-kernel, linux-rdma, bpf, linux-kselftest,
	Stanislav Fomichev, Mina Almasry, Bobby Eshleman
In-Reply-To: <20260508152708.011a9687@kernel.org>

On Fri, May 08, 2026 at 03:27:08PM -0700, Jakub Kicinski wrote:
> On Fri, 8 May 2026 14:28:55 -0700 Bobby Eshleman wrote:
> > My guess is this would probably be the simplest way?
> 
> IDK. Notifiers are so inelegant. Don't we have the same problem with
> the main ->dev on Tx binding?

Yes, true. For some reason, I thought I recalled the dma buf attachment
causing some chain of reference holding that kept the device alive, but
that is actually not true...

^ permalink raw reply

* Re: [PATCH v6 01/43] KVM: guest_memfd: Introduce per-gmem attributes, use to guard user mappings
From: Ackerley Tng @ 2026-05-08 23:36 UTC (permalink / raw)
  To: Ackerley Tng via B4 Relay, aik, andrew.jones, binbin.wu, brauner,
	chao.p.peng, david, ira.weiny, jmattson, jthoughton, michael.roth,
	oupton, pankaj.gupta, qperret, rick.p.edgecombe, rientjes,
	shivankg, steven.price, tabba, willy, wyihan, yan.y.zhao,
	forkloop, pratyush, suzuki.poulose, aneesh.kumar, liam,
	Paolo Bonzini, Sean Christopherson, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Vishal Annapurve, Andrew Morton, Chris Li,
	Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Axel Rasmussen, Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng,
	Shakeel Butt, Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka
  Cc: kvm, linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
	linux-mm, linux-coco
In-Reply-To: <20260507-gmem-inplace-conversion-v6-1-91ab5a8b19a4@google.com>

Ackerley Tng via B4 Relay <devnull+ackerleytng.google.com@kernel.org>
writes:

>
> [...snip...]
>
> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index 69c9d6d546b28..5011d38820d0d 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
> @@ -4,6 +4,7 @@
>  #include <linux/falloc.h>
>  #include <linux/fs.h>
>  #include <linux/kvm_host.h>
> +#include <linux/maple_tree.h>
>  #include <linux/mempolicy.h>
>  #include <linux/pseudo_fs.h>
>  #include <linux/pagemap.h>
> @@ -33,6 +34,13 @@ struct gmem_inode {
>  	struct list_head gmem_file_list;
>
>  	u64 flags;
> +	/*
> +	 * Every index in this inode, whether memory is populated or
> +	 * not, is tracked in attributes. The entire range of indices,
> +	 * corresponding to the size of this inode, is represented in
> +	 * this maple tree.

Concretely, if the entire guest_memfd is 2M in size, indices [0, 511] is
represented with some value, either 0 (SHARED) or
KVM_MEMORY_ATTRIBUTE_PRIVATE. [512, ULONG_MAX] is also defined in the
tree, as NULL.

Since guest_memfd uses xa_mk_value(0) to store the value 0 ("SHARED"),
that makes 0 distinct from NULL, which works for guest_memfd.


(Liam and I discussed this off-list due to a email configuration issue)

> +	 */
> +	struct maple_tree attributes;
>  };
>
>
> [...snip...]
>

^ permalink raw reply

* Re: [PATCH] killswitch: add per-function short-circuit mitigation primitive
From: Andrew Morton @ 2026-05-08 23:49 UTC (permalink / raw)
  To: Sasha Levin
  Cc: corbet, skhan, linux-doc, linux-kernel, linux-kselftest, gregkh
In-Reply-To: <af5Z2IvtS5pVorSl@laps>

On Fri, 8 May 2026 17:47:04 -0400 Sasha Levin <sashal@kernel.org> wrote:

> >> 19 files changed, 1451 insertions(+), 1 deletion(-)
> >
> >wowzers.  I'm looking at samples/livepatch/livepatch-sample.c wondering
> >"why"?
> 
> Yup, a bit chunky, but over half of it is documentation and testing, and the
> actual functional code is largely the securityfs interface.

So we can't use livepatch here?

^ permalink raw reply

* Re: [PATCH v9 0/4] kunit: Add support for suppressing warning backtraces
From: Andrew Morton @ 2026-05-08 23:52 UTC (permalink / raw)
  To: Albert Esteve
  Cc: Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-kernel,
	linux-arch, linux-kselftest, kunit-dev, dri-devel, workflows,
	linux-riscv, linux-doc, peterz, Alessandro Carminati,
	Guenter Roeck, Kees Cook, Linux Kernel Functional Testing,
	Maíra Canal, Dan Carpenter, Simona Vetter
In-Reply-To: <20260508-kunit_add_support-v9-0-99df7aa880f6@redhat.com>

On Fri, 08 May 2026 17:02:44 +0200 Albert Esteve <aesteve@redhat.com> wrote:

> Some unit tests intentionally trigger warning backtraces by passing bad
> parameters to kernel API functions. Such unit tests typically check the
> return value from such calls, not the existence of the warning backtrace.
> 
> ...
> 
> Solve the problem by providing a means to suppress warning backtraces
> originating from the current kthread while executing test code.
> Since each KUnit test runs in its own kthread, this effectively scopes
> suppression to the test that enabled it, without requiring any
> architecture-specific code.

Thanks.  AI review has a bunch of questions:
	https://sashiko.dev/#/patchset/20260508-kunit_add_support-v9-0-99df7aa880f6@redhat.com

^ permalink raw reply

* Re: [PATCH v2 02/14] mm: rename uffd-wp PTE bit macros to uffd
From: SeongJae Park @ 2026-05-08 23:52 UTC (permalink / raw)
  To: Kiryl Shutsemau (Meta)
  Cc: SeongJae Park, akpm, rppt, peterx, david, ljs, surenb, vbabka,
	Liam.Howlett, ziy, corbet, skhan, seanjc, pbonzini, jthoughton,
	aarcange, usama.arif, linux-mm, linux-kernel, linux-doc,
	linux-kselftest, kvm, kernel-team
In-Reply-To: <4ff3508b951ec4879177dd079003c3fa3af0a444.1778254670.git.kas@kernel.org>

On Fri,  8 May 2026 16:55:14 +0100 "Kiryl Shutsemau (Meta)" <kas@kernel.org> wrote:

> The uffd-wp PTE bit is about to gain a second consumer: userfaultfd
> RWP will use the same bit to mark access-tracking PTEs, distinct
> from mprotect(PROT_NONE) or NUMA-hinting PTEs. WP vs RWP semantics
> come from the VMA flag; the bit is just "uffd has claimed this
> entry." Drop the "_wp" suffix from the arch-private bit macros so
> they reflect that.
> 
>   x86:   _PAGE_BIT_UFFD_WP  -> _PAGE_BIT_UFFD
>          _PAGE_UFFD_WP      -> _PAGE_UFFD
>          _PAGE_SWP_UFFD_WP  -> _PAGE_SWP_UFFD
>   arm64: PTE_UFFD_WP        -> PTE_UFFD
>          PTE_SWP_UFFD_WP    -> PTE_SWP_UFFD
>   riscv: _PAGE_UFFD_WP      -> _PAGE_UFFD
>          _PAGE_SWP_UFFD_WP  -> _PAGE_SWP_UFFD
> 
> Pure mechanical rename -- no behavior change.
> 
> Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
> Assisted-by: Claude:claude-opus-4-6
> Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

Reviewed-by: SeongJae Park <sj@kernel.org>


Thanks,
SJ

[...]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox