timerfd_gettime() on a CLOCK_REALTIME_ALARM or CLOCK_BOOTTIME_ALARM
timerfd keeps reporting the remaining time of the previous setting
after the timer has been disarmed:

        struct itimerspec arm = { .it_value.tv_sec = 100 };
        struct itimerspec disarm = { }, cur;

        fd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0);
        timerfd_settime(fd, 0, &arm, NULL);
        timerfd_settime(fd, 0, &disarm, NULL);
        timerfd_gettime(fd, &cur);

leaves 99.99... seconds in cur.it_value, and the value counts down
until the old expiry time has passed. The same value is returned as
old_value by the next timerfd_settime() and shown as it_value in
/proc/<pid>/fdinfo/<fd>. A zero it_value is how timerfd_gettime()
tells user space that a timer is disarmed, and the timerfds on the
hrtimer based clocks report zero here.

timerfd_setup() initializes the timer again on every
timerfd_settime(). For the hrtimer based clocks hrtimer_setup() clears
the expiry time, so timerfd_get_remaining() finds an expiry time in
the past for a disarmed timer and returns 0. alarm_init() does not
touch alarm->node.expires, timerqueue_init() only clears the rbtree
node, and alarm_expires_remaining() computes the remaining time from
the expiry time of the previous setting.

Reset the expiry time in alarm_init(), like hrtimer_setup() does for
a hrtimer. The other callers of alarm_init() start the alarm, which
sets the expiry time, before anything reads it.

Tested in qemu with a program doing the above for both alarm clocks
(the VM has an RTC, which timerfd does not need for these clocks):
it_value, old_value and fdinfo are zero after the disarm with this
change.

Fixes: 11ffa9d6065f ("timerfd: Add alarm timers")
Assisted-by: LLM
Signed-off-by: Shashank Mohan Jain <[email protected]>
---
Prepared with Claude Code (Anthropic), model Claude Opus 5.5
(claude-opus-5-5).

 kernel/time/alarmtimer.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index ea5be5870e..d6dc6e8272 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -316,6 +316,7 @@ __alarm_init(struct alarm *alarm, enum alarmtimer_type type,
             void (*function)(struct alarm *, ktime_t))
 {
        timerqueue_init(&alarm->node);
+       alarm->node.expires = 0;
        alarm->function = function;
        alarm->type = type;
        alarm->state = ALARMTIMER_STATE_INACTIVE;
-- 
2.43.0


Reply via email to