Linux Kernel Selftest development
 help / color / mirror / Atom feed
* [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets
@ 2026-07-10  3:28 Jing Wu
  2026-07-10  3:28 ` [PATCH v4 01/11] sched/isolation: Add runtime housekeeping mask updates with boot snapshots Jing Wu
                   ` (10 more replies)
  0 siblings, 11 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

This series introduces Dynamic Housekeeping Management (DHM) to the Linux
kernel, enabling runtime reconfiguration of kernel-noise housekeeping
(nohz_full tick suppression, RCU NOCB offloading, and managed IRQ
migration) through the existing cgroup v2 cpuset isolated partition
mechanism — no new kernel ABI required.

When a cpuset partition is set to isolated mode, DHM cycles each CPU in
that partition offline, reconfigures the housekeeping masks (removing the
CPU from HK_TYPE_KERNEL_NOISE and related types), and brings it back
online.  The subsystems (tick/nohz, RCU NOCB, genirq) pick up the new
masks through their existing CPU hotplug callbacks.  Destroying the
partition reverses the cycle: each CPU is taken offline, restored to all
housekeeping masks, and brought back online.

Housekeeping cpumask pointers are RCU-protected to allow lock-free readers
during updates.  A global dhm_cycling_cpus mask suppresses transient
cpuset partition invalidation while CPUs are being cycled.

This work is related to Waiman Long's [PATCH-next 00/23] series
(20260421030351.281436-1-longman@redhat.com), which also targets runtime
cpuset housekeeping control.  DHM's distinguishing feature is zero-boot-
parameter activation: nohz_full+nocb isolation can be achieved at runtime
without any boot-time nohz_full= or rcu_nocbs= parameters.

Signed-off-by: Jing Wu <realwujing@gmail.com>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
---
V3 -> V4:
- Drop apply() callback architecture entirely (struct housekeeping_cbs,
  pre_validate/apply hooks, housekeeping_update_types() notification).
  Thomas Gleixner identified concurrent in-kernel mask apply as
  fundamentally unsafe ("broken beyond repair") and endorsed CPU-by-CPU
  hotplug cycling as the correct approach.
- Track A prerequisites (housekeeping boot type isolation, RCU reader
  protection, cpuset trigger) submitted upstream separately; v4 builds
  on top of those three committed patches.
- Fix rcu/nocb lazy_init: remove __init from rcu_organize_nocb_kthreads()
  forward declaration (tree.h) to prevent GCC from placing the function in
  .init.text, which caused an NX-protected page fault when
  rcu_nocb_cpu_isolate() was called at runtime.  Add noinline to
  rcu_nocb_lazy_init() to preserve the GCC IPA call chain.
- Fix cpuset remote partition cycling suppression: extend dhm_cycling_cpus
  guard to the remote-partition disable path in cpuset_hotplug_update_tasks(),
  preventing false partition invalidation during hotplug cycling steps.
  Fixes selftest TEST_MATRIX[69] (A2: expected 1-2, got 1-3).

V2 -> V3:
- Replace notifier chain with explicit per-type callback interface
  (struct housekeeping_cbs with .name, .pre_validate, .apply fields).
- RCU-protect all housekeeping cpumask pointers; callers must hold
  rcu_read_lock() or use housekeeping_cpumask_rcu() in apply() callbacks.
- Drop 5 patches from v2: HK_TYPE enum separation (upstream aliases are
  already correct), no-op timer/hrtimer patches, kthread dead code, and
  workqueue double-update.
- Fix deadlock in rcu_hk_workfn(): remove cpus_read_lock() wrapper around
  remove_cpu()/add_cpu() which take cpu_hotplug_lock write side.
- Fix UAF in rcu_hk_apply(): snapshot the housekeeping cpumask inside the
  work function under rcu_read_lock(), not at apply() time where the old
  pointer may be freed by synchronize_rcu() before the work runs.
- Fix tick apply(): snapshot housekeeping_cpumask_rcu() under
  rcu_read_lock() as required by lockdep for runtime-mutable types.
- Activate context_tracking dynamically via ct_cpu_track_user() /
  ct_cpu_untrack_user() in tick apply(), eliminating the dependency on
  CONFIG_CONTEXT_TRACKING_USER_FORCE flagged by tglx.
- Fix genirq apply(): snapshot HK_TYPE_MANAGED_IRQ mask under
  rcu_read_lock() before the IRQ iteration loop.
- Simplify cpuset noise_types to BIT(HK_TYPE_KERNEL_NOISE) |
  BIT(HK_TYPE_MANAGED_IRQ), replacing the redundant per-alias bitmask.
- housekeeping_update_types(): always use cpu_possible_mask as base
  for HK_TYPE_KERNEL_NOISE, so de-isolation restores the mask to all
  possible CPUs rather than leaving it at its last non-trivial value.
- Initialize watchdog_cpumask from HK_TYPE_KERNEL_NOISE (not
  HK_TYPE_TIMER) at boot; keep it in sync at runtime via a new
  housekeeping_cbs callback.
- Add kernel-noise selftest to test_cpuset_prs.sh, including
  cpu_in_cpulist() for correct cpulist range membership detection and
  nohz_full sysfs verification when CONFIG_NO_HZ_FULL is active.
- Add RCU caller fixes: sched/core (HK_TYPE_KERNEL_NOISE) and
  drivers/hv (HK_TYPE_MANAGED_IRQ) are required because those types
  are updated at runtime; hrtimer (HK_TYPE_TIMER) and arm64/topology
  (HK_TYPE_TICK) are defensive fixes.
- Reorder patches so all subsystem callbacks are registered before the
  cpuset patch that triggers housekeeping_update_types().

V1 -> V2:
- Rebrand series from DHEI to DHM (Dynamic Housekeeping Management).
- Drop custom sysfs interface entirely.
- Integrate housekeeping control into cgroup v2 cpuset isolated partition
  mechanism.
- Add SMT-aware isolation constraints to prevent splitting SMT siblings.
- Add comprehensive documentation and cgroup functional selftests.
- Refactor mask transition logic to use RCU-safe handover.

v3: https://lore.kernel.org/r/20260618-wujing-dhm-v3-0-28f1a4d83b68@gmail.com
v2: https://lore.kernel.org/r/20260413-wujing-dhm-v2-0-06df21caba5d@gmail.com
v1: https://lore.kernel.org/all/20260325-dhei-v12-final-v1-0-919cca23cadf@gmail.com

---
Jing Wu (11):
      sched/isolation: Add runtime housekeeping mask updates with boot snapshots
      sched/isolation: RCU-protect runtime-mutable housekeeping cpumask readers
      cgroup/cpuset: Drive kernel-noise housekeeping updates from isolated partitions
      context_tracking: allow runtime per-CPU user tracking enable/disable
      rcu/nocb: support lazy init for runtime CPU isolation
      watchdog: sync watchdog_cpumask with HK_TYPE_KERNEL_NOISE on isolation
      tick/nohz: add runtime tick_nohz_full_mask update for CPU isolation
      cpuset: add dhm_cycling_cpus mask to suppress transient invalidation
      cpuset: drive kernel-noise isolation via per-CPU hotplug cycling
      docs: cgroup-v2: document kernel-noise isolation via isolated partitions
      selftests/cgroup: add kernel-noise isolation test to cpuset selftest

 Documentation/admin-guide/cgroup-v2.rst           |  17 +
 arch/arm64/kernel/topology.c                      |   9 +-
 drivers/base/cpu.c                                |  20 +-
 drivers/hv/channel_mgmt.c                         |  50 +-
 include/linux/context_tracking.h                  |   2 +
 include/linux/nmi.h                               |   2 +
 include/linux/rcupdate.h                          |   2 +
 include/linux/sched/isolation.h                   |  32 +-
 include/linux/tick.h                              |   4 +
 kernel/cgroup/cpuset.c                            | 161 +++++-
 kernel/context_tracking.c                         |  38 +-
 kernel/rcu/tree.h                                 |   2 +-
 kernel/rcu/tree_nocb.h                            |  43 +-
 kernel/sched/core.c                               |   7 +-
 kernel/sched/isolation.c                          | 197 +++++++-
 kernel/sched/sched.h                              |   2 +-
 kernel/time/hrtimer.c                             |   5 +-
 kernel/time/tick-sched.c                          |  47 +-
 kernel/watchdog.c                                 |  26 +-
 tools/testing/selftests/cgroup/test_cpuset_prs.sh | 580 +++++++++++++++++++++-
 20 files changed, 1181 insertions(+), 65 deletions(-)
---
base-commit: eb3f4b7426cfd2b79d65b7d37155480b32259a11
change-id: 20260408-wujing-dhm-8f43e2d49cd8

Best regards,
-- 
Jing Wu <realwujing@gmail.com>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v4 01/11] sched/isolation: Add runtime housekeeping mask updates with boot snapshots
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 02/11] sched/isolation: RCU-protect runtime-mutable housekeeping cpumask readers Jing Wu
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

The housekeeping cpumasks for kernel noise (HK_TYPE_KERNEL_NOISE) and
managed interrupts (HK_TYPE_MANAGED_IRQ) are computed once at boot from
the nohz_full= and isolcpus=managed_irq arguments.  Dynamic CPU
isolation driven by cpuset isolated partitions needs to update these
masks after boot.

Add housekeeping_update_types() to recompute one or more housekeeping
masks as (boot snapshot & ~isolated), publish them via RCU and free the
old masks after a grace period.

Introduce HK_TYPE_KERNEL_NOISE_BOOT and HK_TYPE_MANAGED_IRQ_BOOT to
record the immutable boot configuration.  Anchoring every update on the
boot snapshot keeps the runtime mask a subset of the boot set and lets
de-isolation restore the boot configuration exactly.

When neither nohz_full= nor isolcpus=nohz was given at boot no boot
snapshot exists, so cpu_possible_mask is the implicit boot set and
HK_TYPE_KERNEL_NOISE is enabled on the first runtime call.  In that
case housekeeping_update_types() also calls sched_tick_offload_init()
to allocate the tick offload percpu data that would otherwise only be
allocated by housekeeping_init() when nohz_full= is present at boot.
Remove __init from sched_tick_offload_init() and guard it with a
tick_work_cpu check so it is safe to call at runtime.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 include/linux/sched/isolation.h |  32 ++++++-
 kernel/sched/core.c             |   4 +-
 kernel/sched/isolation.c        | 197 +++++++++++++++++++++++++++++++++++-----
 kernel/sched/sched.h            |   2 +-
 4 files changed, 209 insertions(+), 26 deletions(-)

diff --git a/include/linux/sched/isolation.h b/include/linux/sched/isolation.h
index cf0fd03dd7a24..70602a74c1410 100644
--- a/include/linux/sched/isolation.h
+++ b/include/linux/sched/isolation.h
@@ -14,10 +14,26 @@ enum hk_type {
 	 * is always a subset of HK_TYPE_DOMAIN_BOOT.
 	 */
 	HK_TYPE_DOMAIN,
-	/* Inverse of boot-time isolcpus=managed_irq argument */
-	HK_TYPE_MANAGED_IRQ,
-	/* Inverse of boot-time nohz_full= or isolcpus=nohz arguments */
+
+	/*
+	 * Inverse of the boot-time nohz_full= or isolcpus=nohz arguments.
+	 * When neither is given, DHM still records cpu_possible_mask here so
+	 * that kernel-noise isolation can be enabled purely at runtime.
+	 */
+	HK_TYPE_KERNEL_NOISE_BOOT,
+	/*
+	 * A subset of HK_TYPE_KERNEL_NOISE_BOOT: it may exclude additional
+	 * CPUs isolated at runtime via cpuset isolated partitions.
+	 */
 	HK_TYPE_KERNEL_NOISE,
+
+	/* Inverse of the boot-time isolcpus=managed_irq argument */
+	HK_TYPE_MANAGED_IRQ_BOOT,
+	/*
+	 * A subset of HK_TYPE_MANAGED_IRQ_BOOT: it may exclude additional
+	 * CPUs isolated at runtime via cpuset isolated partitions.
+	 */
+	HK_TYPE_MANAGED_IRQ,
 	HK_TYPE_MAX,
 
 	/*
@@ -40,10 +56,13 @@ enum hk_type {
 DECLARE_STATIC_KEY_FALSE(housekeeping_overridden);
 extern int housekeeping_any_cpu(enum hk_type type);
 extern const struct cpumask *housekeeping_cpumask(enum hk_type type);
+extern const struct cpumask *housekeeping_cpumask_rcu(enum hk_type type);
 extern bool housekeeping_enabled(enum hk_type type);
 extern void housekeeping_affine(struct task_struct *t, enum hk_type type);
 extern bool housekeeping_test_cpu(int cpu, enum hk_type type);
 extern int housekeeping_update(struct cpumask *isol_mask);
+extern int housekeeping_update_types(unsigned long type_mask,
+				     struct cpumask *isol_mask);
 extern void __init housekeeping_init(void);
 
 #else
@@ -58,6 +77,11 @@ static inline const struct cpumask *housekeeping_cpumask(enum hk_type type)
 	return cpu_possible_mask;
 }
 
+static inline const struct cpumask *housekeeping_cpumask_rcu(enum hk_type type)
+{
+	return cpu_possible_mask;
+}
+
 static inline bool housekeeping_enabled(enum hk_type type)
 {
 	return false;
@@ -72,6 +96,8 @@ static inline bool housekeeping_test_cpu(int cpu, enum hk_type type)
 }
 
 static inline int housekeeping_update(struct cpumask *isol_mask) { return 0; }
+static inline int housekeeping_update_types(unsigned long type_mask,
+					    struct cpumask *isol_mask) { return 0; }
 static inline void housekeeping_init(void) { }
 #endif /* CONFIG_CPU_ISOLATION */
 
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index b8871449d3c69..79c3349f65bb4 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -5813,8 +5813,10 @@ static void sched_tick_stop(int cpu)
 }
 #endif /* CONFIG_HOTPLUG_CPU */
 
