All of lore.kernel.org
 help / color / mirror / Atom feed
* [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

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.