Skip to contentSkip to conversation

tick/nohz: Add runtime control of non-idle tick suppression

RFC PATCH Superseded Peng Wang 3 participants 12 messages Oct 15 – Oct 17

Subsystems (6)
Tracing Driver core cgroup ABI/API +2 more subsystems

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Peng Wang

author
#0 lore raw

cover tick/nohz: Add runtime control of non-idle tick suppression

Workloads and their need for periodic ticks can change after boot. This series lets userspace choose at runtime which CPUs in the boot-time nohz_full set retain the non-idle scheduler tick and which are permitted to stop it, while preserving the existing isolation configuration.

The first patch adds a per-CPU tick dependency; the second exposes a sysfs CPU list. Permission defaults to all nohz_full CPUs. Revocation uses the existing asynchronous kick. Idle tick suppression is unchanged, and policy persists across hotplug.

Existing cpuset partition modes do not express this independent tick policy. Feedback on the proposed interface is welcome.

Thanks,
Peng

Peng Wang (2):
tick/nohz: Add a policy dependency for full dynticks
cpu: Add sysfs control of full dynticks policy

 .../ABI/testing/sysfs-devices-system-cpu      | 40 ++++++++++++
 drivers/base/cpu.c                            | 46 ++++++++++++++
 include/linux/tick.h                          |  8 ++-
 include/trace/events/timer.h                  |  3 +-
 kernel/time/tick-sched.c                      | 62 +++++++++++++++++++
 5 files changed, 156 insertions(+), 3 deletions(-)

base-commit: 0fcb05b72344f40806cbe810de8872f1698bf053

author
#1 lore raw

RFC PATCH 1/2 tick/nohz: Add a policy dependency for full dynticks

Workloads and their need for periodic ticks can change after boot. Operators may need to retain the non-idle scheduler tick on selected CPUs in the boot-time nohz_full set while permitting tick suppression on others, without changing the isolation configuration. The policy can be changed without taking CPUs offline.

Add a per-CPU POLICY dependency to inhibit non-idle tick suppression. All nohz_full CPUs remain permitted by default. Revocation uses the existing asynchronous kick; granting permission clears only POLICY. Preserve the policy across hotplug and recognize it in tick_stop tracing. Idle tick suppression is unchanged.

---
 include/linux/tick.h         |  8 +++--
 include/trace/events/timer.h |  3 +-
 kernel/time/tick-sched.c     | 62 ++++++++++++++++++++++++++++++++++++
 3 files changed, 70 insertions(+), 3 deletions(-)
