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 <[email protected]>
Signed-off-by: He Rongguang <[email protected]>
Signed-off-by: Peng Wang <[email protected]>
---
 .../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 <[email protected]>
+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 <[email protected]>
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


Reply via email to