-int __init sched_tick_offload_init(void)
+int sched_tick_offload_init(void)
 {
+	if (tick_work_cpu)
+		return 0;
 	tick_work_cpu = alloc_percpu(struct tick_work);
 	BUG_ON(!tick_work_cpu);
 	return 0;
diff --git a/kernel/sched/isolation.c b/kernel/sched/isolation.c
index ef152d401fe20..4602c8d0108e4 100644
--- a/kernel/sched/isolation.c
+++ b/kernel/sched/isolation.c
@@ -12,10 +12,12 @@
 #include "sched.h"
 
 enum hk_flags {
-	HK_FLAG_DOMAIN_BOOT	= BIT(HK_TYPE_DOMAIN_BOOT),
-	HK_FLAG_DOMAIN		= BIT(HK_TYPE_DOMAIN),
-	HK_FLAG_MANAGED_IRQ	= BIT(HK_TYPE_MANAGED_IRQ),
-	HK_FLAG_KERNEL_NOISE	= BIT(HK_TYPE_KERNEL_NOISE),
+	HK_FLAG_DOMAIN_BOOT	  = BIT(HK_TYPE_DOMAIN_BOOT),
+	HK_FLAG_DOMAIN		  = BIT(HK_TYPE_DOMAIN),
+	HK_FLAG_KERNEL_NOISE_BOOT = BIT(HK_TYPE_KERNEL_NOISE_BOOT),
+	HK_FLAG_KERNEL_NOISE	  = BIT(HK_TYPE_KERNEL_NOISE),
+	HK_FLAG_MANAGED_IRQ_BOOT  = BIT(HK_TYPE_MANAGED_IRQ_BOOT),
+	HK_FLAG_MANAGED_IRQ	  = BIT(HK_TYPE_MANAGED_IRQ),
 };
 
 DEFINE_STATIC_KEY_FALSE(housekeeping_overridden);
@@ -34,25 +36,40 @@ bool housekeeping_enabled(enum hk_type type)
 }
 EXPORT_SYMBOL_GPL(housekeeping_enabled);
 