include/linux/tick.h +6 -2
index b121c5d53e308..08d272dca0947 100644
@@ -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 void tick_nohz_full_get_allowed(struct cpumask *mask);
+extern int tick_nohz_full_set_allowed(const struct cpumask *mask);
 
 /*
  * The below are tick_nohz_[set,clear]_dep() wrappers that optimize off-cases
include/trace/events/timer.h +2 -1
index 3aa0608c63611..dff9579cd90eb 100644
@@ -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
kernel/time/tick-sched.c +62 -0

99 lines not on this page · raw message

author
#2 lore raw

RFC PATCH 2/2 cpu: Add sysfs control of full dynticks policy

In reply to Peng Wang

Expose nohz_full_allowed under /sys/devices/system/cpu/ as the subset of nohz_full CPUs permitted to suppress their non-idle scheduler tick.

Writes replace the entire list; reads report policy, not current tick state. Reject invalid input before updating policy. Document asynchronous revocation, offline CPU configuration and persistence across hotplug. The existing nohz_full attribute continues to report membership.

---
 .../ABI/testing/sysfs-devices-system-cpu      | 40 ++++++++++++++++
 drivers/base/cpu.c                            | 46 +++++++++++++++++++
 2 files changed, 86 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +40 -0
index 82d10d556cc89..edf3408fd84c8 100644
@@ -767,6 +767,46 @@ Description:	Preferred MTE tag checking mode
 
 		See also: Documentation/arch/arm64/memory-tagging-extension.rst
 
+What:		/sys/devices/system/cpu/nohz_full_allowed
+Date:		October 2026
+Contact:	Linux kernel mailing list <[email protected]>
+Description:
+		(RW) CPUs permitted to stop the scheduler tick while non-idle.
+		Available with CONFIG_NO_HZ_FULL. The default is the complete
+		nohz_full CPU list. Readback reports permission, not the current
+		tick state: all the usual tick dependencies still apply.
+
+		A write replaces the entire list, using CPU-list syntax (for
+		example, "1-3,5"). An empty line revokes permission from all
+		nohz_full CPUs. Invalid input is rejected without changing
+		policy. A parsed list containing CPUs outside nohz_full fails
+		with EINVAL. CPU numbers beyond the mask range and numeric
+		overflows are rejected by the CPU-list parser with ERANGE and
+		EOVERFLOW, respectively. Input must be shorter than PAGE_SIZE
+		bytes, otherwise E2BIG is returned. Embedded NULs and newlines
+		other than a trailing newline are rejected with EINVAL. Valid
+		writes without an active nohz_full configuration fail with
+		EOPNOTSUPP.
+
+		Reads fail with EOVERFLOW if the formatted output reaches
+		PAGE_SIZE - 1 bytes, rather than returning a potentially
+		truncated CPU list.
+
+		Revoking permission adds a per-CPU tick dependency. Online CPUs
+		are notified through the existing asynchronous kick mechanism
+		to restart a stopped non-idle tick. A successful write means
+		the policy is updated and any needed notification is queued;
+		it does not wait for tick restart. Changes to multiple CPUs
+		are not simultaneous. Granting permission only removes this
+		dependency and does not force the tick to stop.
+
+		Offline CPUs may be configured without sending notifications.
+		Policy persists across CPU offline/online. Idle tick suppression
+		is unchanged; the policy applies when a CPU leaves idle.
+
+		This interface 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 <[email protected]>
drivers/base/cpu.c +46 -0

58 lines not on this page · raw message

#3 lore raw

Re: [RFC PATCH 2/2] cpu: Add sysfs control of full dynticks policy

Quoted 21 lines · Peng Wang

Expose nohz_full_allowed under /sys/devices/system/cpu/ as the subset of nohz_full CPUs permitted to suppress their non-idle scheduler tick.

Writes replace the entire list; reads report policy, not current tick state. Reject invalid input before updating policy. Document asynchronous revocation, offline CPU configuration and persistence across hotplug. The existing nohz_full attribute continues to report membership.

---
  .../ABI/testing/sysfs-devices-system-cpu      | 40 ++++++++++++++++
  drivers/base/cpu.c                            | 46 +++++++++++++++++++
  2 files changed, 86 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +0 -0
index 82d10d556cc89..edf3408fd84c8 100644

Documentation/ABI/testing/sysfs-devices-system-cpu Description: Preferred MTE tag checking mode · line 770

Quoted 4 lines
Documentation/ABI/testing/sysfs-devices-system-cpu +1 -0
@@ -767,6 +767,46 @@ Description:	Preferred MTE tag checking mode
 
  		See also: Documentation/arch/arm64/memory-tagging-extension.rst
 
+What:		/sys/devices/system/cpu/nohz_full_allowed

I do have a problem with the name of this sysfs file. nohz_full is more than just the tick. The naming may introduce the wrong impression that all nohz_full behavior can be controlled. May be a name like "nohz_tick_allowed" which is much more specific will be better.

Regards,
Longman

Quoted 106 lines
Documentation/ABI/testing/sysfs-devices-system-cpu +39 -0
+Date:		October 2026
+Contact:	Linux kernel mailing list <[email protected]>
+Description:
+		(RW) CPUs permitted to stop the scheduler tick while non-idle.
+		Available with CONFIG_NO_HZ_FULL. The default is the complete
+		nohz_full CPU list. Readback reports permission, not the current
+		tick state: all the usual tick dependencies still apply.
+
+		A write replaces the entire list, using CPU-list syntax (for
+		example, "1-3,5"). An empty line revokes permission from all
+		nohz_full CPUs. Invalid input is rejected without changing
+		policy. A parsed list containing CPUs outside nohz_full fails
+		with EINVAL. CPU numbers beyond the mask range and numeric
+		overflows are rejected by the CPU-list parser with ERANGE and
+		EOVERFLOW, respectively. Input must be shorter than PAGE_SIZE
+		bytes, otherwise E2BIG is returned. Embedded NULs and newlines
+		other than a trailing newline are rejected with EINVAL. Valid
+		writes without an active nohz_full configuration fail with
+		EOPNOTSUPP.
+
+		Reads fail with EOVERFLOW if the formatted output reaches
+		PAGE_SIZE - 1 bytes, rather than returning a potentially
+		truncated CPU list.
+
+		Revoking permission adds a per-CPU tick dependency. Online CPUs
+		are notified through the existing asynchronous kick mechanism
+		to restart a stopped non-idle tick. A successful write means
+		the policy is updated and any needed notification is queued;
+		it does not wait for tick restart. Changes to multiple CPUs
+		are not simultaneous. Granting permission only removes this
+		dependency and does not force the tick to stop.
+
+		Offline CPUs may be configured without sending notifications.
+		Policy persists across CPU offline/online. Idle tick suppression
+		is unchanged; the policy applies when a CPU leaves idle.
+
+		This interface 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 <[email protected]>
drivers/base/cpu.c +46 -0
index 69e52fed42415..299ce61cef895 100644
@@ -324,6 +324,51 @@ static ssize_t nohz_full_show(struct device *dev,
  	return sysfs_emit(buf, "\n");
  }
  static DEVICE_ATTR_RO(nohz_full);
+
+static ssize_t nohz_full_allowed_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	cpumask_var_t mask;
+	ssize_t ret;
+
+	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+		return -ENOMEM;
+
+	tick_nohz_full_get_allowed(mask);
+	ret = sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(mask));
+
+	free_cpumask_var(mask);
+
+	/* Never present a truncated CPU list as the complete policy. */
+	return ret == PAGE_SIZE - 1 ? -EOVERFLOW : ret;
+}
+
+static ssize_t nohz_full_allowed_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	cpumask_var_t mask;
+	int ret;
+
+	/* kernfs may truncate an oversized write to PAGE_SIZE. */
+	if (count >= PAGE_SIZE)
+		return -E2BIG;
+	if (memchr(buf, '\0', count) ||
+	    (count > 1 && memchr(buf, '\n', count - 1)))
+		return -EINVAL;
+
+	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+		return -ENOMEM;
+
+	ret = cpulist_parse(buf, mask);
+	if (!ret)
+		ret = tick_nohz_full_set_allowed(mask);
+
+	free_cpumask_var(mask);
+
+	return ret ? ret : count;
+}
+static DEVICE_ATTR_RW(nohz_full_allowed);
  #endif
 
  #ifdef CONFIG_CRASH_HOTPLUG
