| // SPDX-License-Identifier: GPL-2.0-or-later | |
| /* | |
| * Copyright (c) 2019 Linaro Limited. All rights reserved. | |
| * Author: Rafael David Tinoco <[email protected]> | |
| */ | |
| #include <errno.h> | |
| #define TST_NO_DEFAULT_MAIN | |
| #include "tst_test.h" | |
| #include "tst_timer.h" | |
| #include "tst_clocks.h" | |
| #include "tst_rtctime.h" | |
| #include "tst_wallclock.h" | |
| #include "lapi/posix_clocks.h" | |
| static struct timespec real_begin, mono_begin; | |
| static struct rtc_time rtc_begin; | |
| static int clock_saved; | |
| void tst_wallclock_save(void) | |
| { | |
| /* save initial monotonic time to restore it when needed */ | |
| if (tst_clock_gettime(CLOCK_REALTIME, &real_begin)) | |
| tst_brk(TBROK | TERRNO, "tst_clock_gettime() realtime failed"); | |
| if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_begin)) { | |
| if (errno == EINVAL) { | |
| tst_brk(TCONF | TERRNO, | |
| "tst_clock_gettime() didn't support CLOCK_MONOTONIC_RAW"); | |
| } | |
| tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed"); | |
| } | |
| clock_saved = 1; | |
| } | |
| void tst_wallclock_restore(void) | |
| { | |
| static const char *localtime = "/etc/localtime"; | |
| static struct timespec mono_end, elapsed, adjust; | |
| int ret; | |
| if (!clock_saved) | |
| return; | |
| clock_saved = 0; | |
| if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_end)) | |
| tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed"); | |
| elapsed = tst_timespec_diff(mono_end, mono_begin); | |
| adjust = tst_timespec_add(real_begin, elapsed); | |
| /* restore realtime clock based on monotonic delta */ | |
| if (tst_clock_settime(CLOCK_REALTIME, &adjust)) | |
| tst_brk(TBROK | TERRNO, "tst_clock_settime() realtime failed"); | |
| /* | |
| * Fix access time of /etc/localtime because adjusting the wallclock | |
| * might have changed it to a time value which lies far ahead | |
| * in the future. | |
| * The access time of a file only changes if the new one is past | |
| * the current one, therefore, just opening a file and reading it | |
| * might not be enough because the current access time might be far | |
| * in the future. | |
| */ | |
| ret = access(localtime, F_OK | W_OK); | |
| if (!ret) | |
| SAFE_TOUCH(localtime, 0, NULL); | |
| } | |
| void tst_rtc_clock_save(const char *rtc_dev) | |
| { | |
| /* save initial monotonic time to restore it when needed */ | |
| if (tst_rtc_gettime(rtc_dev, &rtc_begin)) | |
| tst_brk(TBROK | TERRNO, "tst_rtc_gettime() realtime failed"); | |
| if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_begin)) | |
| tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed"); | |
| clock_saved = 1; | |
| } | |
| void tst_rtc_clock_restore(const char *rtc_dev) | |
| { | |
| static struct timespec mono_end, elapsed; | |
| static struct timespec rtc_begin_tm, rtc_adjust; | |
| static struct rtc_time rtc_restore; | |
| if (!clock_saved) | |
| return; | |
| clock_saved = 0; | |
| if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_end)) | |
| tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed"); | |
| elapsed = tst_timespec_diff(mono_end, mono_begin); | |
| rtc_begin_tm.tv_nsec = 0; | |
| rtc_begin_tm.tv_sec = tst_rtc_tm_to_time(&rtc_begin); | |
| rtc_adjust = tst_timespec_add(rtc_begin_tm, elapsed); | |
| tst_rtc_time_to_tm(rtc_adjust.tv_sec, &rtc_restore); | |
| /* restore realtime clock based on monotonic delta */ | |
| if (tst_rtc_settime(rtc_dev, &rtc_restore)) | |
| tst_brk(TBROK | TERRNO, "tst_rtc_settime() realtime failed"); | |
| } |