Skip to content

Commit 4790e3a

Browse files
esbengregkh
authored andcommitted
rtc: interface: Fix long-standing race when setting alarm
commit 795cda8 upstream. As described in the old comment dating back to commit 6610e08 ("RTC: Rework RTC code to use timerqueue for events") from 2010, we have been living with a race window when setting alarm with an expiry in the near future (i.e. next second). With 1 second resolution, it can happen that the second ticks after the check for the timer having expired, but before the alarm is actually set. When this happen, no alarm IRQ is generated, at least not with some RTC chips (isl12022 is an example of this). With UIE RTC timer being implemented on top of alarm irq, being re-armed every second, UIE will occasionally fail to work, as an alarm irq lost due to this race will stop the re-arming loop. For now, I have limited the additional expiry check to only be done for alarms set to next seconds. I expect it should be good enough, although I don't know if we can now for sure that systems with loads could end up causing the same problems for alarms set 2 seconds or even longer in the future. I haven't been able to reproduce the problem with this check in place. Cc: [email protected] Signed-off-by: Esben Haabendal <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexandre Belloni <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 04eaae7 commit 4790e3a

File tree

1 file changed

+23
-0
lines changed

1 file changed

+23
-0
lines changed

drivers/rtc/interface.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,29 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
443443
else
444444
err = rtc->ops->set_alarm(rtc->dev.parent, alarm);
445445

446+
/*
447+
* Check for potential race described above. If the waiting for next
448+
* second, and the second just ticked since the check above, either
449+
*
450+
* 1) It ticked after the alarm was set, and an alarm irq should be
451+
* generated.
452+
*
453+
* 2) It ticked before the alarm was set, and alarm irq most likely will
454+
* not be generated.
455+
*
456+
* While we cannot easily check for which of these two scenarios we
457+
* are in, we can return -ETIME to signal that the timer has already
458+
* expired, which is true in both cases.
459+
*/
460+
if ((scheduled - now) <= 1) {
461+
err = __rtc_read_time(rtc, &tm);
462+
if (err)
463+
return err;
464+
now = rtc_tm_to_time64(&tm);
465+
if (scheduled <= now)
466+
return -ETIME;
467+
}
468+
446469
trace_rtc_set_alarm(rtc_tm_to_time64(&alarm->time), err);
447470
return err;
448471
}

0 commit comments

Comments
 (0)