@@ -525,6 +570,7 @@ static struct attribute *cpu_root_attrs[] = {
  	&dev_attr_housekeeping.attr,
  #ifdef CONFIG_NO_HZ_FULL
  	&dev_attr_nohz_full.attr,
+	&dev_attr_nohz_full_allowed.attr,
  #endif
  #ifdef CONFIG_CRASH_HOTPLUG
  	&dev_attr_crash_hotplug.attr,
author
#4 lore raw

Re: [RFC PATCH 2/2] cpu: Add sysfs control of full dynticks policy

Quoted 18 lines · Waiman Long
Quoted 16 lines · Peng Wang

Expose nohz_full_allowed under /sys/devices/system/cpu/ as the subset of nohz_full CPUs permitted to suppress their non-idle scheduler tick.

Writes replace the entire list; reads report policy, not current tick state. Reject invalid input before updating policy. Document asynchronous revocation, offline CPU configuration and persistence across hotplug. The existing nohz_full attribute continues to report membership.

---
  .../ABI/testing/sysfs-devices-system-cpu      | 40 ++++++++++++++++
  drivers/base/cpu.c                            | 46 +++++++++++++++++++
  2 files changed, 86 insertions(+)
Quoted 12 lines

Documentation/ABI/testing/sysfs-devices-system-cpu Description: Preferred MTE tag checking mode · line 768

Quoted 7 lines
Documentation/ABI/testing/sysfs-devices-system-cpu +1 -0
index 82d10d556cc89..edf3408fd84c8 100644
@@ -767,6 +767,46 @@ Description:	Preferred MTE tag checking mode
  		See also: Documentation/arch/arm64/memory-tagging-extension.rst
+What:		/sys/devices/system/cpu/nohz_full_allowed

I do have a problem with the name of this sysfs file. nohz_full is more than just the tick. The naming may introduce the wrong impression that all nohz_full behavior can be controlled. May be a name like "nohz_tick_allowed" which is much more specific will be better.

Hi Waiman,

Agreed, nohz_tick_allowed describes the scope more accurately. If the control remains in sysfs, I'll use that name in the next version.

Thanks,
Peng

Quoted 110 lines

Regards,
Longman

Quoted 105 lines
Documentation/ABI/testing/sysfs-devices-system-cpu +39 -0
+Date:		October 2026
+Contact:	Linux kernel mailing list <[email protected]>
+Description:
+		(RW) CPUs permitted to stop the scheduler tick while non-idle.
+		Available with CONFIG_NO_HZ_FULL. The default is the complete
+		nohz_full CPU list. Readback reports permission, not the current
+		tick state: all the usual tick dependencies still apply.
+
+		A write replaces the entire list, using CPU-list syntax (for
+		example, "1-3,5"). An empty line revokes permission from all
+		nohz_full CPUs. Invalid input is rejected without changing
+		policy. A parsed list containing CPUs outside nohz_full fails
+		with EINVAL. CPU numbers beyond the mask range and numeric
+		overflows are rejected by the CPU-list parser with ERANGE and
+		EOVERFLOW, respectively. Input must be shorter than PAGE_SIZE
+		bytes, otherwise E2BIG is returned. Embedded NULs and newlines
+		other than a trailing newline are rejected with EINVAL. Valid
+		writes without an active nohz_full configuration fail with
+		EOPNOTSUPP.
+
+		Reads fail with EOVERFLOW if the formatted output reaches
+		PAGE_SIZE - 1 bytes, rather than returning a potentially
+		truncated CPU list.
+
+		Revoking permission adds a per-CPU tick dependency. Online CPUs
+		are notified through the existing asynchronous kick mechanism
+		to restart a stopped non-idle tick. A successful write means
+		the policy is updated and any needed notification is queued;
+		it does not wait for tick restart. Changes to multiple CPUs
+		are not simultaneous. Granting permission only removes this
+		dependency and does not force the tick to stop.
+
+		Offline CPUs may be configured without sending notifications.
+		Policy persists across CPU offline/online. Idle tick suppression
+		is unchanged; the policy applies when a CPU leaves idle.
+
+		This interface 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 <[email protected]>
drivers/base/cpu.c +46 -0
index 69e52fed42415..299ce61cef895 100644
@@ -324,6 +324,51 @@ static ssize_t nohz_full_show(struct device *dev,
  	return sysfs_emit(buf, "\n");
  }
  static DEVICE_ATTR_RO(nohz_full);
+
+static ssize_t nohz_full_allowed_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	cpumask_var_t mask;
+	ssize_t ret;
+
+	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+		return -ENOMEM;
+
+	tick_nohz_full_get_allowed(mask);
+	ret = sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(mask));
+
+	free_cpumask_var(mask);
+
+	/* Never present a truncated CPU list as the complete policy. */
+	return ret == PAGE_SIZE - 1 ? -EOVERFLOW : ret;
+}
+
+static ssize_t nohz_full_allowed_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	cpumask_var_t mask;
+	int ret;
+
+	/* kernfs may truncate an oversized write to PAGE_SIZE. */
+	if (count >= PAGE_SIZE)
+		return -E2BIG;
+	if (memchr(buf, '\0', count) ||
+	    (count > 1 && memchr(buf, '\n', count - 1)))
+		return -EINVAL;
+
+	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+		return -ENOMEM;
+
+	ret = cpulist_parse(buf, mask);
+	if (!ret)
+		ret = tick_nohz_full_set_allowed(mask);
+
+	free_cpumask_var(mask);
+
+	return ret ? ret : count;
+}
+static DEVICE_ATTR_RW(nohz_full_allowed);
  #endif
  #ifdef CONFIG_CRASH_HOTPLUG
