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