Re: [PATCH] rtc: cros-ec: Limit RTC alarm range if needed

From: Guenter Roeck
Date: Tue Nov 08 2022 - 11:59:28 EST


Hi,

On Mon, Nov 07, 2022 at 11:52:50PM +0100, Alexandre Belloni wrote:
[ ... ]
>
> I'll take the patch as-is so you can backport it and have a solution.
> I'll also work on the alarm range and I'll let you get the series once
> this is ready so you can test.
>

Excellent, thanks a lot. I also started looking into a poor-man's solution
of range support. I attached what I currently have below for your
reference. It isn't much, but it let me test follow-up changes in the
cros-ec rtc driver. Unfortunately I was not able to find a means to
implement something like "go back to sleep fast" in the alarm timer code.

In this context: Is there a standardized set of error codes for RTC
drivers ? I see -EINVAL, -ETIME, -EDOM, -ERANGE, but those are not
consistently used. I assumed -ETIME for "time expired" and -ERANGE
for "time too far in the future" below, but that was just a wild guess.

Thanks,
Guenter

---
commit 7918f162f947424ec0ad7a318c45febeaea51d2e
Author: Guenter Roeck <linux@xxxxxxxxxxxx>
AuthorDate: Wed Nov 2 19:35:09 2022 -0700
Commit: Guenter Roeck <linux@xxxxxxxxxxxx>
CommitDate: Fri Nov 4 09:54:06 2022 -0700

rtc: Add support for limited alarm timer offsets

Some alarm timers are based on time offsets, not on absolute times.
In some situations, the amount of time that can be scheduled in the
future is limited. This may result in a refusal to suspend the system,
causing substantial battery drain.

Some RTC alarm drivers remedy the situation by setting the alarm time
to the maximum supported time if a request for an out-of-range timeout
is made. This is not really desirable since it may result in unexpected
early wakeups.

To reduce the impact of this problem, let RTC drivers report the maximum
supported alarm timer offset. The code setting alarm timers can then
decide if it wants to reject setting alarm timers to a larger value, if it
wants to implement recurring alarms until the actually requested alarm
time is met, or if it wants to accept the limited alarm time.

Signed-off-by: Guenter Roeck <linux@xxxxxxxxxxxx>

diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 9edd662c69ac..05ec9afbb6ba 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -426,6 +426,10 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)

if (scheduled <= now)
return -ETIME;
+
+ if (rtc->range_max_offset && scheduled - now > rtc->range_max_offset)
+ return -ERANGE;
+
/*
* XXX - We just checked to make sure the alarm time is not
* in the past, but there is still a race window where if
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index 1fd9c6a21ebe..b6d000ab1e5e 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -146,6 +146,7 @@ struct rtc_device {

time64_t range_min;
timeu64_t range_max;
+ timeu64_t range_max_offset;
time64_t start_secs;
time64_t offset_secs;
bool set_start_time;
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 5897828b9d7e..af8e0a9e0d63 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -291,6 +291,19 @@ static int alarmtimer_suspend(struct device *dev)
rtc_timer_cancel(rtc, &rtctimer);
rtc_read_time(rtc, &tm);
now = rtc_tm_to_ktime(tm);
+
+ /*
+ * If the RTC alarm timer only supports a limited time offset, set
+ * the alarm time to the maximum supported value.
+ * The system will wake up earlier than necessary and is expected
+ * to go back to sleep if it has nothing to do.
+ * It would be desirable to handle such early wakeups without fully
+ * waking up the system, but it is unknown if this is even possible.
+ */
+ if (rtc->range_max_offset &&
+ rtc->range_max_offset * NSEC_PER_SEC > ktime_to_ns(min))
+ min = ns_to_ktime(rtc->range_max_offset * NSEC_PER_SEC);
+
now = ktime_add(now, min);

/* Set alarm, if in the past reject suspend briefly to handle */