+/*
+ * Types that can change at runtime via cpuset isolated partitions.
+ * Boot-only types (DOMAIN_BOOT) are always safe to read without lockdep.
+ */
+static bool housekeeping_type_can_change(enum hk_type type)
+{
+	switch (type) {
+	case HK_TYPE_DOMAIN:
+	case HK_TYPE_KERNEL_NOISE:
+	case HK_TYPE_MANAGED_IRQ:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static bool housekeeping_dereference_check(enum hk_type type)
 {
-	if (IS_ENABLED(CONFIG_LOCKDEP) && type == HK_TYPE_DOMAIN) {
-		/* Cpuset isn't even writable yet? */
-		if (system_state <= SYSTEM_SCHEDULING)
-			return true;
+	if (!IS_ENABLED(CONFIG_LOCKDEP) || !housekeeping_type_can_change(type))
+		return true;
 
-		/* CPU hotplug write locked, so cpuset partition can't be overwritten */
-		if (IS_ENABLED(CONFIG_HOTPLUG_CPU) && lockdep_is_cpus_write_held())
-			return true;
+	/* Cpuset isn't even writable yet? */
+	if (system_state <= SYSTEM_SCHEDULING)
+		return true;
 
-		/* Cpuset lock held, partitions not writable */
-		if (IS_ENABLED(CONFIG_CPUSETS) && lockdep_is_cpuset_held())
-			return true;
+	/* CPU hotplug write locked, so cpuset partition can't be overwritten */
+	if (IS_ENABLED(CONFIG_HOTPLUG_CPU) && lockdep_is_cpus_write_held())
+		return true;
 
-		return false;
-	}
+	/* Cpuset lock held, partitions not writable */
+	if (IS_ENABLED(CONFIG_CPUSETS) && lockdep_is_cpuset_held())
+		return true;
 
-	return true;
+	return false;
 }
 
 static inline struct cpumask *housekeeping_cpumask_dereference(enum hk_type type)
@@ -75,12 +92,26 @@ const struct cpumask *housekeeping_cpumask(enum hk_type type)
 }
 EXPORT_SYMBOL_GPL(housekeeping_cpumask);
 
+const struct cpumask *housekeeping_cpumask_rcu(enum hk_type type)
+{
+	const struct cpumask *mask = NULL;
+
+	if (static_branch_unlikely(&housekeeping_overridden)) {
+		if (READ_ONCE(housekeeping.flags) & BIT(type))
+			mask = rcu_dereference(housekeeping.cpumasks[type]);
+	}
+	if (!mask)
+		mask = cpu_possible_mask;
+	return mask;
+}
+EXPORT_SYMBOL_GPL(housekeeping_cpumask_rcu);
+
 int housekeeping_any_cpu(enum hk_type type)
 {
 	int cpu;
 
 	if (static_branch_unlikely(&housekeeping_overridden)) {
-		if (housekeeping.flags & BIT(type)) {
+		if (READ_ONCE(housekeeping.flags) & BIT(type)) {
 			cpu = sched_numa_find_closest(housekeeping_cpumask(type), smp_processor_id());
 			if (cpu < nr_cpu_ids)
 				return cpu;
@@ -162,6 +193,130 @@ int housekeeping_update(struct cpumask *isol_mask)
 	return 0;
 }
 
+/**
+ * housekeeping_update_types - Update housekeeping masks for specified types
+ * @type_mask: Bitmask of housekeeping types to update
+ * @isol_mask: CPUs being added to the isolation set
+ *
+ * For each type in @type_mask, compute the trial mask as
+ * (boot snapshot & ~@isol_mask), validate it against @cpu_online_mask,
+ * then swap the RCU mask pointer and free the old mask after
+ * synchronize_rcu().  Anchoring on the immutable boot snapshot
+ * (HK_TYPE_*_BOOT) keeps the runtime mask a subset of the boot set and
+ * lets de-isolation restore the boot configuration exactly.
+ *
+ * The updated mask only takes effect for subsystems as CPUs cycle
+ * through hotplug; callers isolate CPUs via the CPU hotplug machinery so
+ * that tick, RCU and interrupt state is reconfigured by the existing
+ * online/offline callbacks rather than reconfigured in place.
+ *
+ * HK_TYPE_KERNEL_NOISE also supports runtime first-enable: when neither
+ * nohz_full= nor isolcpus=nohz was given at boot, no boot snapshot
+ * exists, so cpu_possible_mask is the implicit boot set and the type
+ * flag is set in housekeeping.flags on the first call.
+ *
+ * Return: 0 on success, -ENOMEM on allocation failure, -EINVAL if
+ * a trial mask has no online CPUs.
+ */
+int housekeeping_update_types(unsigned long type_mask,
+			      struct cpumask *isol_mask)
+{
+	struct cpumask *trials[HK_TYPE_MAX] = {};
+	struct cpumask *old_masks[HK_TYPE_MAX] = {};
+	enum hk_type type;
+	int ret = 0;
+
+	for_each_set_bit(type, &type_mask, HK_TYPE_MAX) {
+		const struct cpumask *base;
+
+		if (type == HK_TYPE_DOMAIN_BOOT)
+			continue;
+		if (!housekeeping_enabled(type)) {
+			/*
+			 * HK_TYPE_KERNEL_NOISE supports runtime first-enable
+			 * for DHM isolated partitions created without nohz_full=
+			 * at boot.  All other types must be boot-enabled.
+			 */
+			if (type != HK_TYPE_KERNEL_NOISE)
+				continue;
+		}
+
+		/*
+		 * Compute the trial mask relative to the immutable boot
+		 * snapshot, never relative to the current (already shrunk)
+		 * mask.  Using the current mask would let it shrink
+		 * monotonically across isolation/de-isolation cycles and would
+		 * never recover CPUs once de-isolated.  Anchoring on the boot
+		 * snapshot keeps the runtime mask a subset of the boot set and
+		 * lets de-isolation restore exactly the boot configuration.
+		 *
+		 * HK_TYPE_KERNEL_NOISE additionally supports runtime
+		 * first-enable: when no nohz_full=/isolcpus=nohz was given at
+		 * boot, no boot snapshot exists, so cpu_possible_mask is the
+		 * implicit boot set.
+		 */
+		if (type == HK_TYPE_KERNEL_NOISE &&
+		    !(housekeeping.flags & HK_FLAG_KERNEL_NOISE_BOOT))
+			base = cpu_possible_mask;
+		else if (type == HK_TYPE_KERNEL_NOISE)
+			base = housekeeping_cpumask(HK_TYPE_KERNEL_NOISE_BOOT);
+		else if (type == HK_TYPE_MANAGED_IRQ)
+			base = housekeeping_cpumask(HK_TYPE_MANAGED_IRQ_BOOT);
+		else
+			base = housekeeping_cpumask(type);
+		trials[type] = kmalloc(cpumask_size(), GFP_KERNEL);
+		if (!trials[type]) {
+			ret = -ENOMEM;
+			goto err_free;
+		}
+		cpumask_andnot(trials[type], base, isol_mask);
+		if (!cpumask_intersects(trials[type], cpu_online_mask)) {
+			ret = -EINVAL;
+			goto err_free;
+		}
+	}
+
+	if (!housekeeping.flags) {
+		ret = -EINVAL;
+		goto err_free;
+	}
+
+	for_each_set_bit(type, &type_mask, HK_TYPE_MAX) {
+		if (!trials[type])
+			continue;
+		old_masks[type] = housekeeping_cpumask_dereference(type);
+		/* First-time runtime enable: register the type now. */
+		if (!housekeeping_enabled(type)) {
+			WRITE_ONCE(housekeeping.flags,
+				   housekeeping.flags | BIT(type));
+			/*
+			 * HK_TYPE_KERNEL_NOISE first-enable at runtime
+			 * (zero-boot-param path): tick offload percpu data
+			 * was never allocated at boot since nohz_full= was
+			 * absent.  Allocate it now before CPUs cycle through
+			 * hotplug and sched_tick_stop() dereferences
+			 * tick_work_cpu.
+			 */
+			if (type == HK_TYPE_KERNEL_NOISE)
+				WARN_ON_ONCE(sched_tick_offload_init());
+		}
+		rcu_assign_pointer(housekeeping.cpumasks[type], trials[type]);
+		trials[type] = NULL;
+	}
+
+	synchronize_rcu();
+
+	for_each_set_bit(type, &type_mask, HK_TYPE_MAX)
+		kfree(old_masks[type]);
+
+	return 0;
+
+err_free:
+	for_each_set_bit(type, &type_mask, HK_TYPE_MAX)
+		kfree(trials[type]);
+	return ret;
+}
+
 void __init housekeeping_init(void)
 {
 	enum hk_type type;
@@ -305,7 +460,7 @@ static int __init housekeeping_nohz_full_setup(char *str)
 {
 	unsigned long flags;
 
-	flags = HK_FLAG_KERNEL_NOISE;
+	flags = HK_FLAG_KERNEL_NOISE | HK_FLAG_KERNEL_NOISE_BOOT;
 
 	return housekeeping_setup(str, flags);
 }
@@ -324,7 +479,7 @@ static int __init housekeeping_isolcpus_setup(char *str)
 		 */
 		if (!strncmp(str, "nohz,", 5)) {
 			str += 5;
-			flags |= HK_FLAG_KERNEL_NOISE;
+			flags |= HK_FLAG_KERNEL_NOISE | HK_FLAG_KERNEL_NOISE_BOOT;
 			continue;
 		}
 
@@ -336,7 +491,7 @@ static int __init housekeeping_isolcpus_setup(char *str)
 
 		if (!strncmp(str, "managed_irq,", 12)) {
 			str += 12;
-			flags |= HK_FLAG_MANAGED_IRQ;
+			flags |= HK_FLAG_MANAGED_IRQ | HK_FLAG_MANAGED_IRQ_BOOT;
 			continue;
 		}
 
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 9f63b15d309d1..9d54ac3f2251d 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2925,7 +2925,7 @@ extern void post_init_entity_util_avg(struct task_struct *p);
 
 #ifdef CONFIG_NO_HZ_FULL
 extern bool sched_can_stop_tick(struct rq *rq);
-extern int __init sched_tick_offload_init(void);
+extern int sched_tick_offload_init(void);
 
 /*
  * Tick may be needed by tasks in the runqueue depending on their policy and

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 02/11] sched/isolation: RCU-protect runtime-mutable housekeeping cpumask readers
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
  2026-07-10  3:28 ` [PATCH v4 01/11] sched/isolation: Add runtime housekeeping mask updates with boot snapshots Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 03/11] cgroup/cpuset: Drive kernel-noise housekeeping updates from isolated partitions Jing Wu
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

Now that HK_TYPE_KERNEL_NOISE and HK_TYPE_MANAGED_IRQ can be updated at
runtime, their cpumask pointers are swapped and the old masks freed after
an RCU grace period.  Readers that dereference these masks must do so
inside an RCU read-side critical section, otherwise the mask can be freed
while it is still in use.

Convert the runtime-mutable readers to housekeeping_cpumask_rcu() under
rcu_read_lock():

  - get_nohz_timer_target() (HK_TYPE_KERNEL_NOISE)
  - hrtimer target selection (HK_TYPE_TIMER)
  - arm64 topology (HK_TYPE_TICK)
  - Hyper-V channel management (HK_TYPE_MANAGED_IRQ)
  - the housekeeping sysfs attribute (HK_TYPE_KERNEL_NOISE)

The watchdog boot-time cpumask initialisation is switched from the
HK_TYPE_TIMER alias to HK_TYPE_KERNEL_NOISE for consistency; both alias
the same value.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 arch/arm64/kernel/topology.c |  9 ++++++--
 drivers/base/cpu.c           | 20 +++++++++++++-----
 drivers/hv/channel_mgmt.c    | 50 ++++++++++++++++++++++++++++++--------------
 kernel/sched/core.c          |  3 +--
 kernel/time/hrtimer.c        |  5 ++++-
 kernel/watchdog.c            |  2 +-
 6 files changed, 62 insertions(+), 27 deletions(-)

diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index b32f13358fbb1..8f4329b57cea7 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -212,8 +212,13 @@ int arch_freq_get_on_cpu(int cpu)
 			if (!policy)
 				return -EINVAL;
 
-			if (!cpumask_intersects(policy->related_cpus,
-						housekeeping_cpumask(HK_TYPE_TICK))) {
+			bool no_hk_in_policy;
+
+			rcu_read_lock();
+			no_hk_in_policy = !cpumask_intersects(policy->related_cpus,
+							      housekeeping_cpumask_rcu(HK_TYPE_TICK));
+			rcu_read_unlock();
+			if (no_hk_in_policy) {
 				cpufreq_cpu_put(policy);
 				return -EOPNOTSUPP;
 			}
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 875abdc9942e1..e0aa3d34bc41a 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -303,13 +303,23 @@ static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL);
 static ssize_t housekeeping_show(struct device *dev,
 			     struct device_attribute *attr, char *buf)
 {
-	const struct cpumask *hk_mask;
+	ssize_t len;
 
-	hk_mask = housekeeping_cpumask(HK_TYPE_KERNEL_NOISE);
+	if (!housekeeping_enabled(HK_TYPE_KERNEL_NOISE))
+		return sysfs_emit(buf, "\n");
 
-	if (housekeeping_enabled(HK_TYPE_KERNEL_NOISE))
-		return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(hk_mask));
-	return sysfs_emit(buf, "\n");
+	/*
+	 * HK_TYPE_KERNEL_NOISE is runtime-mutable: the mask pointer can be
+	 * swapped and the old mask freed after an RCU grace period.  Hold the
+	 * RCU read lock across the dereference and the format so the mask
+	 * cannot be freed while it is being printed.
+	 */
+	rcu_read_lock();
+	len = sysfs_emit(buf, "%*pbl\n",
+			 cpumask_pr_args(housekeeping_cpumask_rcu(HK_TYPE_KERNEL_NOISE)));
+	rcu_read_unlock();
+
+	return len;
 }
 static DEVICE_ATTR_RO(housekeeping);
 
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 84eb0a6a0b546..fc5247e92e1b3 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -750,26 +750,43 @@ static void init_vp_index(struct vmbus_channel *channel)
 {
 	bool perf_chn = hv_is_perf_channel(channel);
 	u32 i, ncpu = num_online_cpus();
-	cpumask_var_t available_mask;
+	cpumask_var_t available_mask, hk_snap;
 	struct cpumask *allocated_mask;
-	const struct cpumask *hk_mask = housekeeping_cpumask(HK_TYPE_MANAGED_IRQ);
 	u32 target_cpu;
 	int numa_node;
 
-	if (!perf_chn ||
-	    !alloc_cpumask_var(&available_mask, GFP_KERNEL) ||
-	    cpumask_empty(hk_mask)) {
-		/*
-		 * If the channel is not a performance critical
-		 * channel, bind it to VMBUS_CONNECT_CPU.
-		 * In case alloc_cpumask_var() fails, bind it to
-		 * VMBUS_CONNECT_CPU.
-		 * If all the cpus are isolated, bind it to
-		 * VMBUS_CONNECT_CPU.
-		 */
+	if (!perf_chn) {
+		channel->target_cpu = VMBUS_CONNECT_CPU;
+		return;
+	}
+
+	if (!alloc_cpumask_var(&available_mask, GFP_KERNEL)) {
+		channel->target_cpu = VMBUS_CONNECT_CPU;
+		hv_set_allocated_cpu(VMBUS_CONNECT_CPU);
+		return;
+	}
+
+	/*
+	 * Snapshot HK_TYPE_MANAGED_IRQ cpumask under RCU read lock.
+	 * housekeeping_update_types() frees the old cpumask after
+	 * synchronize_rcu(), so we must not hold the pointer beyond an
+	 * RCU read-side critical section.
+	 */
+	if (!alloc_cpumask_var(&hk_snap, GFP_KERNEL)) {
+		free_cpumask_var(available_mask);
+		channel->target_cpu = VMBUS_CONNECT_CPU;
+		hv_set_allocated_cpu(VMBUS_CONNECT_CPU);
+		return;
+	}
+	rcu_read_lock();
+	cpumask_copy(hk_snap, housekeeping_cpumask_rcu(HK_TYPE_MANAGED_IRQ));
+	rcu_read_unlock();
+
+	if (cpumask_empty(hk_snap)) {
+		free_cpumask_var(hk_snap);
+		free_cpumask_var(available_mask);
 		channel->target_cpu = VMBUS_CONNECT_CPU;
-		if (perf_chn)
-			hv_set_allocated_cpu(VMBUS_CONNECT_CPU);
+		hv_set_allocated_cpu(VMBUS_CONNECT_CPU);
 		return;
 	}
 
@@ -788,7 +805,7 @@ static void init_vp_index(struct vmbus_channel *channel)
 
 retry:
 		cpumask_xor(available_mask, allocated_mask, cpumask_of_node(numa_node));
-		cpumask_and(available_mask, available_mask, hk_mask);
+		cpumask_and(available_mask, available_mask, hk_snap);
 
 		if (cpumask_empty(available_mask)) {
 			/*
@@ -809,6 +826,7 @@ static void init_vp_index(struct vmbus_channel *channel)
 
 	channel->target_cpu = target_cpu;
 
+	free_cpumask_var(hk_snap);
 	free_cpumask_var(available_mask);
 }
 
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 79c3349f65bb4..3eb39103f5469 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1272,9 +1272,8 @@ int get_nohz_timer_target(void)
 		default_cpu = cpu;
 	}
 
-	hk_mask = housekeeping_cpumask(HK_TYPE_KERNEL_NOISE);
-
 	guard(rcu)();
+	hk_mask = housekeeping_cpumask_rcu(HK_TYPE_KERNEL_NOISE);
 
 	for_each_domain(cpu, sd) {
 		for_each_cpu_and(i, sched_domain_span(sd), hk_mask) {
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 5bd6efe598f0f..18e17a9dad67b 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -242,8 +242,11 @@ static bool hrtimer_suitable_target(struct hrtimer *timer, struct hrtimer_clock_
 static inline struct hrtimer_cpu_base *get_target_base(struct hrtimer_cpu_base *base, bool pinned)
 {
 	if (!hrtimer_base_is_online(base)) {
-		int cpu = cpumask_any_and(cpu_online_mask, housekeeping_cpumask(HK_TYPE_TIMER));
+		int cpu;
 
+		rcu_read_lock();
+		cpu = cpumask_any_and(cpu_online_mask, housekeeping_cpumask_rcu(HK_TYPE_TIMER));
+		rcu_read_unlock();
 		return &per_cpu(hrtimer_bases, cpu);
 	}
 
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 87dd5e0f6968d..c18c3e9781d7b 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -1389,7 +1389,7 @@ void __init lockup_detector_init(void)
 		pr_info("Disabling watchdog on nohz_full cores by default\n");
 
 	cpumask_copy(&watchdog_cpumask,
-		     housekeeping_cpumask(HK_TYPE_TIMER));
+		     housekeeping_cpumask(HK_TYPE_KERNEL_NOISE));
 
 	if (!watchdog_hardlockup_probe())
 		watchdog_hardlockup_available = true;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 03/11] cgroup/cpuset: Drive kernel-noise housekeeping updates from isolated partitions
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
  2026-07-10  3:28 ` [PATCH v4 01/11] sched/isolation: Add runtime housekeeping mask updates with boot snapshots Jing Wu
  2026-07-10  3:28 ` [PATCH v4 02/11] sched/isolation: RCU-protect runtime-mutable housekeeping cpumask readers Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 04/11] context_tracking: allow runtime per-CPU user tracking enable/disable Jing Wu
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

An isolated cpuset partition already updates the HK_TYPE_DOMAIN
housekeeping mask.  Extend it to also update the kernel-noise masks
(HK_TYPE_KERNEL_NOISE and HK_TYPE_MANAGED_IRQ) so that creating or
destroying an isolated partition reconfigures the full set of
housekeeping cpumasks.

The sched domain mask is updated first because the workqueue flush and
timer migration paths depend on it; the kernel-noise masks are updated
afterwards via housekeeping_update_types().

housekeeping_update() and housekeeping_update_types() are called after
dropping cpus_read_lock and cpuset_mutex, with only cpuset_top_mutex held
for mutual exclusion.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 kernel/cgroup/cpuset.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 5c33ab20cc208..80f43a24d3c8a 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1347,17 +1347,36 @@ static void cpuset_update_sd_hk_unlock(void)
 		rebuild_sched_domains_locked();
 
 	if (update_housekeeping) {
+		static const unsigned long noise_types =
+			BIT(HK_TYPE_KERNEL_NOISE) | BIT(HK_TYPE_MANAGED_IRQ);
+
 		update_housekeeping = false;
 		cpumask_copy(isolated_hk_cpus, isolated_cpus);
 
+		mutex_unlock(&cpuset_mutex);
+		cpus_read_unlock();
+
 		/*
 		 * housekeeping_update() is now called without holding
 		 * cpus_read_lock and cpuset_mutex. Only cpuset_top_mutex
 		 * is still being held for mutual exclusion.
 		 */
-		mutex_unlock(&cpuset_mutex);
-		cpus_read_unlock();
+
+		/*
+		 * Update the sched domain mask first; it must succeed
+		 * before the kernel-noise types because workqueue flush
+		 * and timer migration depend on the sched domain mask.
+		 */
 		WARN_ON_ONCE(housekeeping_update(isolated_hk_cpus));
+
+		/*
+		 * Update the kernel-noise housekeeping masks
+		 * (HK_TYPE_KERNEL_NOISE and HK_TYPE_MANAGED_IRQ).  The tick,
+		 * RCU and managed-interrupt state is reconfigured as the
+		 * affected CPUs are cycled through the CPU hotplug machinery.
+		 */
+		WARN_ON_ONCE(housekeeping_update_types(noise_types,
+						       isolated_hk_cpus));
 		mutex_unlock(&cpuset_top_mutex);
 	} else {
 		cpuset_full_unlock();

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 04/11] context_tracking: allow runtime per-CPU user tracking enable/disable
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
                   ` (2 preceding siblings ...)
  2026-07-10  3:28 ` [PATCH v4 03/11] cgroup/cpuset: Drive kernel-noise housekeeping updates from isolated partitions Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 05/11] rcu/nocb: support lazy init for runtime CPU isolation Jing Wu
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

ct_cpu_track_user() and the context_tracking_key static key are currently
restricted to boot-time use: the key is __ro_after_init and the function
is __init with __initdata state.  This prevents enabling nohz_full context
tracking for CPUs isolated at runtime via cpuset partitions.

Split ct_cpu_track_user() into three functions:

  ct_cpu_track_user(cpu)      - sets per_cpu(context_tracking.active) and
                                increments context_tracking_key; callable
                                at runtime with the CPU offline.

  ct_cpu_untrack_user(cpu)    - reverses the above; for de-isolation.

  ct_cpu_track_user_init(cpu) - __init wrapper; calls ct_cpu_track_user()
                                and handles TIF_NOHZ / tasklist setup.

Change context_tracking_key from DEFINE_STATIC_KEY_FALSE_RO to
DEFINE_STATIC_KEY_FALSE so that static_branch_inc/dec() can be called
after the __ro_after_init window closes.

Update tick_nohz_init() to call ct_cpu_track_user_init() so boot
behaviour is unchanged.

This is a prerequisite for DHM (Dynamic Housekeeping Management) runtime
CPU noise isolation without boot parameters.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 include/linux/context_tracking.h |  2 ++
 kernel/context_tracking.c        | 38 ++++++++++++++++++++++++++++++++++----
 kernel/time/tick-sched.c         |  2 +-
 3 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h
index af9fe87a09225..735d353d87560 100644
--- a/include/linux/context_tracking.h
+++ b/include/linux/context_tracking.h
@@ -12,6 +12,8 @@
 
 #ifdef CONFIG_CONTEXT_TRACKING_USER
 extern void ct_cpu_track_user(int cpu);
+extern void ct_cpu_untrack_user(int cpu);
+extern void __init ct_cpu_track_user_init(int cpu);
 
 /* Called with interrupts disabled.  */
 extern void __ct_user_enter(enum ctx_state state);
diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c
index a743e7ffa6c00..a81d9f8b85eed 100644
--- a/kernel/context_tracking.c
+++ b/kernel/context_tracking.c
@@ -411,7 +411,7 @@ static __always_inline void ct_kernel_enter(bool user, int offset) { }
 #define CREATE_TRACE_POINTS
 #include <trace/events/context_tracking.h>
 
-DEFINE_STATIC_KEY_FALSE_RO(context_tracking_key);
+DEFINE_STATIC_KEY_FALSE(context_tracking_key);
 EXPORT_SYMBOL_GPL(context_tracking_key);
 
 static noinstr bool context_tracking_recursion_enter(void)
@@ -674,14 +674,44 @@ void user_exit_callable(void)
 }
 NOKPROBE_SYMBOL(user_exit_callable);
 
-void __init ct_cpu_track_user(int cpu)
+/**
+ * ct_cpu_track_user - enable context tracking for a CPU
+ * @cpu: target CPU (must be offline when called at runtime)
+ *
+ * Marks @cpu as actively tracking user/kernel transitions and increments
+ * the context_tracking_key refcount.  Safe to call at runtime provided
+ * the CPU is offline so no context-tracking readers are active on it.
+ */
+void ct_cpu_track_user(int cpu)
 {
-	static __initdata bool initialized = false;
-
 	if (!per_cpu(context_tracking.active, cpu)) {
 		per_cpu(context_tracking.active, cpu) = true;
 		static_branch_inc(&context_tracking_key);
 	}
+}
+EXPORT_SYMBOL_GPL(ct_cpu_track_user);
+
+/**
+ * ct_cpu_untrack_user - disable context tracking for a CPU
+ * @cpu: target CPU (must be offline when called)
+ *
+ * Reverses ct_cpu_track_user().  The CPU must be offline so that no
+ * context-tracking readers are active on it.
+ */
+void ct_cpu_untrack_user(int cpu)
+{
+	if (per_cpu(context_tracking.active, cpu)) {
+		per_cpu(context_tracking.active, cpu) = false;
+		static_branch_dec(&context_tracking_key);
+	}
+}
+EXPORT_SYMBOL_GPL(ct_cpu_untrack_user);
+
+void __init ct_cpu_track_user_init(int cpu)
+{
+	static __initdata bool initialized = false;
+
+	ct_cpu_track_user(cpu);
 
 	if (initialized)
 		return;
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index cbbb87a0c6e7c..ba7adc671c580 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -677,7 +677,7 @@ void __init tick_nohz_init(void)
 	}
 
 	for_each_cpu(cpu, tick_nohz_full_mask)
-		ct_cpu_track_user(cpu);
+		ct_cpu_track_user_init(cpu);
 
 	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
 					"kernel/nohz:predown", NULL,

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 05/11] rcu/nocb: support lazy init for runtime CPU isolation
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
                   ` (3 preceding siblings ...)
  2026-07-10  3:28 ` [PATCH v4 04/11] context_tracking: allow runtime per-CPU user tracking enable/disable Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 06/11] watchdog: sync watchdog_cpumask with HK_TYPE_KERNEL_NOISE on isolation Jing Wu
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

When a cpuset isolated partition first requests kernel-noise isolation,
it needs to offload RCU callbacks from the affected CPUs. The existing
rcu_nocb_cpu_offload() requires rcu_nocbs= or nohz_full= at boot so
that rcu_organize_nocb_kthreads() has already set rdp->nocb_gp_rdp for
each CPU. Without a boot parameter, nocb_gp_rdp is NULL and offload
fails immediately.

Introduce lazy nocb initialization so that the first call into the
isolation path triggers the one-time setup automatically:

  rcu_nocb_lazy_init() - allocates rcu_nocb_mask and calls
    rcu_organize_nocb_kthreads() (now without __init) to set
    nocb_gp_rdp for every possible CPU. Uses a dedicated mutex
    for serialization with a fast-path read of nocb_is_setup.

  rcu_nocb_cpu_isolate() - exported entry point called per-CPU
    while the CPU is offline. Calls rcu_nocb_lazy_init() for the
    one-time setup, spawns the GP and CB kthreads via the existing
    rcu_spawn_cpu_nocb_kthread(), then finalizes offload through
    rcu_nocb_cpu_offload(). Adding the CPU to the GP kthread's
    nocb_head_rdp list is handled by nocb_gp_toggle_rdp() in the
    GP kthread, so rcu_organize_nocb_kthreads() can run with an
    empty mask.

Remove __init from rcu_organize_nocb_kthreads() to allow this
runtime call path; the function itself has no __initdata dependencies.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 include/linux/rcupdate.h |  2 ++
 kernel/rcu/tree.h        |  2 +-
 kernel/rcu/tree_nocb.h   | 43 ++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index bfa765132de85..5937485d59118 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -149,6 +149,7 @@ static __always_inline void rcu_irq_work_resched(void) { }
 void rcu_init_nohz(void);
 int rcu_nocb_cpu_offload(int cpu);
 int rcu_nocb_cpu_deoffload(int cpu);
+int rcu_nocb_cpu_isolate(int cpu);
 void rcu_nocb_flush_deferred_wakeup(void);
 
 #define RCU_NOCB_LOCKDEP_WARN(c, s) RCU_LOCKDEP_WARN(c, s)
@@ -158,6 +159,7 @@ void rcu_nocb_flush_deferred_wakeup(void);
 static inline void rcu_init_nohz(void) { }
 static inline int rcu_nocb_cpu_offload(int cpu) { return -EINVAL; }
 static inline int rcu_nocb_cpu_deoffload(int cpu) { return 0; }
+static inline int rcu_nocb_cpu_isolate(int cpu) { return -EINVAL; }
 static inline void rcu_nocb_flush_deferred_wakeup(void) { }
 
 #define RCU_NOCB_LOCKDEP_WARN(c, s)
diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h
index 7dfc57e9adb18..f3d31918ea322 100644
--- a/kernel/rcu/tree.h
+++ b/kernel/rcu/tree.h
@@ -517,7 +517,7 @@ static void rcu_nocb_unlock_irqrestore(struct rcu_data *rdp,
 				       unsigned long flags);
 static void rcu_lockdep_assert_cblist_protected(struct rcu_data *rdp);
 #ifdef CONFIG_RCU_NOCB_CPU
-static void __init rcu_organize_nocb_kthreads(void);
+static void rcu_organize_nocb_kthreads(void);
 
 /*
  * Disable IRQs before checking offloaded state so that local
diff --git a/kernel/rcu/tree_nocb.h b/kernel/rcu/tree_nocb.h
index 1047b30cd46b7..694fd615f1809 100644
--- a/kernel/rcu/tree_nocb.h
+++ b/kernel/rcu/tree_nocb.h
@@ -1347,6 +1347,47 @@ void __init rcu_init_nohz(void)
 	rcu_organize_nocb_kthreads();
 }
 
+static DEFINE_MUTEX(rcu_nocb_lazy_mutex);
+
+/*
+ * Lazily initialize nocb infrastructure on the first call. Allocates
+ * rcu_nocb_mask and sets nocb_gp_rdp for every possible CPU so that
+ * rcu_nocb_cpu_isolate() can offload callbacks without rcu_nocbs= at boot.
+ */
+static noinline int rcu_nocb_lazy_init(void)
+{
+	if (rcu_state.nocb_is_setup)
+		return 0;
+
+	mutex_lock(&rcu_nocb_lazy_mutex);
+	if (!rcu_state.nocb_is_setup) {
+		if (!zalloc_cpumask_var(&rcu_nocb_mask, GFP_KERNEL)) {
+			mutex_unlock(&rcu_nocb_lazy_mutex);
+			return -ENOMEM;
+		}
+		rcu_organize_nocb_kthreads();
+		rcu_state.nocb_is_setup = true;
+	}
+	mutex_unlock(&rcu_nocb_lazy_mutex);
+	return 0;
+}
+
+/*
+ * Offload RCU callbacks for a CPU entering a kernel-noise isolated partition.
+ * @cpu must be offline. Lazily initializes nocb infrastructure on first use.
+ */
+int rcu_nocb_cpu_isolate(int cpu)
+{
+	int ret;
+
+	ret = rcu_nocb_lazy_init();
+	if (ret)
+		return ret;
+	rcu_spawn_cpu_nocb_kthread(cpu);
+	return rcu_nocb_cpu_offload(cpu);
+}
+EXPORT_SYMBOL_GPL(rcu_nocb_cpu_isolate);
+
 /* Initialize per-rcu_data variables for no-CBs CPUs. */
 static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp)
 {
@@ -1439,7 +1480,7 @@ module_param(rcu_nocb_gp_stride, int, 0444);
 /*
  * Initialize GP-CB relationships for all no-CBs CPU.
  */
-static void __init rcu_organize_nocb_kthreads(void)
+static void rcu_organize_nocb_kthreads(void)
 {
 	int cpu;
 	bool firsttime = true;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 06/11] watchdog: sync watchdog_cpumask with HK_TYPE_KERNEL_NOISE on isolation
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
                   ` (4 preceding siblings ...)
  2026-07-10  3:28 ` [PATCH v4 05/11] rcu/nocb: support lazy init for runtime CPU isolation Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 07/11] tick/nohz: add runtime tick_nohz_full_mask update for CPU isolation Jing Wu
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

The watchdog is initialized at boot to run on all housekeeping CPUs
(HK_TYPE_KERNEL_NOISE). When a cpuset isolated partition removes CPUs
from that mask at runtime, watchdog continues running on those CPUs
because nothing updates watchdog_cpumask.

Save the boot-time watchdog_cpumask as watchdog_cpumask_boot, which
captures the user's intended coverage (possibly narrowed via kernel
parameter or sysctl) before any runtime isolation. Introduce
lockup_detector_hk_update() which intersects this boot snapshot with
the current HK_TYPE_KERNEL_NOISE mask and reconfigures the detector.
This ensures that isolated CPUs are excluded while honoring any
manual narrowing the admin applied at or after boot.

