rtc: interface: Fix long-standing race when setting alarm

As described in the old comment dating back to
commit 6610e0893b8b ("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: stable@vger.kernel.org
Signed-off-by: Esben Haabendal <esben@geanix.com>
Link: https://lore.kernel.org/r/20250516-rtc-uie-irq-fixes-v2-1-3de8e530a39e@geanix.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by Esben Haabendal and committed by Alexandre Belloni 795cda83 87064da2

+23
+23
drivers/rtc/interface.c
··· 443 else 444 err = rtc->ops->set_alarm(rtc->dev.parent, alarm); 445 446 trace_rtc_set_alarm(rtc_tm_to_time64(&alarm->time), err); 447 return err; 448 }
··· 443 else 444 err = rtc->ops->set_alarm(rtc->dev.parent, alarm); 445 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 + 469 trace_rtc_set_alarm(rtc_tm_to_time64(&alarm->time), err); 470 return err; 471 }