diff --git a/sys/sys/time.h b/sys/sys/time.h index 7520478cf3ed..ce4c7c1b555e 100644 --- a/sys/sys/time.h +++ b/sys/sys/time.h @@ -1,618 +1,624 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)time.h 8.5 (Berkeley) 5/4/95 * $FreeBSD$ */ #ifndef _SYS_TIME_H_ #define _SYS_TIME_H_ #include #include #include #include struct timezone { int tz_minuteswest; /* minutes west of Greenwich */ int tz_dsttime; /* type of dst correction */ }; #define DST_NONE 0 /* not on dst */ #define DST_USA 1 /* USA style dst */ #define DST_AUST 2 /* Australian style dst */ #define DST_WET 3 /* Western European dst */ #define DST_MET 4 /* Middle European dst */ #define DST_EET 5 /* Eastern European dst */ #define DST_CAN 6 /* Canada */ #if __BSD_VISIBLE struct bintime { time_t sec; uint64_t frac; }; static __inline void bintime_addx(struct bintime *_bt, uint64_t _x) { uint64_t _u; _u = _bt->frac; _bt->frac += _x; if (_u > _bt->frac) _bt->sec++; } static __inline void bintime_add(struct bintime *_bt, const struct bintime *_bt2) { uint64_t _u; _u = _bt->frac; _bt->frac += _bt2->frac; if (_u > _bt->frac) _bt->sec++; _bt->sec += _bt2->sec; } static __inline void bintime_sub(struct bintime *_bt, const struct bintime *_bt2) { uint64_t _u; _u = _bt->frac; _bt->frac -= _bt2->frac; if (_u < _bt->frac) _bt->sec--; _bt->sec -= _bt2->sec; } static __inline void bintime_mul(struct bintime *_bt, u_int _x) { uint64_t _p1, _p2; _p1 = (_bt->frac & 0xffffffffull) * _x; _p2 = (_bt->frac >> 32) * _x + (_p1 >> 32); _bt->sec *= _x; _bt->sec += (_p2 >> 32); _bt->frac = (_p2 << 32) | (_p1 & 0xffffffffull); } static __inline void bintime_shift(struct bintime *_bt, int _exp) { if (_exp > 0) { _bt->sec <<= _exp; _bt->sec |= _bt->frac >> (64 - _exp); _bt->frac <<= _exp; } else if (_exp < 0) { _bt->frac >>= -_exp; _bt->frac |= (uint64_t)_bt->sec << (64 + _exp); _bt->sec >>= -_exp; } } #define bintime_clear(a) ((a)->sec = (a)->frac = 0) #define bintime_isset(a) ((a)->sec || (a)->frac) #define bintime_cmp(a, b, cmp) \ (((a)->sec == (b)->sec) ? \ ((a)->frac cmp (b)->frac) : \ ((a)->sec cmp (b)->sec)) #define SBT_1S ((sbintime_t)1 << 32) #define SBT_1M (SBT_1S * 60) #define SBT_1MS (SBT_1S / 1000) #define SBT_1US (SBT_1S / 1000000) #define SBT_1NS (SBT_1S / 1000000000) /* beware rounding, see nstosbt() */ #define SBT_MAX 0x7fffffffffffffffLL static __inline int sbintime_getsec(sbintime_t _sbt) { return (_sbt >> 32); } static __inline sbintime_t bttosbt(const struct bintime _bt) { return (((sbintime_t)_bt.sec << 32) + (_bt.frac >> 32)); } static __inline struct bintime sbttobt(sbintime_t _sbt) { struct bintime _bt; _bt.sec = _sbt >> 32; _bt.frac = _sbt << 32; return (_bt); } /* * Decimal<->sbt conversions. Multiplying or dividing by SBT_1NS results in * large roundoff errors which sbttons() and nstosbt() avoid. Millisecond and * microsecond functions are also provided for completeness. * * These functions return the smallest sbt larger or equal to the * number of seconds requested so that sbttoX(Xtosbt(y)) == y. Unlike * top of second computations below, which require that we tick at the * top of second, these need to be rounded up so we do whatever for at * least as long as requested. * * The naive computation we'd do is this * ((unit * 2^64 / SIFACTOR) + 2^32-1) >> 32 * However, that overflows. Instead, we compute * ((unit * 2^63 / SIFACTOR) + 2^31-1) >> 32 * and use pre-computed constants that are the ceil of the 2^63 / SIFACTOR * term to ensure we are using exactly the right constant. We use the lesser * evil of ull rather than a uint64_t cast to ensure we have well defined * right shift semantics. With these changes, we get all the ns, us and ms * conversions back and forth right. * Note: This file is used for both kernel and userland includes, so we can't * rely on KASSERT being defined, nor can we pollute the namespace by including * assert.h. */ static __inline int64_t sbttons(sbintime_t _sbt) { uint64_t ns; #ifdef KASSERT KASSERT(_sbt >= 0, ("Negative values illegal for sbttons: %jx", _sbt)); #endif ns = _sbt; if (ns >= SBT_1S) ns = (ns >> 32) * 1000000000; else ns = 0; return (ns + (1000000000 * (_sbt & 0xffffffffu) >> 32)); } static __inline sbintime_t nstosbt(int64_t _ns) { sbintime_t sb = 0; #ifdef KASSERT KASSERT(_ns >= 0, ("Negative values illegal for nstosbt: %jd", _ns)); #endif if (_ns >= 1000000000) { sb = (_ns / 1000000000) * SBT_1S; _ns = _ns % 1000000000; } /* 9223372037 = ceil(2^63 / 1000000000) */ sb += ((_ns * 9223372037ull) + 0x7fffffff) >> 31; return (sb); } static __inline int64_t sbttous(sbintime_t _sbt) { - return ((1000000 * _sbt) >> 32); +#ifdef KASSERT + KASSERT(_sbt >= 0, ("Negative values illegal for sbttous: %jx", _sbt)); +#endif + return ((_sbt >> 32) * 1000000 + + (1000000 * (_sbt & 0xffffffffu) >> 32)); } static __inline sbintime_t ustosbt(int64_t _us) { sbintime_t sb = 0; #ifdef KASSERT KASSERT(_us >= 0, ("Negative values illegal for ustosbt: %jd", _us)); #endif if (_us >= 1000000) { sb = (_us / 1000000) * SBT_1S; _us = _us % 1000000; } /* 9223372036855 = ceil(2^63 / 1000000) */ sb += ((_us * 9223372036855ull) + 0x7fffffff) >> 31; return (sb); } static __inline int64_t sbttoms(sbintime_t _sbt) { - - return ((1000 * _sbt) >> 32); +#ifdef KASSERT + KASSERT(_sbt >= 0, ("Negative values illegal for sbttoms: %jx", _sbt)); +#endif + return ((_sbt >> 32) * 1000 + (1000 * (_sbt & 0xffffffffu) >> 32)); } static __inline sbintime_t mstosbt(int64_t _ms) { sbintime_t sb = 0; #ifdef KASSERT KASSERT(_ms >= 0, ("Negative values illegal for mstosbt: %jd", _ms)); #endif if (_ms >= 1000) { sb = (_ms / 1000) * SBT_1S; _ms = _ms % 1000; } /* 9223372036854776 = ceil(2^63 / 1000) */ sb += ((_ms * 9223372036854776ull) + 0x7fffffff) >> 31; return (sb); } /*- * Background information: * * When converting between timestamps on parallel timescales of differing * resolutions it is historical and scientific practice to round down rather * than doing 4/5 rounding. * * The date changes at midnight, not at noon. * * Even at 15:59:59.999999999 it's not four'o'clock. * * time_second ticks after N.999999999 not after N.4999999999 */ static __inline void bintime2timespec(const struct bintime *_bt, struct timespec *_ts) { _ts->tv_sec = _bt->sec; _ts->tv_nsec = ((uint64_t)1000000000 * (uint32_t)(_bt->frac >> 32)) >> 32; } static __inline uint64_t bintime2ns(const struct bintime *_bt) { uint64_t ret; ret = (uint64_t)(_bt->sec) * (uint64_t)1000000000; ret += (((uint64_t)1000000000 * (uint32_t)(_bt->frac >> 32)) >> 32); return (ret); } static __inline void timespec2bintime(const struct timespec *_ts, struct bintime *_bt) { _bt->sec = _ts->tv_sec; /* 18446744073 = int(2^64 / 1000000000) */ _bt->frac = _ts->tv_nsec * (uint64_t)18446744073LL; } static __inline void bintime2timeval(const struct bintime *_bt, struct timeval *_tv) { _tv->tv_sec = _bt->sec; _tv->tv_usec = ((uint64_t)1000000 * (uint32_t)(_bt->frac >> 32)) >> 32; } static __inline void timeval2bintime(const struct timeval *_tv, struct bintime *_bt) { _bt->sec = _tv->tv_sec; /* 18446744073709 = int(2^64 / 1000000) */ _bt->frac = _tv->tv_usec * (uint64_t)18446744073709LL; } static __inline struct timespec sbttots(sbintime_t _sbt) { struct timespec _ts; _ts.tv_sec = _sbt >> 32; _ts.tv_nsec = sbttons((uint32_t)_sbt); return (_ts); } static __inline sbintime_t tstosbt(struct timespec _ts) { return (((sbintime_t)_ts.tv_sec << 32) + nstosbt(_ts.tv_nsec)); } static __inline struct timeval sbttotv(sbintime_t _sbt) { struct timeval _tv; _tv.tv_sec = _sbt >> 32; _tv.tv_usec = sbttous((uint32_t)_sbt); return (_tv); } static __inline sbintime_t tvtosbt(struct timeval _tv) { return (((sbintime_t)_tv.tv_sec << 32) + ustosbt(_tv.tv_usec)); } #endif /* __BSD_VISIBLE */ #ifdef _KERNEL /* * Simple macros to convert ticks to milliseconds * or microseconds and vice-versa. The answer * will always be at least 1. Note the return * value is a uint32_t however we step up the * operations to 64 bit to avoid any overflow/underflow * problems. */ #define TICKS_2_MSEC(t) max(1, (uint32_t)(hz == 1000) ? \ (t) : (((uint64_t)(t) * (uint64_t)1000)/(uint64_t)hz)) #define TICKS_2_USEC(t) max(1, (uint32_t)(hz == 1000) ? \ ((t) * 1000) : (((uint64_t)(t) * (uint64_t)1000000)/(uint64_t)hz)) #define MSEC_2_TICKS(m) max(1, (uint32_t)((hz == 1000) ? \ (m) : ((uint64_t)(m) * (uint64_t)hz)/(uint64_t)1000)) #define USEC_2_TICKS(u) max(1, (uint32_t)((hz == 1000) ? \ ((u) / 1000) : ((uint64_t)(u) * (uint64_t)hz)/(uint64_t)1000000)) #endif /* Operations on timespecs */ #define timespecclear(tvp) ((tvp)->tv_sec = (tvp)->tv_nsec = 0) #define timespecisset(tvp) ((tvp)->tv_sec || (tvp)->tv_nsec) #define timespeccmp(tvp, uvp, cmp) \ (((tvp)->tv_sec == (uvp)->tv_sec) ? \ ((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \ ((tvp)->tv_sec cmp (uvp)->tv_sec)) #define timespecadd(tsp, usp, vsp) \ do { \ (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ if ((vsp)->tv_nsec >= 1000000000L) { \ (vsp)->tv_sec++; \ (vsp)->tv_nsec -= 1000000000L; \ } \ } while (0) #define timespecsub(tsp, usp, vsp) \ do { \ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ if ((vsp)->tv_nsec < 0) { \ (vsp)->tv_sec--; \ (vsp)->tv_nsec += 1000000000L; \ } \ } while (0) #define timespecvalid_interval(tsp) ((tsp)->tv_sec >= 0 && \ (tsp)->tv_nsec >= 0 && (tsp)->tv_nsec < 1000000000L) #ifdef _KERNEL /* Operations on timevals. */ #define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) #define timevalisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) #define timevalcmp(tvp, uvp, cmp) \ (((tvp)->tv_sec == (uvp)->tv_sec) ? \ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ ((tvp)->tv_sec cmp (uvp)->tv_sec)) /* timevaladd and timevalsub are not inlined */ #endif /* _KERNEL */ #ifndef _KERNEL /* NetBSD/OpenBSD compatible interfaces */ #define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) #define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) #define timercmp(tvp, uvp, cmp) \ (((tvp)->tv_sec == (uvp)->tv_sec) ? \ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ ((tvp)->tv_sec cmp (uvp)->tv_sec)) #define timeradd(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ if ((vvp)->tv_usec >= 1000000) { \ (vvp)->tv_sec++; \ (vvp)->tv_usec -= 1000000; \ } \ } while (0) #define timersub(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) #endif /* * Names of the interval timers, and structure * defining a timer setting. */ #define ITIMER_REAL 0 #define ITIMER_VIRTUAL 1 #define ITIMER_PROF 2 struct itimerval { struct timeval it_interval; /* timer interval */ struct timeval it_value; /* current value */ }; /* * Getkerninfo clock information structure */ struct clockinfo { int hz; /* clock frequency */ int tick; /* micro-seconds per hz tick */ int spare; int stathz; /* statistics clock frequency */ int profhz; /* profiling clock frequency */ }; #if __BSD_VISIBLE #define CPUCLOCK_WHICH_PID 0 #define CPUCLOCK_WHICH_TID 1 #endif #if defined(_KERNEL) || defined(_STANDALONE) /* * Kernel to clock driver interface. */ void inittodr(time_t base); void resettodr(void); extern volatile time_t time_second; extern volatile time_t time_uptime; extern struct bintime tc_tick_bt; extern sbintime_t tc_tick_sbt; extern struct bintime tick_bt; extern sbintime_t tick_sbt; extern int tc_precexp; extern int tc_timepercentage; extern struct bintime bt_timethreshold; extern struct bintime bt_tickthreshold; extern sbintime_t sbt_timethreshold; extern sbintime_t sbt_tickthreshold; extern volatile int rtc_generation; /* * Functions for looking at our clock: [get]{bin,nano,micro}[up]time() * * Functions without the "get" prefix returns the best timestamp * we can produce in the given format. * * "bin" == struct bintime == seconds + 64 bit fraction of seconds. * "nano" == struct timespec == seconds + nanoseconds. * "micro" == struct timeval == seconds + microseconds. * * Functions containing "up" returns time relative to boot and * should be used for calculating time intervals. * * Functions without "up" returns UTC time. * * Functions with the "get" prefix returns a less precise result * much faster than the functions without "get" prefix and should * be used where a precision of 1/hz seconds is acceptable or where * performance is priority. (NB: "precision", _not_ "resolution" !) */ void binuptime(struct bintime *bt); void nanouptime(struct timespec *tsp); void microuptime(struct timeval *tvp); static __inline sbintime_t sbinuptime(void) { struct bintime _bt; binuptime(&_bt); return (bttosbt(_bt)); } void bintime(struct bintime *bt); void nanotime(struct timespec *tsp); void microtime(struct timeval *tvp); void getbinuptime(struct bintime *bt); void getnanouptime(struct timespec *tsp); void getmicrouptime(struct timeval *tvp); static __inline sbintime_t getsbinuptime(void) { struct bintime _bt; getbinuptime(&_bt); return (bttosbt(_bt)); } void getbintime(struct bintime *bt); void getnanotime(struct timespec *tsp); void getmicrotime(struct timeval *tvp); void getboottime(struct timeval *boottime); void getboottimebin(struct bintime *boottimebin); /* Other functions */ int itimerdecr(struct itimerval *itp, int usec); int itimerfix(struct timeval *tv); int ppsratecheck(struct timeval *, int *, int); int ratecheck(struct timeval *, const struct timeval *); void timevaladd(struct timeval *t1, const struct timeval *t2); void timevalsub(struct timeval *t1, const struct timeval *t2); int tvtohz(struct timeval *tv); #define TC_DEFAULTPERC 5 #define BT2FREQ(bt) \ (((uint64_t)0x8000000000000000 + ((bt)->frac >> 2)) / \ ((bt)->frac >> 1)) #define SBT2FREQ(sbt) ((SBT_1S + ((sbt) >> 1)) / (sbt)) #define FREQ2BT(freq, bt) \ { \ (bt)->sec = 0; \ (bt)->frac = ((uint64_t)0x8000000000000000 / (freq)) << 1; \ } #define TIMESEL(sbt, sbt2) \ (((sbt2) >= sbt_timethreshold) ? \ ((*(sbt) = getsbinuptime()), 1) : ((*(sbt) = sbinuptime()), 0)) #else /* !_KERNEL && !_STANDALONE */ #include #include #include __BEGIN_DECLS int setitimer(int, const struct itimerval *, struct itimerval *); int utimes(const char *, const struct timeval *); #if __BSD_VISIBLE int adjtime(const struct timeval *, struct timeval *); int clock_getcpuclockid2(id_t, int, clockid_t *); int futimes(int, const struct timeval *); int futimesat(int, const char *, const struct timeval [2]); int lutimes(const char *, const struct timeval *); int settimeofday(const struct timeval *, const struct timezone *); #endif #if __XSI_VISIBLE int getitimer(int, struct itimerval *); int gettimeofday(struct timeval *, struct timezone *); #endif __END_DECLS #endif /* !_KERNEL */ #endif /* !_SYS_TIME_H_ */ diff --git a/tests/sys/sys/Makefile b/tests/sys/sys/Makefile index f6c45971d93c..7b0cf7e92743 100644 --- a/tests/sys/sys/Makefile +++ b/tests/sys/sys/Makefile @@ -1,18 +1,19 @@ # $FreeBSD$ .include TESTSDIR= ${TESTSBASE}/sys/sys ATF_TESTS_C= arb_test \ bitset_test \ bitstring_test \ qmath_test \ rb_test \ - splay_test + splay_test \ + time_test .if ${COMPILER_TYPE} == "gcc" CFLAGS.bitstring_test= -fno-strict-overflow .endif .include diff --git a/tests/sys/sys/time_test.c b/tests/sys/sys/time_test.c new file mode 100644 index 000000000000..ef6e497458f0 --- /dev/null +++ b/tests/sys/sys/time_test.c @@ -0,0 +1,224 @@ +/*- + * Copyright (c) 2022 Axcient + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD$ + */ +#include +#include + +#include +#include + +#include + + +static void +atf_check_nstosbt(sbintime_t expected, int64_t ns) { + sbintime_t actual = nstosbt(ns); + + ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1, + "%"PRId64" != nstosbt(%"PRId64") (%"PRId64")", + expected, ns, actual); +} + +ATF_TC_WITHOUT_HEAD(nstosbt); +ATF_TC_BODY(nstosbt, tc) +{ + atf_check_nstosbt(0, 0); + atf_check_nstosbt(4, 1); + /* 1 second */ + atf_check_nstosbt((1ll << 32) - 4, 999999999); + atf_check_nstosbt(1ll << 32, 1000000000); + /* 2 seconds https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263073 */ + atf_check_nstosbt((1ll << 33) - 4, 1999999999); + atf_check_nstosbt(1ll << 33, 2000000000); + /* 4 seconds */ + atf_check_nstosbt((1ll << 34) - 4, 3999999999); + atf_check_nstosbt((1ll << 34), 4000000000); + /* Max value */ + atf_check_nstosbt(((1ll << 31) - 1) << 32, + ((1ll << 31) - 1) * 1000000000); +} + +static void +atf_check_ustosbt(sbintime_t expected, int64_t us) { + sbintime_t actual = ustosbt(us); + + ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1, + "%"PRId64" != ustosbt(%"PRId64") (%"PRId64")", + expected, us, actual); +} + +ATF_TC_WITHOUT_HEAD(ustosbt); +ATF_TC_BODY(ustosbt, tc) +{ + atf_check_ustosbt(0, 0); + atf_check_ustosbt(4295, 1); + /* 1 second */ + atf_check_ustosbt((1ll << 32) - 4295, 999999); + atf_check_ustosbt(1ll << 32, 1000000); + /* 2 seconds https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263073 */ + atf_check_ustosbt((1ll << 33) - 4295, 1999999); + atf_check_ustosbt(1ll << 33, 2000000); + /* 4 seconds */ + atf_check_ustosbt((1ll << 34) - 4295, 3999999); + atf_check_ustosbt(1ll << 34, 4000000); + /* Max value */ + atf_check_ustosbt(((1ull << 31) - 1) << 32, + ((1ll << 31) - 1) * 1000000); +} + +static void +atf_check_mstosbt(sbintime_t expected, int64_t ms) { + sbintime_t actual = mstosbt(ms); + + ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1, + "%"PRId64" != mstosbt(%"PRId64") (%"PRId64")", + expected, ms, actual); +} + +ATF_TC_WITHOUT_HEAD(mstosbt); +ATF_TC_BODY(mstosbt, tc) +{ + atf_check_mstosbt(0, 0); + atf_check_mstosbt(4294967, 1); + /* 1 second */ + atf_check_mstosbt((1ll << 32) - 4294968, 999); + atf_check_mstosbt(1ll << 32, 1000); + /* 2 seconds https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263073 */ + atf_check_mstosbt((1ll << 33) - 4294968, 1999); + atf_check_mstosbt(1ll << 33, 2000); + /* 4 seconds */ + atf_check_mstosbt((1ll << 34) - 4294968, 3999); + atf_check_mstosbt(1ll << 34, 4000); + /* Max value */ + atf_check_mstosbt(((1ll << 31) - 1) << 32, ((1ll << 31) - 1) * 1000); +} + +static void +atf_check_sbttons(int64_t expected, sbintime_t sbt) { + int64_t actual = sbttons(sbt); + + ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1, + "%"PRId64" != sbttons(%"PRId64") (%"PRId64")", + expected, sbt, actual); +} + +ATF_TC_WITHOUT_HEAD(sbttons); +ATF_TC_BODY(sbttons, tc) +{ + atf_check_sbttons(0, 0); + atf_check_sbttons(0, 1); + atf_check_sbttons(1, (1ll << 32) / 1000000000); + /* 1 second */ + atf_check_sbttons(1000000000, 1ll << 32); + atf_check_sbttons(1999999999, (1ll << 33) - 1); + /* 2 seconds */ + atf_check_sbttons(1999999999, (1ll << 33) - 1); + atf_check_sbttons(2000000000, 1ll << 33); + /* 4 seconds */ + atf_check_sbttons(3999999999, (1ll << 34) - 1); + atf_check_sbttons(4000000000, 1ll << 34); + /* edge cases */ + atf_check_sbttons(999999999, (1ll << 32) - 1); + atf_check_sbttons((1ll << 31) * 1000000000, (1ull << 63) - 1); +} + +static void +atf_check_sbttous(int64_t expected, sbintime_t sbt) { + int64_t actual = sbttous(sbt); + + ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1, + "%"PRId64" != sbttous(%"PRId64") (%"PRId64")", + expected, sbt, actual); +} + +ATF_TC_WITHOUT_HEAD(sbttous); +ATF_TC_BODY(sbttous, tc) +{ + atf_check_sbttous(0, 0); + atf_check_sbttous(0, 1); + atf_check_sbttous(1, (1ll << 32) / 1000000); + /* 1 second */ + atf_check_sbttous(1000000, 1ll << 32); + atf_check_sbttous(1999999, (1ll << 33) - 1); + /* 2 seconds */ + atf_check_sbttous(1999999, (1ll << 33) - 1); + atf_check_sbttous(2000000, 1ll << 33); + /* 4 seconds */ + atf_check_sbttous(3999999, (1ll << 34) -1); + atf_check_sbttous(4000000, 1ll << 34); + /* Overflows (bug 263073) */ + atf_check_sbttous(1ll << 31, (1ull << 63) / 1000000); + atf_check_sbttous(1ll << 31, (1ull << 63) / 1000000 + 1); + atf_check_sbttous((1ll << 31) * 1000000, (1ull << 63) - 1); +} + +static void +atf_check_sbttoms(int64_t expected, sbintime_t sbt) { + int64_t actual = sbttoms(sbt); + + ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1, + "%"PRId64" != sbttoms(%"PRId64") (%"PRId64")", + expected, sbt, actual); +} + +ATF_TC_WITHOUT_HEAD(sbttoms); +ATF_TC_BODY(sbttoms, tc) +{ + atf_check_sbttoms(0, 0); + atf_check_sbttoms(0, 1); + atf_check_sbttoms(1, (1ll << 32) / 1000); + /* 1 second */ + atf_check_sbttoms(999, (1ll << 32) - 1); + atf_check_sbttoms(1000, 1ll << 32); + /* 2 seconds */ + atf_check_sbttoms(1999, (1ll << 33) - 1); + atf_check_sbttoms(2000, 1ll << 33); + /* 4 seconds */ + atf_check_sbttoms(3999, (1ll << 34) - 1); + atf_check_sbttoms(4000, 1ll << 34); + /* Overflows (bug 263073) */ + atf_check_sbttoms(1ll << 31, (1ull << 63) / 1000); + atf_check_sbttoms(1ll << 31, (1ull << 63) / 1000 + 1); + atf_check_sbttoms((1ll << 31) * 1000, (1ull << 63) - 1); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, nstosbt); + ATF_TP_ADD_TC(tp, ustosbt); + ATF_TP_ADD_TC(tp, mstosbt); + ATF_TP_ADD_TC(tp, sbttons); + ATF_TP_ADD_TC(tp, sbttous); + ATF_TP_ADD_TC(tp, sbttoms); + + return (atf_no_error()); +}