* [RFC PATCH v2 0/2] tick/nohz: Add per-CPU control of non-idle tick suppression
@ 2026-10-17 13:13 Peng Wang
2026-10-17 13:13 ` [RFC PATCH v2 1/2] tick/nohz: Add a policy dependency for full dynticks Peng Wang
2026-10-17 13:13 ` [RFC PATCH v2 2/2] cpu: Add per-CPU control of non-idle tick suppression Peng Wang
0 siblings, 2 replies; 3+ messages in thread
From: Peng Wang @ 2026-10-17 13:13 UTC (permalink / raw)
To: Anna-Maria Behnsen, Frederic Weisbecker, Ingo Molnar,
Thomas Gleixner
Cc: Peng Wang, Greg Kroah-Hartman, Rafael J . Wysocki,
Danilo Krummrich, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Peter Zijlstra, Waiman Long, Tejun Heo,
He Rongguang, linux-kernel, linux-api, driver-core,
linux-trace-kernel, cgroups
This series adds per-CPU control of non-idle tick suppression within
the boot-time nohz_full set. The policy can be changed without taking
CPUs offline.
Some cloud hosts pin vCPU threads one-to-one to host CPUs. At full
allocation, housekeeping CPUs also provide vCPU capacity and always
retain their non-idle tick. Operators want comparable performance for
otherwise equivalent vCPUs, regardless of CPU placement. Retaining the
non-idle tick on nohz_full CPUs as well avoids one placement-dependent
difference in host tick interference.
With spare capacity, selected nohz_full CPUs may stop the tick to reduce
host scheduler-tick interference with vCPUs. Allocation changes as VMs
are created or destroyed while other VMs remain running. A vCPU thread
may be the only runnable task in either state, so existing tick
dependencies do not express this policy.
The per-CPU nohz_tick_allowed boolean defaults to 1 (allowed). Writing
0 prevents non-idle tick suppression; writing 1 allows it, subject to
existing dependencies. Readback reports permission, not tick state.
This leaves idle tick suppression, nohz_full membership, context
tracking, RCU offloading and housekeeping configuration unchanged.
This series depends on the separately submitted nohz kick fix [2].
Testing used that fix together with this series in x86_64 KVM guests,
with NO_HZ_FULL enabled and disabled.
Changes since v1:
- Following Greg's sysfs feedback [1], replace the global CPU list with
a per-CPU boolean, removing the list-size and custom parsing issues.
- Rename nohz_full_allowed to nohz_tick_allowed, as suggested by Waiman.
- Explain the vCPU allocation use case and clarify permission semantics.
v1: https://lore.kernel.org/all/cover.1792067271.git.peng_wang@linux.alibaba.com/
[1] https://lore.kernel.org/all/2026101743-monotype-demeaning-f97b@gregkh/
[2] "tick/nohz: Requeue kicks flushed outside hardirq context"
Message-ID: <43668cf88d976084e2865ec07dea82b64ad402ba.1792240863.git.peng_wang@linux.alibaba.com>
Peng Wang (2):
tick/nohz: Add a policy dependency for full dynticks
cpu: Add per-CPU control of non-idle tick suppression
.../ABI/testing/sysfs-devices-system-cpu | 27 ++++++++++
drivers/base/cpu.c | 53 +++++++++++++++++++
include/linux/tick.h | 8 ++-
include/trace/events/timer.h | 3 +-
kernel/time/tick-sched.c | 42 +++++++++++++++
5 files changed, 130 insertions(+), 3 deletions(-)
base-commit: 70dd4ee72eb2d5793c62e71c14e3aba78e5a4e56
prerequisite-patch-id: 6036ece815f0ab92a0413d433675d5b240b7a8c1
--
2.47.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [RFC PATCH v2 1/2] tick/nohz: Add a policy dependency for full dynticks
2026-10-17 13:13 [RFC PATCH v2 0/2] tick/nohz: Add per-CPU control of non-idle tick suppression Peng Wang
@ 2026-10-17 13:13 ` Peng Wang
2026-10-17 13:13 ` [RFC PATCH v2 2/2] cpu: Add per-CPU control of non-idle tick suppression Peng Wang
1 sibling, 0 replies; 3+ messages in thread
From: Peng Wang @ 2026-10-17 13:13 UTC (permalink / raw)
To: Anna-Maria Behnsen, Frederic Weisbecker, Ingo Molnar,
Thomas Gleixner
Cc: Peng Wang, Greg Kroah-Hartman, Rafael J . Wysocki,
Danilo Krummrich, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Peter Zijlstra, Waiman Long, Tejun Heo,
He Rongguang, linux-kernel, linux-api, driver-core,
linux-trace-kernel, cgroups
Cloud hosts may pin vCPU threads one-to-one to host CPUs. At full
allocation this includes the housekeeping CPUs, which always retain
their non-idle tick. Operators want comparable performance for otherwise
equivalent vCPUs. Retaining the non-idle tick on nohz_full CPUs as well
avoids one placement-dependent difference in host tick interference.
With spare capacity, selected CPUs may stop the tick to reduce host
scheduler-tick interference with vCPUs.
Allocation changes as VMs are created or destroyed while other VMs
remain running. A vCPU thread may be the only runnable task in either
state, so existing tick dependencies do not express this policy.
Add a per-CPU POLICY dependency within the boot-time nohz_full set.
Permission defaults to enabled. The policy can be changed without
taking CPUs offline. Revoking permission uses the existing asynchronous
kick; granting it leaves all other dependencies effective. Preserve
POLICY across CPU hotplug and report it in tick_stop tracing.
Idle tick suppression, nohz_full membership, context tracking, RCU
offloading and housekeeping configuration are unchanged.
Co-developed-by: He Rongguang <herongguang@linux.alibaba.com>
Signed-off-by: He Rongguang <herongguang@linux.alibaba.com>
Signed-off-by: Peng Wang <peng_wang@linux.alibaba.com>
---
include/linux/tick.h | 8 +++++--
include/trace/events/timer.h | 3 ++-
kernel/time/tick-sched.c | 42 ++++++++++++++++++++++++++++++++++++
3 files changed, 50 insertions(+), 3 deletions(-)
diff --git a/include/linux/tick.h b/include/linux/tick.h
index b121c5d53e308..8b250f245ff31 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -111,9 +111,10 @@ enum tick_dep_bits {
TICK_DEP_BIT_SCHED = 2,
TICK_DEP_BIT_CLOCK_UNSTABLE = 3,
TICK_DEP_BIT_RCU = 4,
- TICK_DEP_BIT_RCU_EXP = 5
+ TICK_DEP_BIT_RCU_EXP = 5,
+ TICK_DEP_BIT_POLICY = 6
};
-#define TICK_DEP_BIT_MAX TICK_DEP_BIT_RCU_EXP
+#define TICK_DEP_BIT_MAX TICK_DEP_BIT_POLICY
#define TICK_DEP_MASK_NONE 0
#define TICK_DEP_MASK_POSIX_TIMER (1 << TICK_DEP_BIT_POSIX_TIMER)
@@ -122,6 +123,7 @@ enum tick_dep_bits {
#define TICK_DEP_MASK_CLOCK_UNSTABLE (1 << TICK_DEP_BIT_CLOCK_UNSTABLE)
#define TICK_DEP_MASK_RCU (1 << TICK_DEP_BIT_RCU)
#define TICK_DEP_MASK_RCU_EXP (1 << TICK_DEP_BIT_RCU_EXP)
+#define TICK_DEP_MASK_POLICY (1 << TICK_DEP_BIT_POLICY)
#ifdef CONFIG_NO_HZ_COMMON
extern bool tick_nohz_enabled;
@@ -205,6 +207,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 bool tick_nohz_full_get_allowed(unsigned int cpu);
+extern int tick_nohz_full_set_allowed(unsigned int cpu, bool allowed);
/*
* The below are tick_nohz_[set,clear]_dep() wrappers that optimize off-cases
diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h
index 3aa0608c63611..dff9579cd90eb 100644
--- a/include/trace/events/timer.h
+++ b/include/trace/events/timer.h
@@ -435,7 +435,8 @@ TRACE_EVENT(itimer_expire,
tick_dep_name(SCHED) \
tick_dep_name(CLOCK_UNSTABLE) \
tick_dep_name(RCU) \
- tick_dep_name_end(RCU_EXP)
+ tick_dep_name(RCU_EXP) \
+ tick_dep_name_end(POLICY)
#undef tick_dep_name
#undef tick_dep_mask_name
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 87e2a7a875265..0d6fcc3736bae 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -375,6 +375,11 @@ static bool check_tick_dependency(atomic_t *dep)
return true;
}
+ if (val & TICK_DEP_MASK_POLICY) {
+ trace_tick_stop(0, TICK_DEP_MASK_POLICY);
+ return true;
+ }
+
return false;
}
@@ -561,6 +566,39 @@ void tick_nohz_dep_clear_cpu(int cpu, enum tick_dep_bits bit)
}
EXPORT_SYMBOL_GPL(tick_nohz_dep_clear_cpu);
+/* Read policy, not whether a CPU currently has its tick stopped. */
+bool tick_nohz_full_get_allowed(unsigned int cpu)
+{
+ struct tick_sched *ts = per_cpu_ptr(&tick_cpu_sched, cpu);
+
+ /* CPU teardown resets and restores this bit under stop_machine(). */
+ return !(atomic_read(&ts->tick_dep_mask) & TICK_DEP_MASK_POLICY);
+}
+
+int tick_nohz_full_set_allowed(unsigned int cpu, bool allowed)
+{
+ struct tick_sched *ts = per_cpu_ptr(&tick_cpu_sched, cpu);
+
+ if (!tick_nohz_full_cpu(cpu))
+ return -EINVAL;
+
+ /* CPU device removal may wait for an active sysfs callback. */
+ if (!cpus_read_trylock())
+ return -EBUSY;
+
+ if (allowed) {
+ tick_nohz_dep_clear_cpu(cpu, TICK_DEP_BIT_POLICY);
+ } else if (cpu_online(cpu)) {
+ /* The existing kick requests an asynchronous restart. */
+ tick_nohz_dep_set_cpu(cpu, TICK_DEP_BIT_POLICY);
+ } else {
+ /* Record policy without queuing irq_work on an offline CPU. */
+ atomic_or(TICK_DEP_MASK_POLICY, &ts->tick_dep_mask);
+ }
+ cpus_read_unlock();
+ return 0;
+}
+
/*
* Set a per-task tick dependency. RCU needs this. Also posix CPU timers
* in order to elapse per task timers.
@@ -1533,6 +1571,7 @@ void tick_sched_timer_dying(int cpu)
{
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
unsigned long idle_calls, idle_sleeps;
+ int policy;
/* This must happen before hrtimers are migrated! */
if (tick_sched_flag_test(ts, TS_FLAG_HIGHRES))
@@ -1540,7 +1579,10 @@ void tick_sched_timer_dying(int cpu)
idle_calls = ts->idle_calls;
idle_sleeps = ts->idle_sleeps;
+ /* Preserve only the policy dependency across CPU offline/online. */
+ policy = atomic_read(&ts->tick_dep_mask) & TICK_DEP_MASK_POLICY;
memset(ts, 0, sizeof(*ts));
+ atomic_set(&ts->tick_dep_mask, policy);
ts->idle_calls = idle_calls;
ts->idle_sleeps = idle_sleeps;
}
--
2.47.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC PATCH v2 2/2] cpu: Add per-CPU control of non-idle tick suppression
2026-10-17 13:13 [RFC PATCH v2 0/2] tick/nohz: Add per-CPU control of non-idle tick suppression Peng Wang
2026-10-17 13:13 ` [RFC PATCH v2 1/2] tick/nohz: Add a policy dependency for full dynticks Peng Wang
@ 2026-10-17 13:13 ` Peng Wang
1 sibling, 0 replies; 3+ messages in thread
From: Peng Wang @ 2026-10-17 13:13 UTC (permalink / raw)
To: Anna-Maria Behnsen, Frederic Weisbecker, Ingo Molnar,
Thomas Gleixner
Cc: Peng Wang, Greg Kroah-Hartman, Rafael J . Wysocki,
Danilo Krummrich, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Peter Zijlstra, Waiman Long, Tejun Heo,
He Rongguang, linux-kernel, linux-api, driver-core,
linux-trace-kernel, cgroups
Expose cpuN/nohz_tick_allowed on CPUs in the boot-time nohz_full set.
The boolean defaults to 1: allow non-idle tick suppression, subject to
existing dependencies. Writing 0 prevents suppression through POLICY.
Readback reports permission, not the current tick state.
Each write affects only that CPU. Registered offline CPUs remain
configurable, and policy persists across offline/online transitions.
Retry writes when CPU hotplug is in progress so CPU device removal
can drain active sysfs callbacks without a lock dependency cycle.
Co-developed-by: He Rongguang <herongguang@linux.alibaba.com>
Signed-off-by: He Rongguang <herongguang@linux.alibaba.com>
Signed-off-by: Peng Wang <peng_wang@linux.alibaba.com>
---
.../ABI/testing/sysfs-devices-system-cpu | 27 ++++++++++
drivers/base/cpu.c | 53 +++++++++++++++++++
2 files changed, 80 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 82d10d556cc89..4f6ac6b41e2b6 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -767,6 +767,33 @@ Description: Preferred MTE tag checking mode
See also: Documentation/arch/arm64/memory-tagging-extension.rst
+What: /sys/devices/system/cpu/cpuX/nohz_tick_allowed
+Date: October 2026
+Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
+Description:
+ (RW) Boolean allowing this CPU to stop its non-idle scheduler
+ tick. Present only for registered CPUs in the boot-time
+ nohz_full set, with CONFIG_NO_HZ_FULL.
+
+ The default is 1 (allowed), subject to existing tick
+ dependencies. Writing 0 prevents non-idle tick suppression;
+ writing 1 allows it without forcing the tick to stop. Each
+ write affects one CPU. Readback reports permission, not the
+ current tick state.
+
+ Revoking permission requests an asynchronous restart of a
+ stopped non-idle tick on an online CPU. The write does not wait
+ for restart. Idle tick suppression is unchanged; the policy
+ applies when the CPU leaves idle.
+
+ Registered offline CPUs may be configured. Policy is retained
+ per logical CPU across offline/online and CPU device removal
+ and re-addition. CPUs without a registered cpuN device cannot
+ be configured through this interface.
+
+ This does not change nohz_full membership, context tracking,
+ RCU offloading or housekeeping configuration.
+
What: /sys/devices/system/cpu/nohz_full
Date: Apr 2015
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 69e52fed42415..6fad1b0227dac 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/cpu.h>
#include <linux/topology.h>
#include <linux/device.h>
@@ -189,7 +190,56 @@ static const struct attribute_group crash_note_cpu_attr_group = {
};
#endif
+#ifdef CONFIG_NO_HZ_FULL
+static ssize_t nohz_tick_allowed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", tick_nohz_full_get_allowed(dev->id));
+}
+
+static ssize_t nohz_tick_allowed_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ bool allowed;
+ int ret;
+
+ ret = kstrtobool(buf, &allowed);
+ if (ret)
+ return ret;
+
+ ret = tick_nohz_full_set_allowed(dev->id, allowed);
+ if (ret == -EBUSY) {
+ /* Retry after releasing the sysfs active reference. */
+ fsleep(5000);
+ return restart_syscall();
+ }
+
+ return ret ? ret : count;
+}
+static DEVICE_ATTR_RW(nohz_tick_allowed);
+
+static umode_t nohz_tick_is_visible(struct kobject *kobj,
+ struct attribute *attr, int unused)
+{
+ return tick_nohz_full_cpu(kobj_to_dev(kobj)->id) ? attr->mode : 0;
+}
+
+static struct attribute *nohz_tick_cpu_attrs[] = {
+ &dev_attr_nohz_tick_allowed.attr,
+ NULL
+};
+
+static const struct attribute_group nohz_tick_cpu_attr_group = {
+ .attrs = nohz_tick_cpu_attrs,
+ .is_visible = nohz_tick_is_visible,
+};
+#endif
+
static const struct attribute_group *common_cpu_attr_groups[] = {
+#ifdef CONFIG_NO_HZ_FULL
+ &nohz_tick_cpu_attr_group,
+#endif
#ifdef CONFIG_CRASH_DUMP
&crash_note_cpu_attr_group,
#endif
@@ -197,6 +247,9 @@ static const struct attribute_group *common_cpu_attr_groups[] = {
};
static const struct attribute_group *hotplugable_cpu_attr_groups[] = {
+#ifdef CONFIG_NO_HZ_FULL
+ &nohz_tick_cpu_attr_group,
+#endif
#ifdef CONFIG_CRASH_DUMP
&crash_note_cpu_attr_group,
#endif
--
2.47.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-17 13:14 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-17 13:13 [RFC PATCH v2 0/2] tick/nohz: Add per-CPU control of non-idle tick suppression Peng Wang
2026-10-17 13:13 ` [RFC PATCH v2 1/2] tick/nohz: Add a policy dependency for full dynticks Peng Wang
2026-10-17 13:13 ` [RFC PATCH v2 2/2] cpu: Add per-CPU control of non-idle tick suppression Peng Wang
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.