lockup_detector_hk_update() snapshots the RCU-protected housekeeping
mask under rcu_read_lock(), then updates watchdog_cpumask and calls
__lockup_detector_reconfigure() under watchdog_mutex, matching the
same locking discipline used by proc_watchdog_cpumask().

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 include/linux/nmi.h |  2 ++
 kernel/watchdog.c   | 24 ++++++++++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/include/linux/nmi.h b/include/linux/nmi.h
index bc1162895f355..0bbe562de67b7 100644
--- a/include/linux/nmi.h
+++ b/include/linux/nmi.h
@@ -37,6 +37,7 @@ extern int sysctl_hardlockup_all_cpu_backtrace;
 static inline void lockup_detector_init(void) { }
 static inline void lockup_detector_retry_init(void) { }
 static inline void lockup_detector_soft_poweroff(void) { }
+static inline void lockup_detector_hk_update(void) { }
 #endif /* !CONFIG_LOCKUP_DETECTOR */
 
 #ifdef CONFIG_SOFTLOCKUP_DETECTOR
@@ -120,6 +121,7 @@ void watchdog_hardlockup_enable(unsigned int cpu);
 void watchdog_hardlockup_disable(unsigned int cpu);
 
 void lockup_detector_reconfigure(void);
+void lockup_detector_hk_update(void);
 
 #ifdef CONFIG_HARDLOCKUP_DETECTOR_BUDDY
 void watchdog_buddy_check_hardlockup(int hrtimer_interrupts);
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index c18c3e9781d7b..26463f6d3a39d 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -53,6 +53,8 @@ static int __read_mostly watchdog_hardlockup_available;
 
 struct cpumask watchdog_cpumask __read_mostly;
 unsigned long *watchdog_cpumask_bits = cpumask_bits(&watchdog_cpumask);
+/* Boot snapshot: user's intended watchdog mask before any runtime isolation. */
+static struct cpumask watchdog_cpumask_boot __ro_after_init;
 
 #ifdef CONFIG_HARDLOCKUP_DETECTOR
 
@@ -1348,6 +1350,27 @@ static void __init lockup_detector_delay_init(struct work_struct *work)
 	lockup_detector_setup();
 }
 