@@ -525,6 +570,7 @@ static struct attribute *cpu_root_attrs[] = {
  	&dev_attr_housekeeping.attr,
  #ifdef CONFIG_NO_HZ_FULL
  	&dev_attr_nohz_full.attr,
+	&dev_attr_nohz_full_allowed.attr,
  #endif
  #ifdef CONFIG_CRASH_HOTPLUG
  	&dev_attr_crash_hotplug.attr,
#5 lore raw

Re: [RFC PATCH 2/2] cpu: Add sysfs control of full dynticks policy

In reply to Peng Wang
Quoted 82 lines · Peng Wang

Expose nohz_full_allowed under /sys/devices/system/cpu/ as the subset of nohz_full CPUs permitted to suppress their non-idle scheduler tick.

Writes replace the entire list; reads report policy, not current tick state. Reject invalid input before updating policy. Document asynchronous revocation, offline CPU configuration and persistence across hotplug. The existing nohz_full attribute continues to report membership.

---
 .../ABI/testing/sysfs-devices-system-cpu      | 40 ++++++++++++++++
 drivers/base/cpu.c                            | 46 +++++++++++++++++++
 2 files changed, 86 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +40 -0
index 82d10d556cc89..edf3408fd84c8 100644
@@ -767,6 +767,46 @@ Description:	Preferred MTE tag checking mode
 
 		See also: Documentation/arch/arm64/memory-tagging-extension.rst
 
+What:		/sys/devices/system/cpu/nohz_full_allowed
+Date:		October 2026
+Contact:	Linux kernel mailing list <[email protected]>
+Description:
+		(RW) CPUs permitted to stop the scheduler tick while non-idle.
+		Available with CONFIG_NO_HZ_FULL. The default is the complete
+		nohz_full CPU list. Readback reports permission, not the current
+		tick state: all the usual tick dependencies still apply.
+
+		A write replaces the entire list, using CPU-list syntax (for
+		example, "1-3,5"). An empty line revokes permission from all
+		nohz_full CPUs. Invalid input is rejected without changing
+		policy. A parsed list containing CPUs outside nohz_full fails
+		with EINVAL. CPU numbers beyond the mask range and numeric
+		overflows are rejected by the CPU-list parser with ERANGE and
+		EOVERFLOW, respectively. Input must be shorter than PAGE_SIZE
+		bytes, otherwise E2BIG is returned. Embedded NULs and newlines
+		other than a trailing newline are rejected with EINVAL. Valid
+		writes without an active nohz_full configuration fail with
+		EOPNOTSUPP.
+
+		Reads fail with EOVERFLOW if the formatted output reaches
+		PAGE_SIZE - 1 bytes, rather than returning a potentially
+		truncated CPU list.
+
+		Revoking permission adds a per-CPU tick dependency. Online CPUs
+		are notified through the existing asynchronous kick mechanism
+		to restart a stopped non-idle tick. A successful write means
+		the policy is updated and any needed notification is queued;
+		it does not wait for tick restart. Changes to multiple CPUs
+		are not simultaneous. Granting permission only removes this
+		dependency and does not force the tick to stop.
+
+		Offline CPUs may be configured without sending notifications.
+		Policy persists across CPU offline/online. Idle tick suppression
+		is unchanged; the policy applies when a CPU leaves idle.
+
+		This interface 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 <[email protected]>
drivers/base/cpu.c +7 -0
index 69e52fed42415..299ce61cef895 100644
@@ -324,6 +324,51 @@ static ssize_t nohz_full_show(struct device *dev,
 	return sysfs_emit(buf, "\n");
 }
 static DEVICE_ATTR_RO(nohz_full);
+
+static ssize_t nohz_full_allowed_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	cpumask_var_t mask;
+	ssize_t ret;
+

drivers/base/cpu.c static ssize_t nohz_full_show(struct device *dev, · line 343

Quoted 10 lines
drivers/base/cpu.c +10 -0
+	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+		return -ENOMEM;
+
+	tick_nohz_full_get_allowed(mask);
+	ret = sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(mask));
+
+	free_cpumask_var(mask);
+
+	/* Never present a truncated CPU list as the complete policy. */
+	return ret == PAGE_SIZE - 1 ? -EOVERFLOW : ret;

Why not? What will userspace do then if it gets -EOVERFLOW as it can not get the full list?

drivers/base/cpu.c static ssize_t nohz_full_show(struct device *dev, · line 355

Quoted 12 lines
drivers/base/cpu.c +12 -0
+}
+
+static ssize_t nohz_full_allowed_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	cpumask_var_t mask;
+	int ret;
+
+	/* kernfs may truncate an oversized write to PAGE_SIZE. */
+	if (count >= PAGE_SIZE)
+		return -E2BIG;

How are you getting a larger than PAGE_SIZE value here?

drivers/base/cpu.c static ssize_t nohz_full_show(struct device *dev, · line 358

Quoted 3 lines
drivers/base/cpu.c +3 -0
+	if (memchr(buf, '\0', count) ||
+	    (count > 1 && memchr(buf, '\n', count - 1)))
+		return -EINVAL;

What are you testing for here, sorry, I can't easily figure it out.

thanks,

greg k-h

author
#6 lore raw

Re: [RFC PATCH 2/2] cpu: Add sysfs control of full dynticks policy

Quoted 88 lines · Greg Kroah-Hartman
Quoted 87 lines · Peng Wang

Expose nohz_full_allowed under /sys/devices/system/cpu/ as the subset of nohz_full CPUs permitted to suppress their non-idle scheduler tick.

Writes replace the entire list; reads report policy, not current tick state. Reject invalid input before updating policy. Document asynchronous revocation, offline CPU configuration and persistence across hotplug. The existing nohz_full attribute continues to report membership.

---
 .../ABI/testing/sysfs-devices-system-cpu      | 40 ++++++++++++++++
 drivers/base/cpu.c                            | 46 +++++++++++++++++++
 2 files changed, 86 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +40 -0
index 82d10d556cc89..edf3408fd84c8 100644
@@ -767,6 +767,46 @@ Description:	Preferred MTE tag checking mode
 
 		See also: Documentation/arch/arm64/memory-tagging-extension.rst
 
+What:		/sys/devices/system/cpu/nohz_full_allowed
+Date:		October 2026
+Contact:	Linux kernel mailing list <[email protected]>
+Description:
+		(RW) CPUs permitted to stop the scheduler tick while non-idle.
+		Available with CONFIG_NO_HZ_FULL. The default is the complete
+		nohz_full CPU list. Readback reports permission, not the current
+		tick state: all the usual tick dependencies still apply.
+
+		A write replaces the entire list, using CPU-list syntax (for
+		example, "1-3,5"). An empty line revokes permission from all
+		nohz_full CPUs. Invalid input is rejected without changing
+		policy. A parsed list containing CPUs outside nohz_full fails
+		with EINVAL. CPU numbers beyond the mask range and numeric
+		overflows are rejected by the CPU-list parser with ERANGE and
+		EOVERFLOW, respectively. Input must be shorter than PAGE_SIZE
+		bytes, otherwise E2BIG is returned. Embedded NULs and newlines
+		other than a trailing newline are rejected with EINVAL. Valid
+		writes without an active nohz_full configuration fail with
+		EOPNOTSUPP.
+
+		Reads fail with EOVERFLOW if the formatted output reaches
+		PAGE_SIZE - 1 bytes, rather than returning a potentially
+		truncated CPU list.
+
+		Revoking permission adds a per-CPU tick dependency. Online CPUs
+		are notified through the existing asynchronous kick mechanism
+		to restart a stopped non-idle tick. A successful write means
+		the policy is updated and any needed notification is queued;
+		it does not wait for tick restart. Changes to multiple CPUs
+		are not simultaneous. Granting permission only removes this
+		dependency and does not force the tick to stop.
+
+		Offline CPUs may be configured without sending notifications.
+		Policy persists across CPU offline/online. Idle tick suppression
+		is unchanged; the policy applies when a CPU leaves idle.
+
+		This interface 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 <[email protected]>
drivers/base/cpu.c +12 -0
index 69e52fed42415..299ce61cef895 100644
@@ -324,6 +324,51 @@ static ssize_t nohz_full_show(struct device *dev,
 	return sysfs_emit(buf, "\n");
 }
 static DEVICE_ATTR_RO(nohz_full);
+
+static ssize_t nohz_full_allowed_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	cpumask_var_t mask;
+	ssize_t ret;
+
+	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+		return -ENOMEM;
+
+	tick_nohz_full_get_allowed(mask);
+	ret = sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(mask));
Quoted 8 lines

drivers/base/cpu.c static ssize_t nohz_full_show(struct device *dev, · line 343

Quoted 5 lines
drivers/base/cpu.c +5 -0
+
+	free_cpumask_var(mask);
+
+	/* Never present a truncated CPU list as the complete policy. */
+	return ret == PAGE_SIZE - 1 ? -EOVERFLOW : ret;

Why not? What will userspace do then if it gets -EOVERFLOW as it can not get the full list?

Hi Greg,

There is no way to retrieve the full list through this attribute. The intent was to avoid returning a truncated list that userspace could write back, unintentionally revoking permission for the omitted CPUs. Returning an error does not solve that interface limitation.

Quoted 15 lines

drivers/base/cpu.c static ssize_t nohz_full_show(struct device *dev, · line 355

Quoted 13 lines
drivers/base/cpu.c +12 -0
+}
+
+static ssize_t nohz_full_allowed_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	cpumask_var_t mask;
+	int ret;
+
+	/* kernfs may truncate an oversized write to PAGE_SIZE. */
+	if (count >= PAGE_SIZE)
+		return -E2BIG;

How are you getting a larger than PAGE_SIZE value here?

I cannot; kernfs caps count at PAGE_SIZE. The check is intended for count == PAGE_SIZE, to avoid applying a potentially truncated prefix as a complete list. It also rejects an exactly page-sized input.

Quoted 6 lines

drivers/base/cpu.c static ssize_t nohz_full_show(struct device *dev, · line 358

Quoted 4 lines
drivers/base/cpu.c +3 -0
+	if (memchr(buf, '\0', count) ||
+	    (count > 1 && memchr(buf, '\n', count - 1)))
+		return -EINVAL;

What are you testing for here, sorry, I can't easily figure it out.

The checks reject user-supplied NULs and non-final newlines. For example, cpulist_parse() accepts "1\n2\n" as CPU 1 only, while store() would report the full input as consumed. The NUL appended by kernfs is at buf[count], outside the checked range.

I propose replacing the global attribute with /sys/devices/system/cpu/cpuN/nohz_tick_allowed, available only on nohz_full CPUs. Writing 0 would prevent non-idle tick suppression, using the existing asynchronous kick when needed. Writing 1 would allow suppression, subject to the existing tick dependencies. Readback would report policy, not the current tick state.

Each write would affect only that CPU, without changing nohz_full membership or other isolation settings. This would give up the single-write whole-list replacement. Would that be a better fit?

Thanks,
Peng

Quoted 4 lines

thanks,

greg k-h

#7 lore raw

Re: [RFC PATCH 2/2] cpu: Add sysfs control of full dynticks policy

Quoted 98 lines · Peng Wang
Quoted 88 lines · Greg Kroah-Hartman
Quoted 87 lines · Peng Wang

Expose nohz_full_allowed under /sys/devices/system/cpu/ as the subset of nohz_full CPUs permitted to suppress their non-idle scheduler tick.

Writes replace the entire list; reads report policy, not current tick state. Reject invalid input before updating policy. Document asynchronous revocation, offline CPU configuration and persistence across hotplug. The existing nohz_full attribute continues to report membership.

---
 .../ABI/testing/sysfs-devices-system-cpu      | 40 ++++++++++++++++
 drivers/base/cpu.c                            | 46 +++++++++++++++++++
 2 files changed, 86 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +40 -0
index 82d10d556cc89..edf3408fd84c8 100644
@@ -767,6 +767,46 @@ Description:	Preferred MTE tag checking mode
 
 		See also: Documentation/arch/arm64/memory-tagging-extension.rst
 
+What:		/sys/devices/system/cpu/nohz_full_allowed
+Date:		October 2026
+Contact:	Linux kernel mailing list <[email protected]>
+Description:
+		(RW) CPUs permitted to stop the scheduler tick while non-idle.
+		Available with CONFIG_NO_HZ_FULL. The default is the complete
+		nohz_full CPU list. Readback reports permission, not the current
+		tick state: all the usual tick dependencies still apply.
+
+		A write replaces the entire list, using CPU-list syntax (for
+		example, "1-3,5"). An empty line revokes permission from all
+		nohz_full CPUs. Invalid input is rejected without changing
+		policy. A parsed list containing CPUs outside nohz_full fails
+		with EINVAL. CPU numbers beyond the mask range and numeric
+		overflows are rejected by the CPU-list parser with ERANGE and
+		EOVERFLOW, respectively. Input must be shorter than PAGE_SIZE
+		bytes, otherwise E2BIG is returned. Embedded NULs and newlines
+		other than a trailing newline are rejected with EINVAL. Valid
+		writes without an active nohz_full configuration fail with
+		EOPNOTSUPP.
+
+		Reads fail with EOVERFLOW if the formatted output reaches
+		PAGE_SIZE - 1 bytes, rather than returning a potentially
+		truncated CPU list.
+
+		Revoking permission adds a per-CPU tick dependency. Online CPUs
+		are notified through the existing asynchronous kick mechanism
+		to restart a stopped non-idle tick. A successful write means
+		the policy is updated and any needed notification is queued;
+		it does not wait for tick restart. Changes to multiple CPUs
+		are not simultaneous. Granting permission only removes this
+		dependency and does not force the tick to stop.
+
+		Offline CPUs may be configured without sending notifications.
+		Policy persists across CPU offline/online. Idle tick suppression
+		is unchanged; the policy applies when a CPU leaves idle.
+
+		This interface 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 <[email protected]>
drivers/base/cpu.c +12 -0
index 69e52fed42415..299ce61cef895 100644
@@ -324,6 +324,51 @@ static ssize_t nohz_full_show(struct device *dev,
 	return sysfs_emit(buf, "\n");
 }
 static DEVICE_ATTR_RO(nohz_full);
+
+static ssize_t nohz_full_allowed_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	cpumask_var_t mask;
+	ssize_t ret;
+
+	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+		return -ENOMEM;
+
+	tick_nohz_full_get_allowed(mask);
+	ret = sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(mask));
Quoted 8 lines

drivers/base/cpu.c static ssize_t nohz_full_show(struct device *dev, · line 343

Quoted 5 lines
drivers/base/cpu.c +5 -0
+
+	free_cpumask_var(mask);
+
+	/* Never present a truncated CPU list as the complete policy. */
+	return ret == PAGE_SIZE - 1 ? -EOVERFLOW : ret;

Why not? What will userspace do then if it gets -EOVERFLOW as it can not get the full list?

Quoted 6 lines

Hi Greg,

There is no way to retrieve the full list through this attribute. The intent was to avoid returning a truncated list that userspace could write back, unintentionally revoking permission for the omitted CPUs. Returning an error does not solve that interface limitation.

If you _MUST_ have such a size of a sysfs file, then use a binary attribute which can accept any size. I think we have had to do that for other sysfs files that show this information in the past, right? Please check them.

Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
drivers/base/cpu.c +6 -0
+}
+
+static ssize_t nohz_full_allowed_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
Quoted 8 lines

drivers/base/cpu.c static ssize_t nohz_full_show(struct device *dev, · line 355

Quoted 6 lines
drivers/base/cpu.c +6 -0
+	cpumask_var_t mask;
+	int ret;
+
+	/* kernfs may truncate an oversized write to PAGE_SIZE. */
+	if (count >= PAGE_SIZE)
+		return -E2BIG;

How are you getting a larger than PAGE_SIZE value here?

Quoted 3 lines

I cannot; kernfs caps count at PAGE_SIZE. The check is intended for count == PAGE_SIZE, to avoid applying a potentially truncated prefix as a complete list. It also rejects an exactly page-sized input.

If the check is for == PAGE_SIZE then check for that, not for something that can never happen :)

But again, don't do this at all by using a binary attribute if you are going to be needing to accept larger amounts of data. Don't create a known-broken api from the start please.

Quoted 11 lines
Quoted 6 lines

drivers/base/cpu.c static ssize_t nohz_full_show(struct device *dev, · line 358

Quoted 4 lines
drivers/base/cpu.c +3 -0
+	if (memchr(buf, '\0', count) ||
+	    (count > 1 && memchr(buf, '\n', count - 1)))
+		return -EINVAL;

What are you testing for here, sorry, I can't easily figure it out.

The checks reject user-supplied NULs and non-final newlines. For example, cpulist_parse() accepts "1\n2\n" as CPU 1 only, while store() would report the full input as consumed. The NUL appended by kernfs is at buf[count], outside the checked range.

Please document that in a comment here.

Quoted 10 lines

I propose replacing the global attribute with /sys/devices/system/cpu/cpuN/nohz_tick_allowed, available only on nohz_full CPUs. Writing 0 would prevent non-idle tick suppression, using the existing asynchronous kick when needed. Writing 1 would allow suppression, subject to the existing tick dependencies. Readback would report policy, not the current tick state.

Each write would affect only that CPU, without changing nohz_full membership or other isolation settings. This would give up the single-write whole-list replacement. Would that be a better fit?

That's much simpler, so I'm all for that from a sysfs point of view, thanks.

greg k-h

#8 lore raw
In reply to Peng Wang
Quoted 9 lines · Peng Wang

Workloads and their need for periodic ticks can change after boot. This series lets userspace choose at runtime which CPUs in the boot-time nohz_full set retain the non-idle scheduler tick and which are permitted to stop it, while preserving the existing isolation configuration.

The first patch adds a per-CPU tick dependency; the second exposes a sysfs CPU list. Permission defaults to all nohz_full CPUs. Revocation uses the existing asynchronous kick. Idle tick suppression is unchanged, and policy persists across hotplug.

So you are basically setting up more nohz_full CPUs than the system actually needs to use at the moment and so want to disable nohz/tick part of nohz_full CPUs that don't need it. Can you elaborate more about the problems solve by introducing this new behavior?

Quoted 3 lines

Existing cpuset partition modes do not express this independent tick policy. Feedback on the proposed interface is welcome.

We are planning to eventually extend cpuset partition mode to change nohz_full at runtime. This is still a work in progress.

Regards,
Longman

Quoted 17 lines

Thanks,
Peng

Peng Wang (2):
tick/nohz: Add a policy dependency for full dynticks
cpu: Add sysfs control of full dynticks policy

  .../ABI/testing/sysfs-devices-system-cpu      | 40 ++++++++++++
  drivers/base/cpu.c                            | 46 ++++++++++++++
  include/linux/tick.h                          |  8 ++-
  include/trace/events/timer.h                  |  3 +-
  kernel/time/tick-sched.c                      | 62 +++++++++++++++++++
  5 files changed, 156 insertions(+), 3 deletions(-)

base-commit: 0fcb05b72344f40806cbe810de8872f1698bf053

author
#9 lore raw
Quoted 6 lines · Waiman Long
Quoted 5 lines · Peng Wang

Workloads and their need for periodic ticks can change after boot. This series lets userspace choose at runtime which CPUs in the boot-time nohz_full set retain the non-idle scheduler tick and which are permitted to stop it, while preserving the existing isolation configuration.

Quoted 8 lines
Quoted 4 lines

The first patch adds a per-CPU tick dependency; the second exposes a sysfs CPU list. Permission defaults to all nohz_full CPUs. Revocation uses the existing asynchronous kick. Idle tick suppression is unchanged, and policy persists across hotplug.

So you are basically setting up more nohz_full CPUs than the system actually needs to use at the moment and so want to disable nohz/tick part of nohz_full CPUs that don't need it. Can you elaborate more about the problems solve by introducing this new behavior?

Hi Waiman,

Yes. We have CPU-pinned workloads that benefit from reduced periodic tick interference. The desired tick policy changes with CPU allocation.

With spare capacity, we allow selected nohz_full CPUs to stop their tick. When capacity is fully allocated, we prefer to retain the non-idle periodic tick across CPUs, avoiding an additional difference in tick interference between housekeeping and nohz_full CPUs. A CPU may still have only one runnable task in either case, so the existing tick dependencies do not express this policy.

This series keeps the boot-time nohz_full CPU set, context tracking, RCU offloading and housekeeping configuration unchanged. It only controls whether those CPUs may stop their non-idle tick. The policy can be changed without taking CPUs offline.

Quoted 6 lines
Quoted 3 lines

Existing cpuset partition modes do not express this independent tick policy. Feedback on the proposed interface is welcome.

We are planning to eventually extend cpuset partition mode to change nohz_full at runtime. This is still a work in progress.

Thanks for mentioning the cpuset work. Would this tick-only policy fit into that plan? The core mechanism in patch 1 is independent of the sysfs interface.

Thanks,
Peng

Quoted 22 lines

Regards,
Longman

Quoted 17 lines

Thanks,
Peng

Peng Wang (2):
tick/nohz: Add a policy dependency for full dynticks
cpu: Add sysfs control of full dynticks policy

  .../ABI/testing/sysfs-devices-system-cpu      | 40 ++++++++++++
  drivers/base/cpu.c                            | 46 ++++++++++++++
  include/linux/tick.h                          |  8 ++-
  include/trace/events/timer.h                  |  3 +-
  kernel/time/tick-sched.c                      | 62 +++++++++++++++++++
  5 files changed, 156 insertions(+), 3 deletions(-)

base-commit: 0fcb05b72344f40806cbe810de8872f1698bf053

#10 lore raw
Quoted 17 lines · Peng Wang
Quoted 6 lines · Waiman Long
Quoted 5 lines · Peng Wang

Workloads and their need for periodic ticks can change after boot. This series lets userspace choose at runtime which CPUs in the boot-time nohz_full set retain the non-idle scheduler tick and which are permitted to stop it, while preserving the existing isolation configuration.

Quoted 8 lines
Quoted 4 lines

The first patch adds a per-CPU tick dependency; the second exposes a sysfs CPU list. Permission defaults to all nohz_full CPUs. Revocation uses the existing asynchronous kick. Idle tick suppression is unchanged, and policy persists across hotplug.

So you are basically setting up more nohz_full CPUs than the system actually needs to use at the moment and so want to disable nohz/tick part of nohz_full CPUs that don't need it. Can you elaborate more about the problems solve by introducing this new behavior?

Hi Waiman,

Quoted 7 lines

Yes. We have CPU-pinned workloads that benefit from reduced periodic tick interference. The desired tick policy changes with CPU allocation.

With spare capacity, we allow selected nohz_full CPUs to stop their tick. When capacity is fully allocated, we prefer to retain the non-idle periodic tick across CPUs, avoiding an additional difference in tick interference between housekeeping and nohz_full CPUs.

What problem does that cause?

Quoted 7 lines

A CPU may still have only one runnable task in either case, so the existing tick dependencies do not express this policy.

This series keeps the boot-time nohz_full CPU set, context tracking, RCU offloading and housekeeping configuration unchanged. It only controls whether those CPUs may stop their non-idle tick. The policy can be changed without taking CPUs offline.

So the policy is to make the tick keeps running when there is only one running task. Right? I am just not clear about the advantage of doing that. Is there a use case that will require this particular feature? I doubt this will get accepted unless there is a compelling reason to show why we will want to have this feature.

Cheers,
Longman

author
#11 lore raw
Quoted 18 lines · Waiman Long
Quoted 17 lines · Peng Wang
Quoted 6 lines · Waiman Long
Quoted 5 lines · Peng Wang

Workloads and their need for periodic ticks can change after boot. This series lets userspace choose at runtime which CPUs in the boot-time nohz_full set retain the non-idle scheduler tick and which are permitted to stop it, while preserving the existing isolation configuration.

Quoted 8 lines
Quoted 4 lines

The first patch adds a per-CPU tick dependency; the second exposes a sysfs CPU list. Permission defaults to all nohz_full CPUs. Revocation uses the existing asynchronous kick. Idle tick suppression is unchanged, and policy persists across hotplug.

So you are basically setting up more nohz_full CPUs than the system actually needs to use at the moment and so want to disable nohz/tick part of nohz_full CPUs that don't need it. Can you elaborate more about the problems solve by introducing this new behavior?

Hi Waiman,

Quoted 8 lines
Quoted 7 lines

Yes. We have CPU-pinned workloads that benefit from reduced periodic tick interference. The desired tick policy changes with CPU allocation.

With spare capacity, we allow selected nohz_full CPUs to stop their tick. When capacity is fully allocated, we prefer to retain the non-idle periodic tick across CPUs, avoiding an additional difference in tick interference between housekeeping and nohz_full CPUs.

What problem does that cause?

Hi Waiman,

The concern is placement-dependent performance variation between vCPUs sold under the same specification. Customers do not see which host CPU backs their vCPU, so such differences would appear as unexplained variation within the same specification.

At full allocation, we want a consistent non-idle tick policy across the allocated CPUs to avoid this additional source of variation.

Quoted 8 lines
Quoted 7 lines

A CPU may still have only one runnable task in either case, so the existing tick dependencies do not express this policy.

This series keeps the boot-time nohz_full CPU set, context tracking, RCU offloading and housekeeping configuration unchanged. It only controls whether those CPUs may stop their non-idle tick. The policy can be changed without taking CPUs offline.

Quoted 5 lines

So the policy is to make the tick keeps running when there is only one running task. Right? I am just not clear about the advantage of doing that. Is there a use case that will require this particular feature? I doubt this will get accepted unless there is a compelling reason to show why we will want to have this feature.

Yes. We run cloud hosts with vCPU threads pinned one-to-one to host CPUs. Some CPUs are kept outside nohz_full for housekeeping, but they also provide vCPU capacity at full allocation. Dedicating them exclusively to housekeeping would reduce the capacity available to guests.

At full allocation, vCPUs also run on housekeeping CPUs, which retain their non-idle tick. Allowing only the nohz_full CPUs to stop the tick would introduce a placement-dependent difference in tick behavior between vCPUs of the same specification.

We therefore want to restore the non-idle tick on those nohz_full CPUs, accepting the extra tick overhead to remove this controllable difference. Host-side background activity is limited in this deployment, and we can tolerate the remaining housekeeping noise.

With spare capacity, we want to allow selected nohz_full CPUs to stop the tick to reduce host scheduler-tick interference with guest execution. This is an opportunistic optimization when capacity permits.

Host allocation changes as customers acquire or release vCPUs, while other customers' VMs continue running.

A nohz_full CPU hosting a remaining vCPU may still have that vCPU thread as its only runnable task, whether the host is fully or partially allocated. The existing tick dependencies therefore do not express the desired change in policy.

The proposed control allows per-CPU runtime changes without taking CPUs offline or changing the boot-time nohz_full set or other isolation settings.

Thanks,
Peng

Quoted 4 lines

Cheers,
Longman