[PATCH 2/3] posix-timers: Report a disarmed SIGEV_NONE timer as disarmed
From: Shashank Mohan Jain
Date: Sun Oct 11 2026 - 01:22:18 EST
A SIGEV_NONE timer on a hrtimer or alarmtimer based clock is never
queued. The expiry time stored in the timer is all that
common_timer_get() has to compute the remaining time, and
common_timer_set() leaves it alone when the timer is disarmed with a
zero it_value. timer_gettime() then reports the remaining time of the
previous setting until that time has passed:
struct sigevent sev = { .sigev_notify = SIGEV_NONE };
struct itimerspec arm = { .it_value.tv_sec = 100 };
struct itimerspec disarm = { }, cur;
timer_create(CLOCK_MONOTONIC, &sev, &t);
timer_settime(t, 0, &arm, NULL);
timer_settime(t, 0, &disarm, NULL);
timer_gettime(t, &cur);
leaves 99.99... seconds in cur.it_value. POSIX wants a zero it_value
for a disarmed timer. Timers which deliver a signal get that from the
POSIX_TIMER_DISARMED check in common_timer_get(), and the CPU time
clocks set the expiry time to 0 in posix_cpu_timer_set(), so they are
not affected. CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_REALTIME and the
two alarm clocks are.
When a SIGEV_NONE timer is disarmed, move its expiry time into the
past. The timer_arm() callbacks only store the expiry time for such a
timer, so call them with an absolute expiry time of 0, which is not
after the current time of any of these clocks. common_timer_get() then
sees a remaining time which is not positive and reports a zero
it_value.
This is old behaviour: the sequence above gives the same result on a
v6.8 based distribution kernel. The commit which introduced it was
not searched for, hence no Fixes tag.
Tested in qemu with the sequence above on the five clocks, and with
the posix_timers selftest including the test added in the next patch.
Assisted-by: LLM
Signed-off-by: Shashank Mohan Jain <jain.sm@xxxxxxxxx>
---
Prepared with Claude Code (Anthropic), model Claude Opus 5.5
(claude-opus-5-5).
kernel/time/posix-timers.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 436ba794cc..f6dd8d34a8 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -904,15 +904,23 @@ int common_timer_set(struct k_itimer *timr, int flags,
timr->it_status = POSIX_TIMER_DISARMED;
posix_timer_set_common(timr, new_setting);
+ sigev_none = timr->it_sigev_notify == SIGEV_NONE;
/* Keep timer disarmed when it_value is zero */
- if (!new_setting->it_value.tv_sec && !new_setting->it_value.tv_nsec)
+ if (!new_setting->it_value.tv_sec && !new_setting->it_value.tv_nsec) {
+ /*
+ * SIGEV_NONE timers are never queued. The expiry time is
+ * the only information timer_gettime() has to tell whether
+ * such a timer is armed, so move it into the past.
+ */
+ if (sigev_none)
+ kc->timer_arm(timr, 0, true, true);
return 0;
+ }
expires = timespec64_to_ktime(new_setting->it_value);
if (flags & TIMER_ABSTIME)
expires = timens_ktime_to_host(timr->it_clock, expires);
- sigev_none = timr->it_sigev_notify == SIGEV_NONE;
if (kc->timer_arm(timr, expires, flags & TIMER_ABSTIME, sigev_none)) {
if (!sigev_none)
--
2.43.0