+void lockup_detector_hk_update(void)
+{
+	cpumask_var_t new_mask;
+
+	if (!alloc_cpumask_var(&new_mask, GFP_KERNEL))
+		return;
+
+	rcu_read_lock();
+	cpumask_and(new_mask, &watchdog_cpumask_boot,
+		    housekeeping_cpumask_rcu(HK_TYPE_KERNEL_NOISE));
+	rcu_read_unlock();
+
+	mutex_lock(&watchdog_mutex);
+	cpumask_copy(&watchdog_cpumask, new_mask);
+	__lockup_detector_reconfigure(false);
+	mutex_unlock(&watchdog_mutex);
+
+	free_cpumask_var(new_mask);
+}
+EXPORT_SYMBOL_GPL(lockup_detector_hk_update);
+
 /*
  * lockup_detector_retry_init - retry init lockup detector if possible.
  *
@@ -1390,6 +1413,7 @@ void __init lockup_detector_init(void)
 
 	cpumask_copy(&watchdog_cpumask,
 		     housekeeping_cpumask(HK_TYPE_KERNEL_NOISE));
+	cpumask_copy(&watchdog_cpumask_boot, &watchdog_cpumask);
 
 	if (!watchdog_hardlockup_probe())
 		watchdog_hardlockup_available = true;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 07/11] tick/nohz: add runtime tick_nohz_full_mask update for CPU isolation
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
                   ` (5 preceding siblings ...)
  2026-07-10  3:28 ` [PATCH v4 06/11] watchdog: sync watchdog_cpumask with HK_TYPE_KERNEL_NOISE on isolation Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 08/11] cpuset: add dhm_cycling_cpus mask to suppress transient invalidation Jing Wu
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

When kernel-noise isolation is requested for a CPU at runtime via a
cpuset isolated partition, tick suppression must be activated on the
affected CPU without requiring nohz_full= at boot.

tick_nohz_full_mask and tick_nohz_full_running are currently set only
during boot by tick_nohz_full_setup(). There is no runtime path to add
or remove CPUs from the full-dynticks set.

Add tick_nohz_cpu_isolate() and tick_nohz_cpu_deisolate(), both called
with the target CPU offline (between remove_cpu and add_cpu in the
hotplug cycling path):

  tick_nohz_cpu_isolate(cpu) - lazily allocates tick_nohz_full_mask
    if it was never set up at boot, sets the CPU's bit in the mask,
    sets tick_nohz_full_running, and calls ct_cpu_track_user() to
    activate per-CPU context tracking so kernel/user transitions
    suppress the scheduler tick.

  tick_nohz_cpu_deisolate(cpu) - reverses the above: deactivates
    context tracking via ct_cpu_untrack_user(), clears the CPU's bit,
    and clears tick_nohz_full_running when the mask becomes empty.

A per-function mutex guards the lazy allocation and running flag
updates against concurrent isolation requests.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 include/linux/tick.h     |  4 ++++
 kernel/time/tick-sched.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/include/linux/tick.h b/include/linux/tick.h
index 738007d6f577f..c4038e62533ba 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -210,6 +210,8 @@ extern void tick_nohz_dep_set_signal(struct task_struct *tsk,
 extern void tick_nohz_dep_clear_signal(struct signal_struct *signal,
 				       enum tick_dep_bits bit);
 extern bool tick_nohz_cpu_hotpluggable(unsigned int cpu);
+extern int tick_nohz_cpu_isolate(int cpu);
+extern void tick_nohz_cpu_deisolate(int cpu);
 
 /*
  * The below are tick_nohz_[set,clear]_dep() wrappers that optimize off-cases
@@ -281,6 +283,8 @@ static inline bool tick_nohz_full_cpu(int cpu) { return false; }
 static inline void tick_nohz_dep_set_cpu(int cpu, enum tick_dep_bits bit) { }
 static inline void tick_nohz_dep_clear_cpu(int cpu, enum tick_dep_bits bit) { }
 static inline bool tick_nohz_cpu_hotpluggable(unsigned int cpu) { return true; }
+static inline int tick_nohz_cpu_isolate(int cpu) { return -EINVAL; }
+static inline void tick_nohz_cpu_deisolate(int cpu) { }
 
 static inline void tick_dep_set(enum tick_dep_bits bit) { }
 static inline void tick_dep_clear(enum tick_dep_bits bit) { }
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index ba7adc671c580..1bc01e2ab525b 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -621,6 +621,51 @@ void __tick_nohz_task_switch(void)
 	}
 }
 
+static DEFINE_MUTEX(tick_nohz_cpu_isolate_mutex);
+
+/*
+ * tick_nohz_cpu_isolate - Add a CPU to the full-dynticks set at runtime.
+ * @cpu: the CPU to isolate; must be offline.
+ *
+ * Lazily allocates tick_nohz_full_mask on the first call so that no
+ * nohz_full= boot parameter is required.  Activates per-CPU context
+ * tracking so that kernel/user transitions suppress the scheduler tick.
+ */
+int tick_nohz_cpu_isolate(int cpu)
+{
+	mutex_lock(&tick_nohz_cpu_isolate_mutex);
+	if (!cpumask_available(tick_nohz_full_mask)) {
+		if (!zalloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL)) {
+			mutex_unlock(&tick_nohz_cpu_isolate_mutex);
+			return -ENOMEM;
+		}
+	}
+	cpumask_set_cpu(cpu, tick_nohz_full_mask);
+	if (!tick_nohz_full_running)
+		tick_nohz_full_running = true;
+	mutex_unlock(&tick_nohz_cpu_isolate_mutex);
+
+	ct_cpu_track_user(cpu);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tick_nohz_cpu_isolate);
+
+/*
+ * tick_nohz_cpu_deisolate - Remove a CPU from the full-dynticks set.
+ * @cpu: the CPU to de-isolate; must be offline.
+ */
+void tick_nohz_cpu_deisolate(int cpu)
+{
+	ct_cpu_untrack_user(cpu);
+
+	mutex_lock(&tick_nohz_cpu_isolate_mutex);
+	cpumask_clear_cpu(cpu, tick_nohz_full_mask);
+	if (cpumask_empty(tick_nohz_full_mask))
+		tick_nohz_full_running = false;
+	mutex_unlock(&tick_nohz_cpu_isolate_mutex);
+}
+EXPORT_SYMBOL_GPL(tick_nohz_cpu_deisolate);
+
 /* Get the boot-time nohz CPU list from the kernel parameters. */
 void __init tick_nohz_full_setup(cpumask_var_t cpumask)
 {

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 08/11] cpuset: add dhm_cycling_cpus mask to suppress transient invalidation
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
                   ` (6 preceding siblings ...)
  2026-07-10  3:28 ` [PATCH v4 07/11] tick/nohz: add runtime tick_nohz_full_mask update for CPU isolation Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 09/11] cpuset: drive kernel-noise isolation via per-CPU hotplug cycling Jing Wu
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

When the cpuset code cycles a CPU through hotplug to apply kernel-noise
isolation (remove_cpu followed by add_cpu), the CPU disappears from
cpu_active_mask temporarily. cpuset_hotplug_update_tasks() sees an
empty effective CPU set on the isolated partition and issues
partcmd_invalidate, tearing down the partition. The subsequent add_cpu
brings the CPU back online, but the partition has already been marked
invalid and requires manual user intervention to restore.

Add a global dhm_cycling_cpus cpumask protected by dhm_cycling_lock.
The isolation cycling path sets the bits for CPUs being cycled before
calling remove_cpu(), clears them after add_cpu() completes.
cpuset_hotplug_update_tasks() checks whether any of the cpuset's
effective exclusive CPUs are in dhm_cycling_cpus and skips the
invalidation command when they are, treating the transient empty-CPU
state as expected rather than an error.

A global cpumask avoids the need to walk the cpuset tree to find the
owning cpuset during the cycling loop which runs without cpuset locks.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 kernel/cgroup/cpuset.c | 40 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 80f43a24d3c8a..62eb6798a0c3e 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -156,6 +156,14 @@ static bool		update_housekeeping;	/* RWCS */
  */
 static cpumask_var_t	isolated_hk_cpus;	/* T */
 
+/*
+ * CPUs currently being cycled through hotplug for kernel-noise isolation.
+ * Protected by dhm_cycling_lock; read in cpuset_hotplug_update_tasks() to
+ * suppress transient partition invalidation during the offline step.
+ */
+static DEFINE_SPINLOCK(dhm_cycling_lock);
+static cpumask_var_t	dhm_cycling_cpus;
+
 /*
  * A flag to force sched domain rebuild at the end of an operation.
  * It can be set in
@@ -3708,6 +3716,7 @@ int __init cpuset_init(void)
 	BUG_ON(!zalloc_cpumask_var(&subpartitions_cpus, GFP_KERNEL));
 	BUG_ON(!zalloc_cpumask_var(&isolated_cpus, GFP_KERNEL));
 	BUG_ON(!zalloc_cpumask_var(&isolated_hk_cpus, GFP_KERNEL));
+	BUG_ON(!zalloc_cpumask_var(&dhm_cycling_cpus, GFP_KERNEL));
 
 	cpumask_setall(top_cpuset.cpus_allowed);
 	nodes_setall(top_cpuset.mems_allowed);
@@ -3804,6 +3813,20 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
 	if (remote && (cpumask_empty(subpartitions_cpus) ||
 			(cpumask_empty(&new_cpus) &&
 			 partition_is_populated(cs, NULL)))) {
+		bool cycling;
+
+		/*
+		 * Suppress transient invalidation when the offline is part
+		 * of a hotplug cycling step for kernel-noise isolation.
+		 */
+		spin_lock(&dhm_cycling_lock);
+		cycling = cpumask_available(dhm_cycling_cpus) &&
+			  cpumask_intersects(cs->effective_xcpus,
+					     dhm_cycling_cpus);
+		spin_unlock(&dhm_cycling_lock);
+		if (cycling)
+			goto unlock;
+
 		cs->prs_err = PERR_HOTPLUG;
 		remote_partition_disable(cs, tmp);
 		compute_effective_cpumask(&new_cpus, cs, parent);
@@ -3821,8 +3844,21 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
 	if (is_local_partition(cs) &&
 	    (!is_partition_valid(parent) ||
 	     tasks_nocpu_error(parent, cs, &new_cpus) ||
-	     cpumask_empty(subpartitions_cpus)))
-		partcmd = partcmd_invalidate;
+	     cpumask_empty(subpartitions_cpus))) {
+		bool cycling;
+
+		/*
+		 * Suppress transient invalidation when the offline is part
+		 * of a hotplug cycling step for kernel-noise isolation.
+		 */
+		spin_lock(&dhm_cycling_lock);
+		cycling = cpumask_available(dhm_cycling_cpus) &&
+			  cpumask_intersects(cs->effective_xcpus,
+					     dhm_cycling_cpus);
+		spin_unlock(&dhm_cycling_lock);
+		if (!cycling)
+			partcmd = partcmd_invalidate;
+	}
 	/*
 	 * On the other hand, an invalid partition root may be transitioned
 	 * back to a regular one with a non-empty effective xcpus.

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 09/11] cpuset: drive kernel-noise isolation via per-CPU hotplug cycling
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
                   ` (7 preceding siblings ...)
  2026-07-10  3:28 ` [PATCH v4 08/11] cpuset: add dhm_cycling_cpus mask to suppress transient invalidation Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 10/11] docs: cgroup-v2: document kernel-noise isolation via isolated partitions Jing Wu
  2026-07-10  3:28 ` [PATCH v4 11/11] selftests/cgroup: add kernel-noise isolation test to cpuset selftest Jing Wu
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

Track A (cpuset_update_sd_hk_unlock) updates the HK_TYPE_KERNEL_NOISE
and HK_TYPE_MANAGED_IRQ cpumasks but performs no per-CPU reconfiguration.
Tick suppression, RCU callback offloading and managed-IRQ remapping only
take effect when the affected CPUs pass through the CPU hotplug machinery.

Implement dhm_cycle_isolated_cpus() and call it from
cpuset_update_sd_hk_unlock() after all cpuset and hotplug locks are
released so that remove_cpu()/add_cpu() may acquire cpus_write_lock
without violating the cpu_hotplug_lock > cpuset_top_mutex order.

On isolation, for each newly-isolated CPU:
  1. remove_cpu()              - offline; dying callbacks migrate IRQs
  2. tick_nohz_cpu_isolate()  - add to tick_nohz_full_mask, enable
                                 context tracking (B0/B3)
  3. rcu_nocb_cpu_isolate()   - lazy nocb init, spawn kthreads, offload
                                 callbacks (B1)
  4. add_cpu()                 - online; tick and IRQ online callbacks
                                 reconfigure against updated HK masks

On de-isolation, the reverse order is applied.  The managed-IRQ
remapping requires no explicit call: irq_migrate_all_off_this_cpu()
(dying callback) and irq_affinity_online_cpu() (online callback)
already consult the updated HK_TYPE_MANAGED_IRQ mask.

dhm_prev_isolated tracks the previous isolation set so that only CPUs
whose state changed are cycled rather than the full isolation set.
lockup_detector_hk_update() (B2) is called once after all CPUs are
cycled to update the watchdog mask.

CPUs with hotplug disabled (e.g. x86-64 boot CPU) cannot be taken
offline and are skipped.  On any remove_cpu() failure the corresponding
CPU is cleared from dhm_prev_isolated so the next isolation attempt
will retry rather than silently treating it as already isolated.
Symmetrically, a de-isolation remove_cpu() failure re-sets the bit in
dhm_prev_isolated so the CPU remains tracked as still isolated.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 kernel/cgroup/cpuset.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 98 insertions(+)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 62eb6798a0c3e..d9e121bf14292 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -20,6 +20,8 @@
  */
 #include "cpuset-internal.h"
 
