* [PATCH v3 0/5] Stop the idle tick on polling idle on nohz_full CPUs
@ 2026-11-12 16:38 Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 1/5] tick/nohz: Re-evaluate the tick on return to user space Marco Crivellari
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Marco Crivellari @ 2026-11-12 16:38 UTC (permalink / raw)
To: linux-kernel
Cc: Frederic Weisbecker, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Valentin Schneider, K Prateek Nayak,
Paul E . McKenney, Anna-Maria Behnsen, Thomas Gleixner,
Jonathan Corbet, Randy Dunlap, Shuah Khan, Marco Crivellari,
Michal Hocko, Aaron Tomlin, Marcelo Tosatti
Hi,
On nohz_full CPUs booted with idle=poll, the tick interrupts the task once
after every wakeup from idle. do_idle() restarts the tick before polling,
and on wakeup tick_nohz_idle_exit() only re-evaluates the tick if it is
stopped, so it keeps running and fires in userspace before
tick_nohz_irq_exit() stops it. Without idle=poll this doesn't happen:
since commit a5183862e76f ("tick/nohz: Conditionally restart tick on idle
exit") a nohz_full CPU leaves idle with the tick still stopped.
Marcelo Tosatti proposed stopping the tick on polling idle entry:
https://lore.kernel.org/all/aQiWfnnSzxsnwa2o@tpad/
Following Frederic's review, this series does it only on nohz_full CPUs,
and handles on the target CPU the cases where stopping the tick in polling
idle would leave a timer unprogrammed.
[ P1 ] tick/nohz: Re-evaluate the tick on return to user space
On nohz_full CPUs the tick is re-evaluated on IRQ exit and on idle exit.
This is not enough on return to user space. If the tick is left running in
the kernel, for example because a tick dependency was cleared
(tick_nohz_dep_clear*() don't kick the CPU) or a timer that kept it
running was cancelled, it fires at least once in userspace before
tick_nohz_irq_exit() stops it.
Stop it from context tracking right before resuming userspace. When the
tick is already stopped, the common case, this costs a few flag tests on
each return to userspace.
[ P2 ] Don't handle polling idle as dynticks idle
TS_FLAG_INIDLE tells nohz whether the tick is managed by the idle loop or
on IRQ exit. With idle=poll it is set while polling, but cpu_idle_poll()
doesn't run the idle loop again on interrupts: a nohz_full kick or the
expiry of a stopped tick isn't re-evaluated until something sets
TIF_NEED_RESCHED. The tick is therefore restarted before polling, and on
nohz_full CPUs it then keeps running after the wake-up and interrupts
userspace.
Enter dynticks idle only on the cpuidle path, from cpuidle_idle_call(),
and leave it before cpu_idle_poll(). While polling, the tick is then
managed on IRQ exit as for a running task: on nohz_full CPUs it stays
stopped if possible, and nothing is left to do on wake-up, so the task
doesn't pay for a tick stop after waking up. On other CPUs, leaving
dynticks idle restarts the tick as before.
[ P3 ] tick/nohz: Remove tick_nohz_idle_restart_tick()
Polling idle now leaves dynticks idle through tick_nohz_idle_exit()
before cpu_idle_poll(), which restarts the tick where needed.
tick_nohz_idle_restart_tick() has no users left: remove it.
[ P4 ] tick/nohz: Reset got_idle_tick when entering dynticks idle
A tick firing after the last tick_nohz_idle_got_tick() and before
tick_nohz_idle_exit() leaves got_idle_tick set, and the next
cpuidle_idle_call() then assumes that a later dynticks idle period was
ended by the tick. Now that the CPU leaves dynticks idle before polling,
this can also happen within a single idle trip when it switches between
cpuidle and polling, e.g. when it polls because the tick broadcast has
expired.
Reset got_idle_tick when entering dynticks idle, so that it only reports
the ticks of the current period.
[ P5 ] Documentation: no_hz: Add tick stop in polling idle
Document the above in the idle=poll item of "Known Issues".
With big effort on the Claude side, I made a test suite to help testing
the work:
https://github.com/DispatchCode/nohz-full-polling-idle-tick-test-suite
The following is a short summary of the results.
On x86_64 guest, HZ=1000, nohz_full=2-3 idle=poll), a task alternating
100us sleeps with 10ms of userspace work, 200 times:
- ticks in userspace: 200/200 -> 0/200
- ticks while polling: 35 -> 0
- housekeeping CPUs: unchanged (tick runs while polling)
- P2 without the kick change: 15 of 20 timers enqueued from an
interrupt on the polling CPU lost
- P2 without the stopped tick fix: the second of two queued timers up
to 1.9s late
Tick dependencies (POSIX CPU timers, two tasks on the CPU) and CPU hotplug
were also checked.
Thanks,
Marco
---
Changes in v3:
- tick_nohz_user_enter(); moved outside of CT_STATE_USER so that also
guest can benefit from this.
- __tick_nohz_user_enter() has been moved after
tick_nohz_full_stop_tick(), so that the same #ifdef is used.
- P2 now uses guard(irqsave)() instead of irq_save(flags) removing also
the "goto".
- P4, Documentation, now is P5.
- Introduced P4: reset got_idle_tick on tick_nohz_idle_enter().
Link to v2: https://lore.kernel.org/all/20261111114406.346953-1-marco.crivellari@suse.com/
Changes in v2:
- P1: Only for re-evaluate the tick on return to user space
- P2: Changed as suggested by Frederic: TS_FLAG_INIDLE is only set when
the CPU goes idle and not in polling idle; tick_nohz_idle_enter() is
called on every idle loop and runs with interrupt disabled. The tick
is not restarted before polling idle, so
tick_nohz_idle_restart_tick() has been removed.
- P3: Remove tick_nohz_idle_restart_tick(), now unused.
Link to v1: https://lore.kernel.org/all/20261106160621.347228-1-marco.crivellari@suse.com/
Marco Crivellari (5):
tick/nohz: Re-evaluate the tick on return to user space
sched/idle: Don't handle polling idle as dynticks idle
tick/nohz: Remove tick_nohz_idle_restart_tick()
tick/nohz: Reset got_idle_tick when entering dynticks idle
Documentation: no_hz: Add tick stop in polling idle
Documentation/timers/no_hz.rst | 2 ++
include/linux/tick.h | 10 ++++--
kernel/context_tracking.c | 12 +++++++
kernel/sched/idle.c | 13 +++++--
kernel/time/tick-sched.c | 65 +++++++++++++++++++---------------
5 files changed, 70 insertions(+), 32 deletions(-)
--
2.56.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 1/5] tick/nohz: Re-evaluate the tick on return to user space
2026-11-12 16:38 [PATCH v3 0/5] Stop the idle tick on polling idle on nohz_full CPUs Marco Crivellari
@ 2026-11-12 16:38 ` Marco Crivellari
2026-11-14 20:43 ` Frederic Weisbecker
2026-11-12 16:38 ` [PATCH v3 2/5] sched/idle: Don't handle polling idle as dynticks idle Marco Crivellari
` (3 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Marco Crivellari @ 2026-11-12 16:38 UTC (permalink / raw)
To: linux-kernel
Cc: Frederic Weisbecker, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Valentin Schneider, K Prateek Nayak,
Paul E . McKenney, Anna-Maria Behnsen, Thomas Gleixner,
Jonathan Corbet, Randy Dunlap, Shuah Khan, Marco Crivellari,
Michal Hocko, Aaron Tomlin, Marcelo Tosatti
On nohz_full CPUs the tick is re-evaluated on IRQ exit and on idle exit.
This is not enough on return to user space. If the tick is left running in
the kernel, for example because a tick dependency was cleared
(tick_nohz_dep_clear*() don't kick the CPU) or a timer that kept it
running was cancelled, it fires at least once in userspace before
tick_nohz_irq_exit() stops it.
Stop it from context tracking right before resuming userspace. When the
tick is already stopped, the common case, this costs a few flag tests on
each return to userspace.
Suggested-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
---
include/linux/tick.h | 8 ++++++++
kernel/context_tracking.c | 12 ++++++++++++
kernel/time/tick-sched.c | 22 ++++++++++++++++++++++
3 files changed, 42 insertions(+)
diff --git a/include/linux/tick.h b/include/linux/tick.h
index b121c5d53e30..8bfbb053f4fd 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -269,6 +269,7 @@ static inline void tick_dep_clear_signal(struct signal_struct *signal,
extern void tick_nohz_full_kick_cpu(int cpu);
extern void __tick_nohz_task_switch(void);
extern void __init tick_nohz_full_setup(cpumask_var_t cpumask);
+extern void __tick_nohz_user_enter(void);
#else
static inline bool tick_nohz_full_enabled(void) { return false; }
static inline bool tick_nohz_full_cpu(int cpu) { return false; }
@@ -294,6 +295,7 @@ static inline void tick_dep_clear_signal(struct signal_struct *signal,
static inline void tick_nohz_full_kick_cpu(int cpu) { }
static inline void __tick_nohz_task_switch(void) { }
static inline void tick_nohz_full_setup(cpumask_var_t cpumask) { }
+static inline void __tick_nohz_user_enter(void) { }
#endif
static inline void tick_nohz_task_switch(void)
@@ -302,6 +304,12 @@ static inline void tick_nohz_task_switch(void)
__tick_nohz_task_switch();
}
+static inline void tick_nohz_user_enter(void)
+{
+ if (tick_nohz_full_enabled())
+ __tick_nohz_user_enter();
+}
+
static inline void tick_nohz_user_enter_prepare(void)
{
if (tick_nohz_full_cpu(smp_processor_id()))
diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c
index a743e7ffa6c0..8e898b34977d 100644
--- a/kernel/context_tracking.c
+++ b/kernel/context_tracking.c
@@ -24,6 +24,7 @@
#include <linux/export.h>
#include <linux/kprobes.h>
#include <trace/events/rcu.h>
+#include <linux/tick.h>
DEFINE_PER_CPU(struct context_tracking, context_tracking) = {
@@ -470,6 +471,17 @@ void noinstr __ct_user_enter(enum ctx_state state)
vtime_user_enter(current);
instrumentation_end();
}
+
+ /*
+ * On nohz_full CPUs the tick may have been left running in the
+ * kernel, e.g. kept by a tick dependency that is now gone.
+ * Stop it now rather than let it interrupt userspace once
+ * more before the next IRQ exit notices.
+ */
+ instrumentation_begin();
+ tick_nohz_user_enter();
+ instrumentation_end();
+
/*
* Other than generic entry implementation, we may be past the last
* rescheduling opportunity in the entry code. Trigger a self IPI
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index c6d86e4da8a9..edaac8e7c669 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -1026,6 +1026,28 @@ static void tick_nohz_full_stop_tick(struct tick_sched *ts, int cpu)
else
tick_nohz_retain_tick(ts);
}
+
+void __tick_nohz_user_enter(void)
+{
+ struct tick_sched *ts;
+ int cpu;
+
+ lockdep_assert_irqs_disabled();
+
+ cpu = smp_processor_id();
+ if (!tick_nohz_full_cpu(cpu))
+ return;
+
+ ts = this_cpu_ptr(&tick_cpu_sched);
+ if (!tick_sched_flag_test(ts, TS_FLAG_NOHZ))
+ return;
+
+ if (tick_sched_flag_test(ts, TS_FLAG_STOPPED))
+ return;
+
+ if (can_stop_full_tick(cpu, ts))
+ tick_nohz_full_stop_tick(ts, cpu);
+}
#endif /* CONFIG_NO_HZ_FULL */
static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 2/5] sched/idle: Don't handle polling idle as dynticks idle
2026-11-12 16:38 [PATCH v3 0/5] Stop the idle tick on polling idle on nohz_full CPUs Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 1/5] tick/nohz: Re-evaluate the tick on return to user space Marco Crivellari
@ 2026-11-12 16:38 ` Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 3/5] tick/nohz: Remove tick_nohz_idle_restart_tick() Marco Crivellari
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Marco Crivellari @ 2026-11-12 16:38 UTC (permalink / raw)
To: linux-kernel
Cc: Frederic Weisbecker, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Valentin Schneider, K Prateek Nayak,
Paul E . McKenney, Anna-Maria Behnsen, Thomas Gleixner,
Jonathan Corbet, Randy Dunlap, Shuah Khan, Marco Crivellari,
Michal Hocko, Aaron Tomlin, Marcelo Tosatti
TS_FLAG_INIDLE tells nohz whether the tick is managed by the idle loop or
on IRQ exit. With idle=poll it is set while polling, but cpu_idle_poll()
doesn't run the idle loop again on interrupts: a nohz_full kick or the
expiry of a stopped tick isn't re-evaluated until something sets
TIF_NEED_RESCHED. The tick is therefore restarted before polling, and on
nohz_full CPUs it then keeps running after the wake-up and interrupts
userspace.
Enter dynticks idle only on the cpuidle path, from cpuidle_idle_call(),
and leave it before cpu_idle_poll(). While polling, the tick is then
managed on IRQ exit as for a running task: on nohz_full CPUs it stays
stopped if possible, and nothing is left to do on wake-up, so the task
doesn't pay for a tick stop after waking up. On other CPUs, leaving
dynticks idle restarts the tick as before.
tick_nohz_idle_enter() may now be called on each idle loop iteration and
always runs with interrupts disabled; tick_nohz_idle_exit() may be called
outside dynticks idle and with interrupts disabled.
Link: https://lore.kernel.org/all/aQiWfnnSzxsnwa2o@tpad/
Suggested-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
---
kernel/sched/idle.c | 13 +++++++++++--
kernel/time/tick-sched.c | 24 +++++++++++++-----------
2 files changed, 24 insertions(+), 13 deletions(-)
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index e0c10a5dbda2..e0e3c06da528 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -193,6 +193,8 @@ static void cpuidle_idle_call(bool stop_tick)
return;
}
+ tick_nohz_idle_enter();
+
if (cpuidle_not_available(drv, dev)) {
idle_call_stop_or_retain_tick(stop_tick);
@@ -303,7 +305,6 @@ static void do_idle(void)
*/
__current_set_polling();
- tick_nohz_idle_enter();
while (!need_resched()) {
@@ -349,7 +350,15 @@ static void do_idle(void)
* idle as we know that the IPI is going to arrive right away.
*/
if (cpu_idle_force_poll || tick_check_broadcast_expired()) {
- tick_nohz_idle_restart_tick();
+ /*
+ * Polling idle is not dynticks idle: leave it before
+ * polling so that the tick is managed on IRQ exit as for
+ * a running task. On nohz_full CPUs it stays stopped and
+ * nothing is left to do on wake-up that would delay the
+ * task. On other CPUs, leaving dynticks idle restarts it
+ * as before.
+ */
+ tick_nohz_idle_exit();
cpu_idle_poll();
} else {
cpuidle_idle_call(got_tick);
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index edaac8e7c669..cb3117453eaa 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -1225,23 +1225,22 @@ static void tick_nohz_clock_wakeup(struct tick_sched *ts)
/**
* tick_nohz_idle_enter - prepare for entering idle on the current CPU
*
- * Called when we start the idle loop.
+ * Called with IRQs disabled on each idle loop iteration that enters
+ * cpuidle. Only the first call enters dynticks idle.
*/
void tick_nohz_idle_enter(void)
{
- struct tick_sched *ts;
+ struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
- lockdep_assert_irqs_enabled();
+ lockdep_assert_irqs_disabled();
- local_irq_disable();
+ if (tick_sched_flag_test(ts, TS_FLAG_INIDLE))
+ return;
- ts = this_cpu_ptr(&tick_cpu_sched);
WARN_ON_ONCE(ts->timer_expires_base);
tick_sched_flag_set(ts, TS_FLAG_INIDLE);
ts->idle_entrytime = ktime_get();
tick_nohz_clock_sleep(ts);
-
- local_irq_enable();
}
/**
@@ -1413,15 +1412,20 @@ static void tick_nohz_idle_update_tick(struct tick_sched *ts, ktime_t now)
* as long as possible.
* 2.2) If the tick has dependencies, restart the tick.
*
+ * Also called before polling idle, which is not dynticks idle: it may
+ * therefore be called outside dynticks idle, with or without IRQs
+ * disabled.
*/
void tick_nohz_idle_exit(void)
{
struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
ktime_t now;
- local_irq_disable();
+ guard(irqsave)();
+
+ if (!tick_sched_flag_test(ts, TS_FLAG_INIDLE))
+ return;
- WARN_ON_ONCE(!tick_sched_flag_test(ts, TS_FLAG_INIDLE));
WARN_ON_ONCE(ts->timer_expires_base);
tick_sched_flag_clear(ts, TS_FLAG_INIDLE);
@@ -1432,8 +1436,6 @@ void tick_nohz_idle_exit(void)
tick_nohz_dyntick_stop(ts, now);
tick_nohz_idle_update_tick(ts, now);
}
-
- local_irq_enable();
}
/*
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 3/5] tick/nohz: Remove tick_nohz_idle_restart_tick()
2026-11-12 16:38 [PATCH v3 0/5] Stop the idle tick on polling idle on nohz_full CPUs Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 1/5] tick/nohz: Re-evaluate the tick on return to user space Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 2/5] sched/idle: Don't handle polling idle as dynticks idle Marco Crivellari
@ 2026-11-12 16:38 ` Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 4/5] tick/nohz: Reset got_idle_tick when entering dynticks idle Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 5/5] Documentation: no_hz: Add tick stop in polling idle Marco Crivellari
4 siblings, 0 replies; 7+ messages in thread
From: Marco Crivellari @ 2026-11-12 16:38 UTC (permalink / raw)
To: linux-kernel
Cc: Frederic Weisbecker, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Valentin Schneider, K Prateek Nayak,
Paul E . McKenney, Anna-Maria Behnsen, Thomas Gleixner,
Jonathan Corbet, Randy Dunlap, Shuah Khan, Marco Crivellari,
Michal Hocko, Aaron Tomlin, Marcelo Tosatti
Polling idle now leaves dynticks idle through tick_nohz_idle_exit()
before cpu_idle_poll(), which restarts the tick where needed.
tick_nohz_idle_restart_tick() has no users left: remove it.
Suggested-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
---
include/linux/tick.h | 2 --
kernel/time/tick-sched.c | 17 -----------------
2 files changed, 19 deletions(-)
diff --git a/include/linux/tick.h b/include/linux/tick.h
index 8bfbb053f4fd..686f7e58738b 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -130,7 +130,6 @@ extern bool tick_nohz_tick_stopped(void);
extern bool tick_nohz_tick_stopped_cpu(int cpu);
extern void tick_nohz_idle_stop_tick(void);
extern void tick_nohz_idle_retain_tick(void);
-extern void tick_nohz_idle_restart_tick(void);
extern void tick_nohz_idle_enter(void);
extern void tick_nohz_idle_exit(void);
extern void tick_nohz_irq_exit(void);
@@ -145,7 +144,6 @@ static inline int tick_nohz_tick_stopped(void) { return 0; }
static inline int tick_nohz_tick_stopped_cpu(int cpu) { return 0; }
static inline void tick_nohz_idle_stop_tick(void) { }
static inline void tick_nohz_idle_retain_tick(void) { }
-static inline void tick_nohz_idle_restart_tick(void) { }
static inline void tick_nohz_idle_enter(void) { }
static inline void tick_nohz_idle_exit(void) { }
static inline bool tick_nohz_idle_got_tick(void) { return false; }
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index cb3117453eaa..b4149f40b5f1 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -1372,23 +1372,6 @@ static void tick_nohz_dyntick_stop(struct tick_sched *ts, ktime_t now)
kcpustat_dyntick_stop(now, ts->last_tick - TICK_NSEC);
}
-void tick_nohz_idle_restart_tick(void)
-{
- struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
-
- if (tick_sched_flag_test(ts, TS_FLAG_STOPPED)) {
- /*
- * Update entrytime here in case the tick restart is due to temporary
- * polling on forced broadcast. The tick may be stopped again later within
- * the same idle trip. The idle_entrytime was updated recently but make sure
- * no tiny amount of idle time is accounted twice.
- */
- ts->idle_entrytime = ktime_get();
- tick_nohz_dyntick_stop(ts, ts->idle_entrytime);
- tick_nohz_restart_sched_tick(ts, ts->idle_entrytime);
- }
-}
-
static void tick_nohz_idle_update_tick(struct tick_sched *ts, ktime_t now)
{
if (tick_nohz_full_cpu(smp_processor_id()))
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 4/5] tick/nohz: Reset got_idle_tick when entering dynticks idle
2026-11-12 16:38 [PATCH v3 0/5] Stop the idle tick on polling idle on nohz_full CPUs Marco Crivellari
` (2 preceding siblings ...)
2026-11-12 16:38 ` [PATCH v3 3/5] tick/nohz: Remove tick_nohz_idle_restart_tick() Marco Crivellari
@ 2026-11-12 16:38 ` Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 5/5] Documentation: no_hz: Add tick stop in polling idle Marco Crivellari
4 siblings, 0 replies; 7+ messages in thread
From: Marco Crivellari @ 2026-11-12 16:38 UTC (permalink / raw)
To: linux-kernel
Cc: Frederic Weisbecker, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Valentin Schneider, K Prateek Nayak,
Paul E . McKenney, Anna-Maria Behnsen, Thomas Gleixner,
Jonathan Corbet, Randy Dunlap, Shuah Khan, Marco Crivellari,
Michal Hocko, Aaron Tomlin, Marcelo Tosatti
A tick firing after the last tick_nohz_idle_got_tick() and before
tick_nohz_idle_exit() leaves got_idle_tick set, and the next
cpuidle_idle_call() then assumes that a later dynticks idle period was
ended by the tick. Now that the CPU leaves dynticks idle before polling,
this can also happen within a single idle trip when it switches between
cpuidle and polling, e.g. when it polls because the tick broadcast has
expired.
Reset got_idle_tick when entering dynticks idle, so that it only reports
the ticks of the current period.
Suggested-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
---
kernel/time/tick-sched.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index b4149f40b5f1..fb47d5dfbf4e 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -1237,6 +1237,8 @@ void tick_nohz_idle_enter(void)
if (tick_sched_flag_test(ts, TS_FLAG_INIDLE))
return;
+ /* Only report the ticks of this dynticks idle period */
+ ts->got_idle_tick = 0;
WARN_ON_ONCE(ts->timer_expires_base);
tick_sched_flag_set(ts, TS_FLAG_INIDLE);
ts->idle_entrytime = ktime_get();
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 5/5] Documentation: no_hz: Add tick stop in polling idle
2026-11-12 16:38 [PATCH v3 0/5] Stop the idle tick on polling idle on nohz_full CPUs Marco Crivellari
` (3 preceding siblings ...)
2026-11-12 16:38 ` [PATCH v3 4/5] tick/nohz: Reset got_idle_tick when entering dynticks idle Marco Crivellari
@ 2026-11-12 16:38 ` Marco Crivellari
4 siblings, 0 replies; 7+ messages in thread
From: Marco Crivellari @ 2026-11-12 16:38 UTC (permalink / raw)
To: linux-kernel
Cc: Frederic Weisbecker, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Valentin Schneider, K Prateek Nayak,
Paul E . McKenney, Anna-Maria Behnsen, Thomas Gleixner,
Jonathan Corbet, Randy Dunlap, Shuah Khan, Marco Crivellari,
Michal Hocko, Aaron Tomlin, Marcelo Tosatti
On nohz_full CPUs the scheduling-clock tick is stopped while polling.
Update the documentation accordingly.
Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
---
Documentation/timers/no_hz.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/timers/no_hz.rst b/Documentation/timers/no_hz.rst
index 4006cab64079..86cea04d2a49 100644
--- a/Documentation/timers/no_hz.rst
+++ b/Documentation/timers/no_hz.rst
@@ -246,6 +246,8 @@ Known Issues
be even worse than that of dyntick-idle. Furthermore,
this parameter effectively disables Turbo Mode on Intel
CPUs, which can significantly reduce maximum performance.
+ Note also that on nohz_full CPUs the scheduling-clock tick
+ is stopped while polling, as in the other idle states.
* Adaptive-ticks slows user/kernel transitions slightly.
This is not expected to be a problem for computationally intensive
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/5] tick/nohz: Re-evaluate the tick on return to user space
2026-11-12 16:38 ` [PATCH v3 1/5] tick/nohz: Re-evaluate the tick on return to user space Marco Crivellari
@ 2026-11-14 20:43 ` Frederic Weisbecker
0 siblings, 0 replies; 7+ messages in thread
From: Frederic Weisbecker @ 2026-11-14 20:43 UTC (permalink / raw)
To: Marco Crivellari
Cc: linux-kernel, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Valentin Schneider, K Prateek Nayak,
Paul E . McKenney, Anna-Maria Behnsen, Thomas Gleixner,
Jonathan Corbet, Randy Dunlap, Shuah Khan, Michal Hocko,
Aaron Tomlin, Marcelo Tosatti
Le Thu, Nov 12, 2026 at 05:38:09PM +0100, Marco Crivellari a écrit :
> On nohz_full CPUs the tick is re-evaluated on IRQ exit and on idle exit.
>
> This is not enough on return to user space. If the tick is left running in
> the kernel, for example because a tick dependency was cleared
> (tick_nohz_dep_clear*() don't kick the CPU) or a timer that kept it
> running was cancelled, it fires at least once in userspace before
> tick_nohz_irq_exit() stops it.
>
> Stop it from context tracking right before resuming userspace. When the
> tick is already stopped, the common case, this costs a few flag tests on
> each return to userspace.
>
> Suggested-by: Frederic Weisbecker <frederic@kernel.org>
> Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
--
Frederic Weisbecker
SUSE Labs
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-14 20:43 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-12 16:38 [PATCH v3 0/5] Stop the idle tick on polling idle on nohz_full CPUs Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 1/5] tick/nohz: Re-evaluate the tick on return to user space Marco Crivellari
2026-11-14 20:43 ` Frederic Weisbecker
2026-11-12 16:38 ` [PATCH v3 2/5] sched/idle: Don't handle polling idle as dynticks idle Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 3/5] tick/nohz: Remove tick_nohz_idle_restart_tick() Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 4/5] tick/nohz: Reset got_idle_tick when entering dynticks idle Marco Crivellari
2026-11-12 16:38 ` [PATCH v3 5/5] Documentation: no_hz: Add tick stop in polling idle Marco Crivellari
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.