1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019 Linaro Limited. All rights reserved.
4 * Author: Rafael David Tinoco <rafael.tinoco@linaro.org>
5 */
6
7 #include <errno.h>
8
9 #define TST_NO_DEFAULT_MAIN
10
11 #include "tst_test.h"
12 #include "tst_timer.h"
13 #include "tst_clocks.h"
14 #include "tst_rtctime.h"
15 #include "tst_wallclock.h"
16 #include "lapi/posix_clocks.h"
17
18 static struct timespec real_begin, mono_begin;
19
20 static struct rtc_time rtc_begin;
21
22 static int clock_saved;
23
tst_wallclock_save(void)24 void tst_wallclock_save(void)
25 {
26 /* save initial monotonic time to restore it when needed */
27
28 if (tst_clock_gettime(CLOCK_REALTIME, &real_begin))
29 tst_brk(TBROK | TERRNO, "tst_clock_gettime() realtime failed");
30
31 if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_begin)) {
32 if (errno == EINVAL) {
33 tst_brk(TCONF | TERRNO,
34 "tst_clock_gettime() didn't support CLOCK_MONOTONIC_RAW");
35 }
36
37 tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed");
38 }
39
40 clock_saved = 1;
41 }
42
tst_wallclock_restore(void)43 void tst_wallclock_restore(void)
44 {
45 static const char *localtime = "/etc/localtime";
46 static struct timespec mono_end, elapsed, adjust;
47 int ret;
48
49 if (!clock_saved)
50 return;
51
52 clock_saved = 0;
53
54 if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_end))
55 tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed");
56
57 elapsed = tst_timespec_diff(mono_end, mono_begin);
58
59 adjust = tst_timespec_add(real_begin, elapsed);
60
61 /* restore realtime clock based on monotonic delta */
62
63 if (tst_clock_settime(CLOCK_REALTIME, &adjust))
64 tst_brk(TBROK | TERRNO, "tst_clock_settime() realtime failed");
65
66 /*
67 * Fix access time of /etc/localtime because adjusting the wallclock
68 * might have changed it to a time value which lies far ahead
69 * in the future.
70 * The access time of a file only changes if the new one is past
71 * the current one, therefore, just opening a file and reading it
72 * might not be enough because the current access time might be far
73 * in the future.
74 */
75 ret = access(localtime, F_OK | W_OK);
76 if (!ret)
77 SAFE_TOUCH(localtime, 0, NULL);
78 }
79
tst_rtc_clock_save(const char * rtc_dev)80 void tst_rtc_clock_save(const char *rtc_dev)
81 {
82 /* save initial monotonic time to restore it when needed */
83 if (tst_rtc_gettime(rtc_dev, &rtc_begin))
84 tst_brk(TBROK | TERRNO, "tst_rtc_gettime() realtime failed");
85
86 if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_begin))
87 tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed");
88
89 clock_saved = 1;
90 }
91
tst_rtc_clock_restore(const char * rtc_dev)92 void tst_rtc_clock_restore(const char *rtc_dev)
93 {
94 static struct timespec mono_end, elapsed;
95 static struct timespec rtc_begin_tm, rtc_adjust;
96 static struct rtc_time rtc_restore;
97
98 if (!clock_saved)
99 return;
100
101 clock_saved = 0;
102
103 if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_end))
104 tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed");
105
106 elapsed = tst_timespec_diff(mono_end, mono_begin);
107
108 rtc_begin_tm.tv_nsec = 0;
109 rtc_begin_tm.tv_sec = tst_rtc_tm_to_time(&rtc_begin);
110
111 rtc_adjust = tst_timespec_add(rtc_begin_tm, elapsed);
112
113 tst_rtc_time_to_tm(rtc_adjust.tv_sec, &rtc_restore);
114
115 /* restore realtime clock based on monotonic delta */
116 if (tst_rtc_settime(rtc_dev, &rtc_restore))
117 tst_brk(TBROK | TERRNO, "tst_rtc_settime() realtime failed");
118 }
119