+#include <linux/cpu.h>
+#include <linux/cpuhplock.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
@@ -33,7 +35,9 @@
 #include <linux/sched/task.h>
 #include <linux/security.h>
 #include <linux/oom.h>
+#include <linux/nmi.h>
 #include <linux/sched/isolation.h>
+#include <linux/tick.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <linux/task_work.h>
@@ -164,6 +168,14 @@ static cpumask_var_t	isolated_hk_cpus;	/* T */
 static DEFINE_SPINLOCK(dhm_cycling_lock);
 static cpumask_var_t	dhm_cycling_cpus;
 
+/*
+ * Snapshot of the isolated CPUs from the previous housekeeping update.
+ * Used to compute the delta (newly isolated / newly de-isolated) so that
+ * only the changed CPUs are cycled rather than the full isolation set.
+ * Protected by cpuset_top_mutex.
+ */
+static cpumask_var_t	dhm_prev_isolated;
+
 /*
  * A flag to force sched domain rebuild at the end of an operation.
  * It can be set in
@@ -1339,6 +1351,84 @@ static bool prstate_housekeeping_conflict(int prstate, struct cpumask *new_cpus)
 	return false;
 }
 
+/*
+ * dhm_cycle_isolated_cpus - Apply kernel-noise isolation via hotplug cycling
+ *
+ * For each CPU newly entering isolation: cycle it offline, configure tick
+ * suppression and RCU callback offloading while it is offline, then bring
+ * it back online.  The managed-IRQ state is handled automatically by the
+ * existing irq_migrate_all_off_this_cpu() dying callback and the
+ * irq_affinity_online_cpu() online callback which both consult the
+ * already-updated HK_TYPE_MANAGED_IRQ mask.
+ *
+ * For each CPU leaving isolation: cycle it offline, de-offload RCU and
+ * restore the tick, then bring it back online.
+ *
+ * Must be called without any cpuset or hotplug locks held.
+ */
+static void dhm_cycle_isolated_cpus(const struct cpumask *new_isolated)
+{
+	cpumask_var_t newly_isolated, newly_deisolated;
+	int cpu;
+
+	if (!alloc_cpumask_var(&newly_isolated, GFP_KERNEL) ||
+	    !alloc_cpumask_var(&newly_deisolated, GFP_KERNEL)) {
+		free_cpumask_var(newly_isolated);
+		return;
+	}
+
+	cpumask_andnot(newly_isolated, new_isolated, dhm_prev_isolated);
+	cpumask_andnot(newly_deisolated, dhm_prev_isolated, new_isolated);
+	cpumask_copy(dhm_prev_isolated, new_isolated);
+
+	if (cpumask_empty(newly_isolated) && cpumask_empty(newly_deisolated))
+		return;
+
+	/* Mark cycling CPUs so cpuset_hotplug_update_tasks skips invalidation */
+	spin_lock(&dhm_cycling_lock);
+	cpumask_or(dhm_cycling_cpus, newly_isolated, newly_deisolated);
+	spin_unlock(&dhm_cycling_lock);
+
+	for_each_cpu(cpu, newly_isolated) {
+		if (!cpu_is_hotpluggable(cpu)) {
+			pr_warn_once("cpuset: CPU%d cannot be isolated (hotplug disabled)\n",
+				     cpu);
+			cpumask_clear_cpu(cpu, dhm_prev_isolated);
+			continue;
+		}
+		if (remove_cpu(cpu)) {
+			pr_warn_once("cpuset: failed to offline CPU%d for isolation\n",
+				     cpu);
+			cpumask_clear_cpu(cpu, dhm_prev_isolated);
+			continue;
+		}
+		WARN_ON_ONCE(tick_nohz_cpu_isolate(cpu));
+		WARN_ON_ONCE(rcu_nocb_cpu_isolate(cpu));
+		WARN_ON_ONCE(add_cpu(cpu));
+	}
+
+	for_each_cpu(cpu, newly_deisolated) {
+		if (remove_cpu(cpu)) {
+			pr_warn_once("cpuset: failed to offline CPU%d for de-isolation\n",
+				     cpu);
+			cpumask_set_cpu(cpu, dhm_prev_isolated);
+			continue;
+		}
+		WARN_ON_ONCE(rcu_nocb_cpu_deoffload(cpu));
+		tick_nohz_cpu_deisolate(cpu);
+		WARN_ON_ONCE(add_cpu(cpu));
+	}
+
+	spin_lock(&dhm_cycling_lock);
+	cpumask_clear(dhm_cycling_cpus);
+	spin_unlock(&dhm_cycling_lock);
+
+	lockup_detector_hk_update();
+
+	free_cpumask_var(newly_isolated);
+	free_cpumask_var(newly_deisolated);
+}
+
 /*
  * cpuset_update_sd_hk_unlock - Rebuild sched domains, update HK & unlock
  *
@@ -1386,6 +1476,13 @@ static void cpuset_update_sd_hk_unlock(void)
 		WARN_ON_ONCE(housekeeping_update_types(noise_types,
 						       isolated_hk_cpus));
 		mutex_unlock(&cpuset_top_mutex);
+
+		/*
+		 * All cpuset and hotplug locks are released.  Cycle each
+		 * affected CPU through hotplug to activate tick suppression,
+		 * RCU callback offloading and managed-IRQ remapping.
+		 */
+		dhm_cycle_isolated_cpus(isolated_hk_cpus);
 	} else {
 		cpuset_full_unlock();
 	}
@@ -3717,6 +3814,7 @@ int __init cpuset_init(void)
 	BUG_ON(!zalloc_cpumask_var(&isolated_cpus, GFP_KERNEL));
 	BUG_ON(!zalloc_cpumask_var(&isolated_hk_cpus, GFP_KERNEL));
 	BUG_ON(!zalloc_cpumask_var(&dhm_cycling_cpus, GFP_KERNEL));
+	BUG_ON(!zalloc_cpumask_var(&dhm_prev_isolated, GFP_KERNEL));
 
 	cpumask_setall(top_cpuset.cpus_allowed);
 	nodes_setall(top_cpuset.mems_allowed);

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 10/11] docs: cgroup-v2: document kernel-noise isolation via isolated partitions
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
                   ` (8 preceding siblings ...)
  2026-07-10  3:28 ` [PATCH v4 09/11] cpuset: drive kernel-noise isolation via per-CPU hotplug cycling Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  2026-07-10  3:28 ` [PATCH v4 11/11] selftests/cgroup: add kernel-noise isolation test to cpuset selftest Jing Wu
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

Document that creating a cpuset isolated partition updates the
kernel-noise housekeeping masks (HK_TYPE_KERNEL_NOISE and
HK_TYPE_MANAGED_IRQ) in addition to the sched-domain mask, and
that destroying it restores the boot configuration.

No boot-time kernel parameters such as nohz_full= or rcu_nocbs=
are required; writing "isolated" to cpuset.cpus.partition is the
only mechanism needed.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 Documentation/admin-guide/cgroup-v2.rst | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index 6efd0095ed995..eaafe6d88c0e5 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -2721,6 +2721,23 @@ Cpuset Interface Files
 	kernel boot command line option.  If those CPUs are to be put
 	into a partition, they have to be used in an isolated partition.
 
+	When an isolated partition is created or destroyed, the kernel
+	automatically drives runtime updates of the housekeeping masks
+	for kernel-noise types (nohz_full, RCU NOCB, managed IRQ
+	interrupts).  This extends isolation beyond scheduler domains:
+	the tick is stopped on isolated CPUs, RCU callbacks are
+	offloaded to housekeeping cores, and managed interrupts are
+	migrated away.  No boot-time kernel parameters such as
+	``nohz_full=`` or ``rcu_nocbs=`` are required; writing
+	``isolated`` to ``cpuset.cpus.partition`` is the only mechanism
+	needed.  No additional cgroupfs files are required.
+
+	CPUs with hotplug disabled (typically the boot CPU, CPU 0, on
+	x86-64) cannot be cycled offline for kernel-noise isolation.
+	The kernel emits a one-time warning and keeps those CPUs in
+	the tick and RCU-NOCB housekeeping set, even when they appear
+	in an isolated partition.
+
 
 Device controller
 -----------------

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v4 11/11] selftests/cgroup: add kernel-noise isolation test to cpuset selftest
  2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
                   ` (9 preceding siblings ...)
  2026-07-10  3:28 ` [PATCH v4 10/11] docs: cgroup-v2: document kernel-noise isolation via isolated partitions Jing Wu
@ 2026-07-10  3:28 ` Jing Wu
  10 siblings, 0 replies; 12+ messages in thread
From: Jing Wu @ 2026-07-10  3:28 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
	Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Thomas Gleixner
  Cc: Waiman Long, linux-kernel, rcu, cgroups, linux-doc,
	linux-kselftest, Jing Wu, Qiliang Yuan

Add test_hk_noise_isolated() to test_cpuset_prs.sh to verify that
creating and destroying an isolated partition updates the kernel-noise
housekeeping state, including the /sys/devices/system/cpu/nohz_full
attribute.  Add the cpu_in_cpulist() helper to correctly test membership
against a cpulist that may contain ranges.

Also detect and report whether the test is running in zero-boot-param
mode (no nohz_full= in /proc/cmdline).  When in zero-boot-param mode
the test confirms that nohz_full is activated by the DHM runtime path,
verifying that dhm_cycle_isolated_cpus() correctly enables tick
isolation without any boot-time setup.

Co-developed-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Qiliang Yuan <yuanql9@chinatelecom.cn>
Signed-off-by: Jing Wu <realwujing@gmail.com>
---
 tools/testing/selftests/cgroup/test_cpuset_prs.sh | 580 +++++++++++++++++++++-
 1 file changed, 579 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index a56f4153c64df..ce16734545505 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -20,7 +20,7 @@ skip_test() {
 WAIT_INOTIFY=$(cd $(dirname $0); pwd)/wait_inotify
 
 # Find cgroup v2 mount point
-CGROUP2=$(mount -t cgroup2 | head -1 | awk -e '{print $3}')
+CGROUP2=$(mount -t cgroup2 | head -1 | awk '{print $3}')
 [[ -n "$CGROUP2" ]] || skip_test "Cgroup v2 mount point not found!"
 SUBPARTS_CPUS=$CGROUP2/.__DEBUG__.cpuset.cpus.subpartitions
 CPULIST=$(cat $CGROUP2/cpuset.cpus.effective)
@@ -1204,9 +1204,587 @@ test_inotify()
 	echo "" > cpuset.cpus
 }
 
