[PATCH v2] docs: timers: hrtimers: clarify expiry modes and ktimersd on PREEMPT_RT

From: Liang Hao

Date: Wed Aug 12 2026 - 10:58:57 EST


Documentation/timers/hrtimers.rst did not cover the PREEMPT_RT
expiry-mode semantics. On a PREEMPT_RT kernel a timer that is not
explicitly marked HRTIMER_MODE_HARD is forced into softirq expiry and
its callback runs on the per-CPU ktimers/%u thread at the lowest
SCHED_FIFO priority (sched_set_fifo_low), regardless of the priority of
the task that armed it -- a SCHED_FIFO task running at priority 99 that
starts an unmarked timer still expires on ktimers/%u (lowest SCHED_FIFO
priority), not at priority 99.

Add an "Expiry modes and PREEMPT_RT" section that, rather than
duplicating the default-context description in
Documentation/core-api/real-time/differences.rst (Timers),
cross-references it and focuses on what that document does not spell
out:

- the callback does not inherit the arming task's priority, and
priority inheritance on PREEMPT_RT is used for the cancel handshake,
not the arming path (the "Spin until ready" section of the same
document);
- the sleeper exception: hrtimer_setup_sleeper() marks RT/DL-armed
timers HRTIMER_MODE_HARD, so their wakeups do not go through
ktimers/%u.

Documentation only; no code or behaviour change.

Signed-off-by: Liang Hao <haohlliang@xxxxxxxxx>
---
v1 -> v2:
- shorten the RT overview; link to real-time/differences
- state the arming-path priority consequence (priority not inherited;
PI is for the cancel handshake)
- drop the hrtimer_start trace debugging section
- use the ktimers/%u thread name, with ktimersd as its doc alias

Documentation/timers/hrtimers.rst | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)

diff --git a/Documentation/timers/hrtimers.rst b/Documentation/timers/hrtimers.rst
index f88ff8bae89c..fae191550c5d 100644
--- a/Documentation/timers/hrtimers.rst
+++ b/Documentation/timers/hrtimers.rst
@@ -171,3 +171,31 @@ hrtimers-based high-resolution clock implementation, so the hrtimers
code got a healthy amount of testing and use in practice.

Thomas Gleixner, Ingo Molnar
+
+
+Expiry modes and PREEMPT_RT
+---------------------------
+
+The default expiry context on PREEMPT_RT and the role of the ktimersd
+thread are documented in :doc:`/core-api/real-time/differences`
+(Timers). Those details are not repeated here.
+
+The per-CPU ``ktimers/%u`` thread (referred to as ktimersd in that
+document) runs at the lowest ``SCHED_FIFO`` priority via
+``sched_set_fifo_low()``. That priority is fixed: the callback does
+not inherit the priority of the task that armed the timer. A
+``SCHED_FIFO`` task running at priority 99 that starts an unmarked
+timer still expires on ``ktimers/%u`` (lowest ``SCHED_FIFO`` priority),
+not at priority 99.
+Priority inheritance on PREEMPT_RT is used for the cancel handshake,
+not for the arming path; see the "Spin until ready" section of the same
+document.
+
+``hrtimer_setup_sleeper()`` (used by ``clock_nanosleep()`` and similar)
+is an exception: when armed by an RT or DEADLINE task it is marked
+``HRTIMER_MODE_HARD``, so the wakeup runs in hardirq context and does
+not go through ``ktimers/%u``.
+
+If callback work must run at the owning task's RT priority, either
+mark the timer ``HRTIMER_MODE_HARD`` (and keep the callback
+hardirq-safe) or wake a dedicated kthread from the callback.
--
2.50.1 (Apple Git-155)