selftests/rtc: continuously read RTC in a loop for 30s
authorMateusz Jończyk <mat.jonczyk@o2.pl>
Sat, 19 Feb 2022 07:27:13 +0000 (08:27 +0100)
committerShuah Khan <skhan@linuxfoundation.org>
Sat, 26 Feb 2022 00:00:51 +0000 (17:00 -0700)
Some problems with reading the RTC time may happen rarely, for example
while the RTC is updating. So read the RTC many times to catch these
problems. For example, a previous attempt for my
commit ea6fa4961aab ("rtc: mc146818-lib: fix RTC presence check")
was incorrect and would have triggered this selftest.

To avoid the risk of damaging the hardware, wait 11ms before consecutive
reads.

In rtc_time_to_timestamp I copied values manually instead of casting -
just to be on the safe side. The 11ms wait period was chosen so that it is
not a divisor of 1000ms.

Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
tools/testing/selftests/rtc/rtctest.c
tools/testing/selftests/rtc/settings

index 66af608..2b9d929 100644 (file)
@@ -20,6 +20,8 @@
 
 #define NUM_UIE 3
 #define ALARM_DELTA 3
+#define READ_LOOP_DURATION_SEC 30
+#define READ_LOOP_SLEEP_MS 11
 
 static char *rtc_file = "/dev/rtc0";
 
@@ -49,6 +51,70 @@ TEST_F(rtc, date_read) {
               rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
 }
 
+static time_t rtc_time_to_timestamp(struct rtc_time *rtc_time)
+{
+       struct tm tm_time = {
+              .tm_sec = rtc_time->tm_sec,
+              .tm_min = rtc_time->tm_min,
+              .tm_hour = rtc_time->tm_hour,
+              .tm_mday = rtc_time->tm_mday,
+              .tm_mon = rtc_time->tm_mon,
+              .tm_year = rtc_time->tm_year,
+       };
+
+       return mktime(&tm_time);
+}
+
+static void nanosleep_with_retries(long ns)
+{
+       struct timespec req = {
+               .tv_sec = 0,
+               .tv_nsec = ns,
+       };
+       struct timespec rem;
+
+       while (nanosleep(&req, &rem) != 0) {
+               req.tv_sec = rem.tv_sec;
+               req.tv_nsec = rem.tv_nsec;
+       }
+}
+
+TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
+       int rc;
+       long iter_count = 0;
+       struct rtc_time rtc_tm;
+       time_t start_rtc_read, prev_rtc_read;
+
+       TH_LOG("Continuously reading RTC time for %ds (with %dms breaks after every read).",
+              READ_LOOP_DURATION_SEC, READ_LOOP_SLEEP_MS);
+
+       rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
+       ASSERT_NE(-1, rc);
+       start_rtc_read = rtc_time_to_timestamp(&rtc_tm);
+       prev_rtc_read = start_rtc_read;
+
+       do  {
+               time_t rtc_read;
+
+               rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
+               ASSERT_NE(-1, rc);
+
+               rtc_read = rtc_time_to_timestamp(&rtc_tm);
+               /* Time should not go backwards */
+               ASSERT_LE(prev_rtc_read, rtc_read);
+               /* Time should not increase more then 1s at a time */
+               ASSERT_GE(prev_rtc_read + 1, rtc_read);
+
+               /* Sleep 11ms to avoid killing / overheating the RTC */
+               nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
+
+               prev_rtc_read = rtc_read;
+               iter_count++;
+       } while (prev_rtc_read <= start_rtc_read + READ_LOOP_DURATION_SEC);
+
+       TH_LOG("Performed %ld RTC time reads.", iter_count);
+}
+
 TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
        int i, rc, irq = 0;
        unsigned long data;