+#
+# cpu_in_cpulist <cpu> <cpulist>
+#
+# Return 0 if <cpu> appears in <cpulist> (a kernel cpumask list such as
+# "0-3,8-31"), non-zero otherwise.  The kernel cpulist format uses ranges
+# ("lo-hi") and comma-separated items; a simple grep cannot detect that a
+# number falls in the middle of a range, so walk each element explicitly.
+#
+cpu_in_cpulist()
+{
+	local cpu=$1 list=$2 range lo hi
+	for range in $(echo "$list" | tr ',' ' '); do
+		if [[ "$range" == *-* ]]; then
+			lo=${range%-*}
+			hi=${range#*-}
+			[[ $cpu -ge $lo && $cpu -le $hi ]] && return 0
+		else
+			[[ $cpu -eq $range ]] && return 0
+		fi
+	done
+	return 1
+}
+
+#
+# verify_nohz_exact <baseline> <active_cpus> <current>
+#
+# Verify that <current> nohz_full equals <baseline> ∪ <active_cpus>.
+# <active_cpus> may be a cpulist range ("4-7") or empty string ("").
+# Catches both missing CPUs and unexpected extra CPUs.
+#
+verify_nohz_exact()
+{
+	local baseline=$1 active=$2 current=$3 cpu exp got
+	for cpu in $(seq 0 $((NR_CPUS - 1))); do
+		exp=0; got=0
+		cpu_in_cpulist $cpu "$baseline" && exp=1
+		[[ -n "$active" ]] && cpu_in_cpulist $cpu "$active" && exp=1
+		cpu_in_cpulist $cpu "$current" && got=1
+		[[ $exp -eq $got ]] || {
+			if [[ $got -eq 0 ]]; then
+				echo "FAIL: cpu${cpu} expected in nohz_full but absent" \
+				     "(baseline='$baseline' active='$active'" \
+				     "current='$current')"
+			else
+				echo "FAIL: cpu${cpu} unexpectedly in nohz_full" \
+				     "(baseline='$baseline' active='$active'" \
+				     "current='$current')"
+			fi
+			return 1
+		}
+	done
+	return 0
+}
+
+#
+# Test that isolated partition creation/destruction drives kernel-noise
+# housekeeping mask updates and remains correct under pressure.
+#
+# Requires: >=8 CPUs, no isolcpus= boot conflict, root
+#
+
+#
+# hk_noise_check_nocb_affinity <cpulist> <expect_isolated>
+#
+# When expect_isolated=1: verify rcuop/N kthreads for CPUs in cpulist do NOT
+# include those CPUs in their scheduler affinity (RCU NOCB active — callbacks
+# for the isolated CPU are offloaded to a different CPU).
+# When expect_isolated=0: verify CPUs are back in affinity (NOCB restored).
+# Silently skips CPUs whose rcuop/N thread is absent (no NOCB support).
+#
+hk_noise_check_nocb_affinity()
+{
+	local cpulist=$1 expect_isolated=$2
+	local cpu lo hi range pid aff_hex rev_hex nibble_pos nibble_char
+	local nibble_val bit_in_nibble bit failed=0
+
+	command -v taskset > /dev/null 2>&1 || return 0
+
+	for range in $(echo "$cpulist" | tr ',' ' '); do
+		if [[ "$range" == *-* ]]; then
+			lo=${range%-*}; hi=${range#*-}
+		else
+			lo=$range; hi=$range
+		fi
+		for cpu in $(seq "$lo" "$hi"); do
+			pid=$(ps -eo pid,comm | awk -v c="rcuop/$cpu" '$2==c{print $1}')
+			[[ -n "$pid" ]] || continue
+			aff_hex=$(taskset -p "$pid" 2>/dev/null | awk '{print $NF}')
+			[[ -n "$aff_hex" ]] || continue
+
+			# Extract bit <cpu> from the hex affinity mask.
+			# Each hex digit covers 4 CPUs; reverse the string to
+			# work from the LSB side.
+			rev_hex=$(echo "$aff_hex" | rev)
+			nibble_pos=$((cpu / 4))
+			nibble_char=${rev_hex:$nibble_pos:1}
+			if [[ -z "$nibble_char" ]]; then
+				nibble_val=0
+			else
+				nibble_val=$((16#$nibble_char))
+			fi
+			bit_in_nibble=$((cpu % 4))
+			bit=$(( (nibble_val >> bit_in_nibble) & 1 ))
+
+			if [[ $expect_isolated -eq 1 && $bit -eq 1 ]]; then
+				echo "FAIL: rcuop/$cpu affinity still includes" \
+				     "CPU$cpu after isolation (mask=0x$aff_hex)"
+				failed=1
+			elif [[ $expect_isolated -eq 0 && $bit -eq 0 ]]; then
+				echo "FAIL: rcuop/$cpu affinity still excludes" \
+				     "CPU$cpu after de-isolation (mask=0x$aff_hex)"
+				failed=1
+			fi
+		done
+	done
+	return $failed
+}
+
+test_hk_noise_isolated()
+{
+	local ISOL_BEFORE TEST_CPUS i PART ISOL_AFTER ISOL_RESTORE
+	local NOHZ_FILE NOHZ_BEFORE NOHZ_AFTER NOHZ_RESTORE
+	local HK_NOHZ_CHECK=0
+	local LOOPS=100
+	local CMDLINE HAS_BOOT_NOHZ=0
+	local DMESG_LINES_START
+	DMESG_LINES_START=$(dmesg | wc -l)
+
+	[[ $NR_CPUS -ge 8 ]] || {
+		echo "HK-noise test skipped: need >=8 CPUs, have $NR_CPUS"
+		return 0
+	}
+
+	# Detect whether CONFIG_NO_HZ_FULL is active: the sysfs attribute
+	# /sys/devices/system/cpu/nohz_full exposes the current nohz_full
+	# cpumask and is only present when NO_HZ_FULL is enabled.
+	NOHZ_FILE=/sys/devices/system/cpu/nohz_full
+	[[ -r "$NOHZ_FILE" ]] && HK_NOHZ_CHECK=1
+
+	# Determine if running in zero-boot-param mode.  DHM activates tick
+	# and RCU-NOCB isolation at runtime; no nohz_full= or rcu_nocbs=
+	# kernel boot parameters are required.
+	{ read -r CMDLINE < /proc/cmdline; } 2>/dev/null || CMDLINE=""
+	[[ $CMDLINE = *nohz_full=* ]] && HAS_BOOT_NOHZ=1
+	if [[ $HAS_BOOT_NOHZ -eq 0 ]]; then
+		console_msg "HK-noise: zero-boot-param mode" \
+		            "(no nohz_full= in /proc/cmdline -- testing DHM runtime path)"
+	else
+		console_msg "HK-noise: boot-param mode (nohz_full= present at boot)"
+	fi
+
+	cd $CGROUP2/test
+	echo member > cpuset.cpus.partition 2>/dev/null
+	echo "" > cpuset.cpus 2>/dev/null
+
+	ISOL_BEFORE=$(cat $CGROUP2/cpuset.cpus.isolated)
+	[[ $HK_NOHZ_CHECK -eq 1 ]] && NOHZ_BEFORE=$(cat $NOHZ_FILE)
+	TEST_CPUS="4-7"
+	echo $TEST_CPUS > cpuset.cpus
+
+	#
+	# Basic create/destroy cycle — verify domain isolation and
+	# kernel-noise (nohz_full) changes together.
+	#
+	console_msg "HK-noise: basic create/destroy cycle"
+	echo isolated > cpuset.cpus.partition
+
+	ISOL_AFTER=$(cat $CGROUP2/cpuset.cpus.isolated)
+	[[ $ISOL_AFTER != "$ISOL_BEFORE" ]] || {
+		echo "FAIL: isolated set unchanged after partition create"
+		exit 1
+	}
+
+	if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+		NOHZ_AFTER=$(cat $NOHZ_FILE)
+		verify_nohz_exact "$NOHZ_BEFORE" "$TEST_CPUS" "$NOHZ_AFTER" || exit 1
+		console_msg "HK-noise: nohz_full after isolation: $NOHZ_AFTER"
+	fi
+
+	# Verify RCU NOCB: rcuop/N kthreads for isolated CPUs must have those
+	# CPUs removed from their scheduler affinity mask.
+	hk_noise_check_nocb_affinity "$TEST_CPUS" 1 || exit 1
+
+	echo member > cpuset.cpus.partition
+
+	ISOL_RESTORE=$(cat $CGROUP2/cpuset.cpus.isolated)
+	[[ $ISOL_RESTORE = "$ISOL_BEFORE" ]] || {
+		echo "FAIL: expected '$ISOL_BEFORE' after destroy, got '$ISOL_RESTORE'"
+		exit 1
+	}
+
+	if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+		NOHZ_RESTORE=$(cat $NOHZ_FILE)
+		verify_nohz_exact "$NOHZ_BEFORE" "" "$NOHZ_RESTORE" || exit 1
+	fi
+
+	# Verify RCU NOCB restored: isolated CPUs must reappear in rcuop/N affinity.
+	hk_noise_check_nocb_affinity "$TEST_CPUS" 0 || exit 1
+
+	#
+	# Reject all-CPU isolation (must leave at least one housekeeping CPU)
+	#
+	console_msg "HK-noise: reject all-CPU isolation"
+	echo 0-$((NR_CPUS - 1)) > cpuset.cpus
+	echo isolated > cpuset.cpus.partition
+	PART=$(cat cpuset.cpus.partition)
+	[[ $PART = *invalid* || $PART = member ]] || {
+		echo "FAIL: all-CPU isolation was not rejected, got '$PART'"
+		exit 1
+	}
+
+	#
+	# SMT safety: partial sibling isolation
+	#
+	console_msg "HK-noise: SMT sibling constraint"
+	echo $TEST_CPUS > cpuset.cpus
+	echo isolated > cpuset.cpus.partition
+	PART=$(cat cpuset.cpus.partition)
+	[[ $PART = isolated ]] || {
+		echo "FAIL: could not create isolated partition, got '$PART'"
+		exit 1
+	}
+	echo member > cpuset.cpus.partition
+
+	#
+	# Non-hotpluggable CPU: must be skipped with a kernel warning without
+	# rejecting the partition; hotpluggable peers must still be isolated.
+	#
+	# A CPU whose online file is absent (e.g. CPU 0 on x86-64) has hotplug
+	# disabled.  DHM emits pr_warn_once and keeps it in the tick/RCU-NOCB
+	# housekeeping set; it must not appear in nohz_full after isolation.
+	# The remaining hotpluggable CPUs in the partition must still be isolated.
+	#
+	local FIXED_CPU="" c NOHZ_NOW
+	for c in $(seq 0 $((NR_CPUS - 1))); do
+		[[ -f /sys/devices/system/cpu/cpu${c}/online ]] || {
+			FIXED_CPU=$c
+			break
+		}
+	done
+	if [[ -n "$FIXED_CPU" ]]; then
+		console_msg "HK-noise: non-hotpluggable CPU${FIXED_CPU} skip"
+		echo "${FIXED_CPU},${TEST_CPUS}" > cpuset.cpus
+		echo isolated > cpuset.cpus.partition
+		PART=$(cat cpuset.cpus.partition)
+		[[ $PART = isolated ]] || {
+			echo "FAIL: partition rejected when including non-hotpluggable" \
+			     "CPU${FIXED_CPU}: got '$PART'"
+			exit 1
+		}
+		if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+			NOHZ_NOW=$(cat $NOHZ_FILE)
+			if cpu_in_cpulist $FIXED_CPU "$NOHZ_NOW"; then
+				echo "FAIL: non-hotpluggable CPU${FIXED_CPU} appeared" \
+				     "in nohz_full (should be skipped)"
+				exit 1
+			fi
+			local lo hi
+			lo=${TEST_CPUS%%-*}
+			hi=${TEST_CPUS##*-}
+			for cpu in $(seq "$lo" "$hi"); do
+				if ! cpu_in_cpulist $cpu "$NOHZ_NOW"; then
+					echo "FAIL: hotpluggable cpu${cpu} missing from" \
+					     "nohz_full in mixed partition (got: '$NOHZ_NOW')"
+					exit 1
+				fi
+			done
+			console_msg "HK-noise: CPU${FIXED_CPU} absent, ${TEST_CPUS} present" \
+			            "in nohz_full: $NOHZ_NOW"
+		fi
+		echo member > cpuset.cpus.partition
+		echo $TEST_CPUS > cpuset.cpus
+	else
+		console_msg "HK-noise: all CPUs hotpluggable; skip non-hotpluggable subtest"
+	fi
+
+	#
+	# Delta isolation: modify cpuset.cpus while the partition is isolated.
+	# dhm_prev_isolated must track the delta and update nohz_full in step.
+	#
+	local lo hi mid lower_cpus upper_cpu
+	lo=${TEST_CPUS%%-*}
+	hi=${TEST_CPUS##*-}
+	mid=$(( lo + (hi - lo) / 2 ))
+	lower_cpus="${lo}-${mid}"
+	upper_cpu=$(( mid + 1 ))
+	console_msg "HK-noise: delta isolation (shrink ${TEST_CPUS} → ${lower_cpus})"
+	echo $TEST_CPUS > cpuset.cpus
+	echo isolated > cpuset.cpus.partition
+	PART=$(cat cpuset.cpus.partition)
+	[[ $PART = isolated ]] || {
+		echo "FAIL: delta test: initial isolation failed, got '$PART'"
+		exit 1
+	}
+	echo $lower_cpus > cpuset.cpus
+	PART=$(cat cpuset.cpus.partition)
+	if [[ $PART = isolated ]]; then
+		if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+			NOHZ_NOW=$(cat $NOHZ_FILE)
+			verify_nohz_exact "$NOHZ_BEFORE" "$lower_cpus" "$NOHZ_NOW" || exit 1
+		fi
+		# Expand back to full TEST_CPUS and re-verify
+		echo $TEST_CPUS > cpuset.cpus
+		if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+			NOHZ_NOW=$(cat $NOHZ_FILE)
+			verify_nohz_exact "$NOHZ_BEFORE" "$TEST_CPUS" "$NOHZ_NOW" || exit 1
+		fi
+		echo member > cpuset.cpus.partition
+		if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+			NOHZ_NOW=$(cat $NOHZ_FILE)
+			verify_nohz_exact "$NOHZ_BEFORE" "" "$NOHZ_NOW" || exit 1
+		fi
+	else
+		console_msg "HK-noise: delta test: partition invalidated on shrink" \
+		            "('$PART') -- cpuset constraint, not a DHM bug; skipping"
+		echo member > cpuset.cpus.partition 2>/dev/null || true
+	fi
+	echo $TEST_CPUS > cpuset.cpus
+
+	#
+	# Nested partition: parent root → child isolated
+	#
+	console_msg "HK-noise: nested partition inheritance"
+	echo $TEST_CPUS > cpuset.cpus
+	test_partition root
+	mkdir -p HK_SUB
+	cd HK_SUB
+	echo "${lo}-$((lo + 1))" > cpuset.cpus
+	echo isolated > cpuset.cpus.partition
+	ISOL_AFTER=$(cat $CGROUP2/cpuset.cpus.isolated)
+	[[ -n $ISOL_AFTER ]] || {
+		echo "FAIL: nested isolated partition not reflected in cpuset.cpus.isolated"
+		exit 1
+	}
+	echo member > cpuset.cpus.partition
+	cd $CGROUP2/test
+	echo member > cpuset.cpus.partition
+	rmdir HK_SUB 2>/dev/null
+
+	#
+	# Pressure test: 100 create/destroy cycles with nohz_full verified
+	# on every cycle to catch mid-run state corruption.
+	#
+	console_msg "HK-noise: pressure test ($LOOPS cycles)"
+	echo $TEST_CPUS > cpuset.cpus
+	for i in $(seq 1 $LOOPS); do
+		echo isolated > cpuset.cpus.partition
+		PART=$(cat cpuset.cpus.partition)
+		[[ $PART = isolated ]] || {
+			echo "FAIL: cycle $i create failed, got '$PART'"
+			exit 1
+		}
+		if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+			NOHZ_NOW=$(cat $NOHZ_FILE)
+			verify_nohz_exact "$NOHZ_BEFORE" "$TEST_CPUS" "$NOHZ_NOW" || {
+				echo "FAIL: nohz_full wrong at cycle $i (isolated)"
+				exit 1
+			}
+		fi
+		echo member > cpuset.cpus.partition
+		PART=$(cat cpuset.cpus.partition)
+		[[ $PART = member ]] || {
+			echo "FAIL: cycle $i destroy failed, got '$PART'"
+			exit 1
+		}
+		if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+			NOHZ_NOW=$(cat $NOHZ_FILE)
+			verify_nohz_exact "$NOHZ_BEFORE" "" "$NOHZ_NOW" || {
+				echo "FAIL: nohz_full wrong at cycle $i (member)"
+				exit 1
+			}
+		fi
+	done
+
+	#
+	# Stability: after pressure test, verify final state
+	#
+	console_msg "HK-noise: post-pressure cleanup"
+	echo isolated > cpuset.cpus.partition
+	ISOL_AFTER=$(cat $CGROUP2/cpuset.cpus.isolated)
+	[[ -n $ISOL_AFTER ]] || {
+		echo "FAIL: isolated set empty after pressure test"
+		exit 1
+	}
+	echo member > cpuset.cpus.partition
+	echo "" > cpuset.cpus
+	ISOL_RESTORE=$(cat $CGROUP2/cpuset.cpus.isolated)
+	[[ $ISOL_RESTORE = "$ISOL_BEFORE" ]] || {
+		echo "FAIL: final isolated '$ISOL_RESTORE' != '$ISOL_BEFORE'"
+		exit 1
+	}
+
+	if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+		NOHZ_RESTORE=$(cat $NOHZ_FILE)
+		[[ "$NOHZ_RESTORE" = "$NOHZ_BEFORE" ]] || {
+			echo "FAIL: nohz_full not restored after pressure test:" \
+			     "expected '$NOHZ_BEFORE', got '$NOHZ_RESTORE'"
+			exit 1
+		}
+	fi
+
+	#
+	# Pressure with resident task: create/destroy cycles while a sleeping
+	# task occupies the isolated partition.  Exercises the dhm_cycling_cpus
+	# suppression path that prevents false partition invalidation when a
+	# task is present during hotplug cycling steps.
+	#
+	console_msg "HK-noise: pressure with resident task ($LOOPS cycles)"
+	echo $TEST_CPUS > cpuset.cpus
+	sleep 600 &
+	local TASK_PID=$!
+	echo $TASK_PID > cgroup.procs
+	for i in $(seq 1 $LOOPS); do
+		echo isolated > cpuset.cpus.partition
+		PART=$(cat cpuset.cpus.partition)
+		[[ $PART = isolated ]] || {
+			echo "FAIL: task-occupied cycle $i create failed, got '$PART'"
+			kill $TASK_PID 2>/dev/null; wait $TASK_PID 2>/dev/null
+			exit 1
+		}
+		if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+			NOHZ_NOW=$(cat $NOHZ_FILE)
+			verify_nohz_exact "$NOHZ_BEFORE" "$TEST_CPUS" "$NOHZ_NOW" || {
+				kill $TASK_PID 2>/dev/null; wait $TASK_PID 2>/dev/null
+				exit 1
+			}
+		fi
+		echo member > cpuset.cpus.partition
+		PART=$(cat cpuset.cpus.partition)
+		[[ $PART = member ]] || {
+			echo "FAIL: task-occupied cycle $i destroy failed, got '$PART'"
+			kill $TASK_PID 2>/dev/null; wait $TASK_PID 2>/dev/null
+			exit 1
+		}
+	done
+	kill $TASK_PID 2>/dev/null
+	wait $TASK_PID 2>/dev/null
+	echo "" > cpuset.cpus
+
+	#
+	# Concurrent partitions: two sibling cgroups each holding half of
+	# TEST_CPUS simultaneously isolated.  Verifies independent isolation
+	# and correct union in cpuset.cpus.isolated / nohz_full.
+	#
+	local lo hi mid lower_half upper_half NOHZ_BOTH
+	lo=${TEST_CPUS%%-*}; hi=${TEST_CPUS##*-}
+	mid=$(( lo + (hi - lo) / 2 ))
+	lower_half="${lo}-${mid}"
+	upper_half="$((mid + 1))-${hi}"
+	console_msg "HK-noise: concurrent partitions ($lower_half and $upper_half)"
+	echo $TEST_CPUS > cpuset.cpus
+	echo root > cpuset.cpus.partition
+	mkdir -p HK_A HK_B
+	echo "$lower_half" > HK_A/cpuset.cpus
+	echo isolated > HK_A/cpuset.cpus.partition
+	echo "$upper_half" > HK_B/cpuset.cpus
+	echo isolated > HK_B/cpuset.cpus.partition
+	local PA PB
+	PA=$(cat HK_A/cpuset.cpus.partition)
+	PB=$(cat HK_B/cpuset.cpus.partition)
+	[[ $PA = isolated ]] || {
+		echo "FAIL: concurrent partition HK_A not isolated, got '$PA'"
+		echo member > HK_A/cpuset.cpus.partition 2>/dev/null
+		echo member > HK_B/cpuset.cpus.partition 2>/dev/null
+		rmdir HK_A HK_B 2>/dev/null
+		echo member > cpuset.cpus.partition 2>/dev/null
+		exit 1
+	}
+	[[ $PB = isolated ]] || {
+		echo "FAIL: concurrent partition HK_B not isolated, got '$PB'"
+		echo member > HK_A/cpuset.cpus.partition 2>/dev/null
+		echo member > HK_B/cpuset.cpus.partition 2>/dev/null
+		rmdir HK_A HK_B 2>/dev/null
+		echo member > cpuset.cpus.partition 2>/dev/null
+		exit 1
+	}
+	if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+		NOHZ_BOTH=$(cat $NOHZ_FILE)
+		verify_nohz_exact "$NOHZ_BEFORE" "$TEST_CPUS" "$NOHZ_BOTH" || {
+			echo member > HK_A/cpuset.cpus.partition 2>/dev/null
+			echo member > HK_B/cpuset.cpus.partition 2>/dev/null
+			rmdir HK_A HK_B 2>/dev/null
+			echo member > cpuset.cpus.partition 2>/dev/null
+			exit 1
+		}
+	fi
+	hk_noise_check_nocb_affinity "$TEST_CPUS" 1 || {
+		echo member > HK_A/cpuset.cpus.partition 2>/dev/null
+		echo member > HK_B/cpuset.cpus.partition 2>/dev/null
+		rmdir HK_A HK_B 2>/dev/null
+		echo member > cpuset.cpus.partition 2>/dev/null
+		exit 1
+	}
+	echo member > HK_A/cpuset.cpus.partition
+	echo member > HK_B/cpuset.cpus.partition
+	rmdir HK_A HK_B
+	echo member > cpuset.cpus.partition
+	if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+		NOHZ_NOW=$(cat $NOHZ_FILE)
+		verify_nohz_exact "$NOHZ_BEFORE" "" "$NOHZ_NOW" || exit 1
+	fi
+	hk_noise_check_nocb_affinity "$TEST_CPUS" 0 || exit 1
+	echo "" > cpuset.cpus
+
+	#
+	# Concurrent create/destroy: two background subshells race on the same
+	# partition simultaneously.  Verifies that concurrent cpuset writes
+	# do not corrupt kernel state or trigger warnings.
+	#
+	console_msg "HK-noise: concurrent create/destroy race"
+	echo $TEST_CPUS > cpuset.cpus
+	local RACE_LOOPS=30 RACE_PID1 RACE_PID2
+	(for i in $(seq 1 $RACE_LOOPS); do
+		echo isolated > cpuset.cpus.partition 2>/dev/null
+		echo member   > cpuset.cpus.partition 2>/dev/null
+	done) &
+	RACE_PID1=$!
+	(for i in $(seq 1 $RACE_LOOPS); do
+		echo member   > cpuset.cpus.partition 2>/dev/null
+		echo isolated > cpuset.cpus.partition 2>/dev/null
+	done) &
+	RACE_PID2=$!
+	wait $RACE_PID1 $RACE_PID2
+	# Drive to a known-good state regardless of who won the last write.
+	echo member > cpuset.cpus.partition 2>/dev/null || true
+	PART=$(cat cpuset.cpus.partition)
+	[[ $PART = member ]] || {
+		echo "FAIL: concurrent race left partition in bad state: '$PART'"
+		exit 1
+	}
+	if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+		NOHZ_NOW=$(cat $NOHZ_FILE)
+		verify_nohz_exact "$NOHZ_BEFORE" "" "$NOHZ_NOW" || exit 1
+	fi
+	echo "" > cpuset.cpus
+
+	#
+	# Kernel hard-error check: none of the above scenarios must have
+	# triggered a BUG, OOPS, panic, or RCU stall in the kernel log.
+	# Also catch WARNINGs that implicate our subsystems (cpuset / rcu /
+	# nohz / housekeeping / irq_affinity); ignore unrelated WARNINGs from
+	# other kernel subsystems or user-space processes.
+	#
+	console_msg "HK-noise: checking kernel log for errors"
+	local new_errors
+	new_errors=$(dmesg | tail -n "+$((DMESG_LINES_START + 1))" | \
+		grep -c -E \
+		  'kernel BUG at|OOPS|Kernel panic|RCU Stall|scheduling while atomic' \
+		|| true)
+	local new_subsys_warns
+	new_subsys_warns=$(dmesg | tail -n "+$((DMESG_LINES_START + 1))" | \
+		grep 'WARNING:' | \
+		grep -c -E 'cpuset|rcu|nohz|housekeeping|irq_affinity|dhm' \
+		|| true)
+	local total_errors=$(( new_errors + new_subsys_warns ))
+	[[ $total_errors -eq 0 ]] || {
+		echo "FAIL: $total_errors kernel error(s)/warning(s) during HK-noise test:"
+		dmesg | tail -n "+$((DMESG_LINES_START + 1))" | \
+			grep -E 'kernel BUG at|OOPS|Kernel panic|RCU Stall|scheduling while atomic' | head -10
+		dmesg | tail -n "+$((DMESG_LINES_START + 1))" | \
+			grep 'WARNING:' | grep -E 'cpuset|rcu|nohz|housekeeping|irq_affinity|dhm' | head -10
+		exit 1
+	}
+
+	cd $CGROUP2
+	if [[ $HK_NOHZ_CHECK -eq 1 ]]; then
+		if [[ $HAS_BOOT_NOHZ -eq 0 ]]; then
+			console_msg "HK-noise: PASSED" \
+			            "(zero-boot-param: nohz_full verified via DHM runtime path)"
+		else
+			console_msg "HK-noise: PASSED (with nohz_full verification)"
+		fi
+	else
+		console_msg "HK-noise: PASSED (nohz_full skipped: CONFIG_NO_HZ_FULL not active)"
+	fi
+}
+
 trap cleanup 0 2 3 6
 run_state_test TEST_MATRIX
 run_remote_state_test REMOTE_TEST_MATRIX
 test_isolated
 test_inotify
+test_hk_noise_isolated
 echo "All tests PASSED."

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-07-10  3:30 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-10  3:28 [PATCH v4 00/11] Dynamic Housekeeping Management (DHM) via CPUSets Jing Wu
2026-07-10  3:28 ` [PATCH v4 01/11] sched/isolation: Add runtime housekeeping mask updates with boot snapshots Jing Wu
2026-07-10  3:28 ` [PATCH v4 02/11] sched/isolation: RCU-protect runtime-mutable housekeeping cpumask readers Jing Wu
2026-07-10  3:28 ` [PATCH v4 03/11] cgroup/cpuset: Drive kernel-noise housekeeping updates from isolated partitions Jing Wu
2026-07-10  3:28 ` [PATCH v4 04/11] context_tracking: allow runtime per-CPU user tracking enable/disable Jing Wu
2026-07-10  3:28 ` [PATCH v4 05/11] rcu/nocb: support lazy init for runtime CPU isolation Jing Wu
2026-07-10  3:28 ` [PATCH v4 06/11] watchdog: sync watchdog_cpumask with HK_TYPE_KERNEL_NOISE on isolation Jing Wu
2026-07-10  3:28 ` [PATCH v4 07/11] tick/nohz: add runtime tick_nohz_full_mask update for CPU isolation Jing Wu
2026-07-10  3:28 ` [PATCH v4 08/11] cpuset: add dhm_cycling_cpus mask to suppress transient invalidation Jing Wu
2026-07-10  3:28 ` [PATCH v4 09/11] cpuset: drive kernel-noise isolation via per-CPU hotplug cycling Jing Wu
2026-07-10  3:28 ` [PATCH v4 10/11] docs: cgroup-v2: document kernel-noise isolation via isolated partitions Jing Wu
2026-07-10  3:28 ` [PATCH v4 11/11] selftests/cgroup: add kernel-noise isolation test to cpuset selftest Jing Wu

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