xref: /third_party/ltp/lib/tst_wallclock.c (revision f08c3bdf)
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
18static struct timespec real_begin, mono_begin;
19
20static struct rtc_time rtc_begin;
21
22static int clock_saved;
23
24void 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
43void 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
80void 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
92void 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