rtc: handle alarms with a minute resolution
authorAlexandre Belloni <alexandre.belloni@bootlin.com>
Sun, 7 Nov 2021 22:54:46 +0000 (23:54 +0100)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Tue, 9 Nov 2021 23:45:56 +0000 (00:45 +0100)
Handle alarms with a minute resolution in the core. Until now drivers have
been open coding the seconds part removal and have been doing that wrongly.
Most of them are rounding up which means the allow the system to miss
deadlines. So, round down and let __rtc_set_alarm return immediately if the
time has already passed.

Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Link: https://lore.kernel.org/r/20211107225458.111068-1-alexandre.belloni@bootlin.com
drivers/rtc/interface.c

index d005623..d8e8357 100644 (file)
@@ -423,6 +423,7 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
        if (err)
                return err;
        now = rtc_tm_to_time64(&tm);
+
        if (scheduled <= now)
                return -ETIME;
        /*
@@ -447,6 +448,7 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
 
 int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
 {
+       ktime_t alarm_time;
        int err;
 
        if (!rtc->ops)
@@ -468,7 +470,15 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
        if (rtc->aie_timer.enabled)
                rtc_timer_remove(rtc, &rtc->aie_timer);
 
-       rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time);
+       alarm_time = rtc_tm_to_ktime(alarm->time);
+       /*
+        * Round down so we never miss a deadline, checking for past deadline is
+        * done in __rtc_set_alarm
+        */
+       if (test_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features))
+               alarm_time = ktime_sub_ns(alarm_time, (u64)alarm->time.tm_sec * NSEC_PER_SEC);
+
+       rtc->aie_timer.node.expires = alarm_time;
        rtc->aie_timer.period = 0;
        if (alarm->enabled)
                err = rtc_timer_enqueue(rtc, &rtc->aie_timer);