1*127fa8d5Scheloha /* $OpenBSD: kern_tc.c,v 1.83 2024/02/23 23:01:15 cheloha Exp $ */
2a515b5bdSbeck
33c7a1782Sbeck /*
43c7a1782Sbeck * Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org>
53751347eStholo *
63c7a1782Sbeck * Permission to use, copy, modify, and distribute this software for any
73c7a1782Sbeck * purpose with or without fee is hereby granted, provided that the above
83c7a1782Sbeck * copyright notice and this permission notice appear in all copies.
93c7a1782Sbeck *
103c7a1782Sbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113c7a1782Sbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123c7a1782Sbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133c7a1782Sbeck * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143c7a1782Sbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153c7a1782Sbeck * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163c7a1782Sbeck * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173c7a1782Sbeck */
183c7a1782Sbeck
193c7a1782Sbeck /*
203c7a1782Sbeck * If we meet some day, and you think this stuff is worth it, you
213c7a1782Sbeck * can buy me a beer in return. Poul-Henning Kamp
223751347eStholo */
233751347eStholo
243751347eStholo #include <sys/param.h>
25a701e5dfSbluhm #include <sys/atomic.h>
263751347eStholo #include <sys/kernel.h>
271d8de610Scheloha #include <sys/mutex.h>
28af3eeb45Scheloha #include <sys/rwlock.h>
2974106511Scheloha #include <sys/stdint.h>
307f58a11fSjsg #include <sys/timeout.h>
313751347eStholo #include <sys/sysctl.h>
323751347eStholo #include <sys/syslog.h>
333751347eStholo #include <sys/systm.h>
343751347eStholo #include <sys/timetc.h>
358334e679Scheloha #include <sys/queue.h>
363751347eStholo #include <sys/malloc.h>
373751347eStholo
385a8003fbSgrange u_int dummy_get_timecount(struct timecounter *);
395a8003fbSgrange
403751347eStholo int sysctl_tc_hardware(void *, size_t *, void *, size_t);
413751347eStholo int sysctl_tc_choice(void *, size_t *, void *, size_t);
423751347eStholo
433751347eStholo /*
443751347eStholo * Implement a dummy timecounter which we can use until we get a real one
453751347eStholo * in the air. This allows the console and other early stuff to use
463751347eStholo * time services.
473751347eStholo */
483751347eStholo
495a8003fbSgrange u_int
dummy_get_timecount(struct timecounter * tc)503751347eStholo dummy_get_timecount(struct timecounter *tc)
513751347eStholo {
523751347eStholo static u_int now;
533751347eStholo
54e62bad27Scheloha return atomic_inc_int_nv(&now);
553751347eStholo }
563751347eStholo
573751347eStholo static struct timecounter dummy_timecounter = {
588611d3cdScheloha .tc_get_timecount = dummy_get_timecount,
598611d3cdScheloha .tc_counter_mask = ~0u,
608611d3cdScheloha .tc_frequency = 1000000,
618611d3cdScheloha .tc_name = "dummy",
628611d3cdScheloha .tc_quality = -1000000,
638611d3cdScheloha .tc_priv = NULL,
648611d3cdScheloha .tc_user = 0,
653751347eStholo };
663751347eStholo
67af3eeb45Scheloha /*
68af3eeb45Scheloha * Locks used to protect struct members, global variables in this file:
69af3eeb45Scheloha * I immutable after initialization
70b609c616Santon * T tc_lock
71b609c616Santon * W windup_mtx
72af3eeb45Scheloha */
73af3eeb45Scheloha
743751347eStholo struct timehands {
753751347eStholo /* These fields must be initialized by the driver. */
76b609c616Santon struct timecounter *th_counter; /* [W] */
77b609c616Santon int64_t th_adjtimedelta; /* [T,W] */
78fecf25f8Scheloha struct bintime th_next_ntp_update; /* [T,W] */
79b609c616Santon int64_t th_adjustment; /* [W] */
80b609c616Santon u_int64_t th_scale; /* [W] */
81b609c616Santon u_int th_offset_count; /* [W] */
82b609c616Santon struct bintime th_boottime; /* [T,W] */
83b609c616Santon struct bintime th_offset; /* [W] */
84b609c616Santon struct bintime th_naptime; /* [W] */
85b609c616Santon struct timeval th_microtime; /* [W] */
86b609c616Santon struct timespec th_nanotime; /* [W] */
873751347eStholo /* Fields not to be copied in tc_windup start with th_generation. */
88b609c616Santon volatile u_int th_generation; /* [W] */
89af3eeb45Scheloha struct timehands *th_next; /* [I] */
903751347eStholo };
913751347eStholo
926ab36b32Srobert static struct timehands th0;
9374106511Scheloha static struct timehands th1 = {
9474106511Scheloha .th_next = &th0
9574106511Scheloha };
963751347eStholo static struct timehands th0 = {
9774106511Scheloha .th_counter = &dummy_timecounter,
9874106511Scheloha .th_scale = UINT64_MAX / 1000000,
99*127fa8d5Scheloha .th_offset = { .sec = 0, .frac = 0 },
10074106511Scheloha .th_generation = 1,
10174106511Scheloha .th_next = &th1
1023751347eStholo };
1033751347eStholo
104af3eeb45Scheloha struct rwlock tc_lock = RWLOCK_INITIALIZER("tc_lock");
105af3eeb45Scheloha
1061d8de610Scheloha /*
1071d8de610Scheloha * tc_windup() must be called before leaving this mutex.
1081d8de610Scheloha */
109ceab5aefScheloha struct mutex windup_mtx = MUTEX_INITIALIZER(IPL_CLOCK);
1101d8de610Scheloha
111b609c616Santon static struct timehands *volatile timehands = &th0; /* [W] */
112b609c616Santon struct timecounter *timecounter = &dummy_timecounter; /* [T] */
1138334e679Scheloha static SLIST_HEAD(, timecounter) tc_list = SLIST_HEAD_INITIALIZER(tc_list);
1143751347eStholo
1150d88cff5Scheloha /*
1160d88cff5Scheloha * These are updated from tc_windup(). They are useful when
1170d88cff5Scheloha * examining kernel core dumps.
1180d88cff5Scheloha */
11997c55bccScheloha volatile time_t naptime = 0;
120*127fa8d5Scheloha volatile time_t time_second = 0;
121ead574e1Sart volatile time_t time_uptime = 0;
1223751347eStholo
1233751347eStholo static int timestepwarnings;
1243751347eStholo
125c54148e4Scheloha void ntp_update_second(struct timehands *);
126c54148e4Scheloha void tc_windup(struct bintime *, struct bintime *, int64_t *);
1273751347eStholo
1283751347eStholo /*
1293751347eStholo * Return the difference between the timehands' counter value now and what
1303751347eStholo * was when we copied it to the timehands' offset_count.
1313751347eStholo */
1323751347eStholo static __inline u_int
tc_delta(struct timehands * th)1333751347eStholo tc_delta(struct timehands *th)
1343751347eStholo {
1353751347eStholo struct timecounter *tc;
1363751347eStholo
1373751347eStholo tc = th->th_counter;
1383751347eStholo return ((tc->tc_get_timecount(tc) - th->th_offset_count) &
1393751347eStholo tc->tc_counter_mask);
1403751347eStholo }
1413751347eStholo
1423751347eStholo /*
1433751347eStholo * Functions for reading the time. We have to loop until we are sure that
1443751347eStholo * the timehands that we operated on was not updated under our feet. See
145fa5a0c50Scheloha * the comment in <sys/time.h> for a description of these functions.
1463751347eStholo */
1473751347eStholo
1483751347eStholo void
binboottime(struct bintime * bt)149fa5a0c50Scheloha binboottime(struct bintime *bt)
150fa5a0c50Scheloha {
151fa5a0c50Scheloha struct timehands *th;
152fa5a0c50Scheloha u_int gen;
153fa5a0c50Scheloha
154fa5a0c50Scheloha do {
155fa5a0c50Scheloha th = timehands;
156fa5a0c50Scheloha gen = th->th_generation;
157fa5a0c50Scheloha membar_consumer();
158fa5a0c50Scheloha *bt = th->th_boottime;
159fa5a0c50Scheloha membar_consumer();
160fa5a0c50Scheloha } while (gen == 0 || gen != th->th_generation);
161fa5a0c50Scheloha }
162fa5a0c50Scheloha
163fa5a0c50Scheloha void
microboottime(struct timeval * tvp)164fa5a0c50Scheloha microboottime(struct timeval *tvp)
165fa5a0c50Scheloha {
166fa5a0c50Scheloha struct bintime bt;
167fa5a0c50Scheloha
168fa5a0c50Scheloha binboottime(&bt);
16975b45b05Scheloha BINTIME_TO_TIMEVAL(&bt, tvp);
170fa5a0c50Scheloha }
171fa5a0c50Scheloha
172fa5a0c50Scheloha void
nanoboottime(struct timespec * tsp)17302f434f1Scheloha nanoboottime(struct timespec *tsp)
17402f434f1Scheloha {
17502f434f1Scheloha struct bintime bt;
17602f434f1Scheloha
17702f434f1Scheloha binboottime(&bt);
17802f434f1Scheloha BINTIME_TO_TIMESPEC(&bt, tsp);
17902f434f1Scheloha }
18002f434f1Scheloha
18102f434f1Scheloha void
binuptime(struct bintime * bt)1823751347eStholo binuptime(struct bintime *bt)
1833751347eStholo {
1843751347eStholo struct timehands *th;
1853751347eStholo u_int gen;
1863751347eStholo
1873751347eStholo do {
1883751347eStholo th = timehands;
1893751347eStholo gen = th->th_generation;
190a701e5dfSbluhm membar_consumer();
19183dc7839Scheloha TIMECOUNT_TO_BINTIME(tc_delta(th), th->th_scale, bt);
19283dc7839Scheloha bintimeadd(bt, &th->th_offset, bt);
193a701e5dfSbluhm membar_consumer();
1943751347eStholo } while (gen == 0 || gen != th->th_generation);
1953751347eStholo }
1963751347eStholo
1973751347eStholo void
getbinuptime(struct bintime * bt)1984ea72498Sdlg getbinuptime(struct bintime *bt)
1994ea72498Sdlg {
2004ea72498Sdlg struct timehands *th;
2014ea72498Sdlg u_int gen;
2024ea72498Sdlg
2034ea72498Sdlg do {
2044ea72498Sdlg th = timehands;
2054ea72498Sdlg gen = th->th_generation;
2064ea72498Sdlg membar_consumer();
2074ea72498Sdlg *bt = th->th_offset;
2084ea72498Sdlg membar_consumer();
2094ea72498Sdlg } while (gen == 0 || gen != th->th_generation);
2104ea72498Sdlg }
2114ea72498Sdlg
2124ea72498Sdlg void
nanouptime(struct timespec * tsp)2133751347eStholo nanouptime(struct timespec *tsp)
2143751347eStholo {
2153751347eStholo struct bintime bt;
2163751347eStholo
2173751347eStholo binuptime(&bt);
21875b45b05Scheloha BINTIME_TO_TIMESPEC(&bt, tsp);
2193751347eStholo }
2203751347eStholo
2213751347eStholo void
microuptime(struct timeval * tvp)2223751347eStholo microuptime(struct timeval *tvp)
2233751347eStholo {
2243751347eStholo struct bintime bt;
2253751347eStholo
2263751347eStholo binuptime(&bt);
22775b45b05Scheloha BINTIME_TO_TIMEVAL(&bt, tvp);
2283751347eStholo }
2293751347eStholo
2308dca5d44Scheloha time_t
getuptime(void)2318dca5d44Scheloha getuptime(void)
2328dca5d44Scheloha {
2338dca5d44Scheloha #if defined(__LP64__)
2348dca5d44Scheloha return time_uptime; /* atomic */
2358dca5d44Scheloha #else
2368dca5d44Scheloha time_t now;
2378dca5d44Scheloha struct timehands *th;
2388dca5d44Scheloha u_int gen;
2398dca5d44Scheloha
2408dca5d44Scheloha do {
2418dca5d44Scheloha th = timehands;
2428dca5d44Scheloha gen = th->th_generation;
2438dca5d44Scheloha membar_consumer();
2448dca5d44Scheloha now = th->th_offset.sec;
2458dca5d44Scheloha membar_consumer();
2468dca5d44Scheloha } while (gen == 0 || gen != th->th_generation);
2478dca5d44Scheloha
2488dca5d44Scheloha return now;
2498dca5d44Scheloha #endif
2508dca5d44Scheloha }
2518dca5d44Scheloha
2524ea72498Sdlg uint64_t
nsecuptime(void)2534ea72498Sdlg nsecuptime(void)
2544ea72498Sdlg {
2554ea72498Sdlg struct bintime bt;
2564ea72498Sdlg
2574ea72498Sdlg binuptime(&bt);
2582f582782Scheloha return BINTIME_TO_NSEC(&bt);
2594ea72498Sdlg }
2604ea72498Sdlg
2614ea72498Sdlg uint64_t
getnsecuptime(void)2624ea72498Sdlg getnsecuptime(void)
2634ea72498Sdlg {
2644ea72498Sdlg struct bintime bt;
2654ea72498Sdlg
2664ea72498Sdlg getbinuptime(&bt);
2672f582782Scheloha return BINTIME_TO_NSEC(&bt);
2684ea72498Sdlg }
2694ea72498Sdlg
2703751347eStholo void
binruntime(struct bintime * bt)27187ba7848Scheloha binruntime(struct bintime *bt)
27287ba7848Scheloha {
27387ba7848Scheloha struct timehands *th;
27487ba7848Scheloha u_int gen;
27587ba7848Scheloha
27687ba7848Scheloha do {
27787ba7848Scheloha th = timehands;
27887ba7848Scheloha gen = th->th_generation;
27987ba7848Scheloha membar_consumer();
28083dc7839Scheloha TIMECOUNT_TO_BINTIME(tc_delta(th), th->th_scale, bt);
28183dc7839Scheloha bintimeadd(bt, &th->th_offset, bt);
28287ba7848Scheloha bintimesub(bt, &th->th_naptime, bt);
28387ba7848Scheloha membar_consumer();
28487ba7848Scheloha } while (gen == 0 || gen != th->th_generation);
28587ba7848Scheloha }
28687ba7848Scheloha
28787ba7848Scheloha void
nanoruntime(struct timespec * ts)28887ba7848Scheloha nanoruntime(struct timespec *ts)
28987ba7848Scheloha {
29087ba7848Scheloha struct bintime bt;
29187ba7848Scheloha
29287ba7848Scheloha binruntime(&bt);
29387ba7848Scheloha BINTIME_TO_TIMESPEC(&bt, ts);
29487ba7848Scheloha }
29587ba7848Scheloha
29687ba7848Scheloha void
getbinruntime(struct bintime * bt)2976fba7c69Scheloha getbinruntime(struct bintime *bt)
2986fba7c69Scheloha {
2996fba7c69Scheloha struct timehands *th;
3006fba7c69Scheloha u_int gen;
3016fba7c69Scheloha
3026fba7c69Scheloha do {
3036fba7c69Scheloha th = timehands;
3046fba7c69Scheloha gen = th->th_generation;
3056fba7c69Scheloha membar_consumer();
3066fba7c69Scheloha bintimesub(&th->th_offset, &th->th_naptime, bt);
3076fba7c69Scheloha membar_consumer();
3086fba7c69Scheloha } while (gen == 0 || gen != th->th_generation);
3096fba7c69Scheloha }
3106fba7c69Scheloha
3116fba7c69Scheloha uint64_t
getnsecruntime(void)3126fba7c69Scheloha getnsecruntime(void)
3136fba7c69Scheloha {
3146fba7c69Scheloha struct bintime bt;
3156fba7c69Scheloha
3166fba7c69Scheloha getbinruntime(&bt);
3176fba7c69Scheloha return BINTIME_TO_NSEC(&bt);
3186fba7c69Scheloha }
3196fba7c69Scheloha
3206fba7c69Scheloha void
bintime(struct bintime * bt)3213751347eStholo bintime(struct bintime *bt)
3223751347eStholo {
323fa5a0c50Scheloha struct timehands *th;
324fa5a0c50Scheloha u_int gen;
3253751347eStholo
326fa5a0c50Scheloha do {
327fa5a0c50Scheloha th = timehands;
328fa5a0c50Scheloha gen = th->th_generation;
329fa5a0c50Scheloha membar_consumer();
33083dc7839Scheloha TIMECOUNT_TO_BINTIME(tc_delta(th), th->th_scale, bt);
33183dc7839Scheloha bintimeadd(bt, &th->th_offset, bt);
33275b45b05Scheloha bintimeadd(bt, &th->th_boottime, bt);
333fa5a0c50Scheloha membar_consumer();
334fa5a0c50Scheloha } while (gen == 0 || gen != th->th_generation);
3353751347eStholo }
3363751347eStholo
3373751347eStholo void
nanotime(struct timespec * tsp)3383751347eStholo nanotime(struct timespec *tsp)
3393751347eStholo {
3403751347eStholo struct bintime bt;
3413751347eStholo
3423751347eStholo bintime(&bt);
34375b45b05Scheloha BINTIME_TO_TIMESPEC(&bt, tsp);
3443751347eStholo }
3453751347eStholo
3463751347eStholo void
microtime(struct timeval * tvp)3473751347eStholo microtime(struct timeval *tvp)
3483751347eStholo {
3493751347eStholo struct bintime bt;
3503751347eStholo
3513751347eStholo bintime(&bt);
35275b45b05Scheloha BINTIME_TO_TIMEVAL(&bt, tvp);
3533751347eStholo }
3543751347eStholo
3558dca5d44Scheloha time_t
gettime(void)3568dca5d44Scheloha gettime(void)
3578dca5d44Scheloha {
3588dca5d44Scheloha #if defined(__LP64__)
3598dca5d44Scheloha return time_second; /* atomic */
3608dca5d44Scheloha #else
3618dca5d44Scheloha time_t now;
3628dca5d44Scheloha struct timehands *th;
3638dca5d44Scheloha u_int gen;
3648dca5d44Scheloha
3658dca5d44Scheloha do {
3668dca5d44Scheloha th = timehands;
3678dca5d44Scheloha gen = th->th_generation;
3688dca5d44Scheloha membar_consumer();
3698dca5d44Scheloha now = th->th_microtime.tv_sec;
3708dca5d44Scheloha membar_consumer();
3718dca5d44Scheloha } while (gen == 0 || gen != th->th_generation);
3728dca5d44Scheloha
3738dca5d44Scheloha return now;
3748dca5d44Scheloha #endif
3758dca5d44Scheloha }
3768dca5d44Scheloha
3773751347eStholo void
getnanouptime(struct timespec * tsp)3783751347eStholo getnanouptime(struct timespec *tsp)
3793751347eStholo {
3803751347eStholo struct timehands *th;
3813751347eStholo u_int gen;
3823751347eStholo
3833751347eStholo do {
3843751347eStholo th = timehands;
3853751347eStholo gen = th->th_generation;
386a701e5dfSbluhm membar_consumer();
38775b45b05Scheloha BINTIME_TO_TIMESPEC(&th->th_offset, tsp);
388a701e5dfSbluhm membar_consumer();
3893751347eStholo } while (gen == 0 || gen != th->th_generation);
3903751347eStholo }
3913751347eStholo
3923751347eStholo void
getmicrouptime(struct timeval * tvp)3933751347eStholo getmicrouptime(struct timeval *tvp)
3943751347eStholo {
3953751347eStholo struct timehands *th;
3963751347eStholo u_int gen;
3973751347eStholo
3983751347eStholo do {
3993751347eStholo th = timehands;
4003751347eStholo gen = th->th_generation;
401a701e5dfSbluhm membar_consumer();
40275b45b05Scheloha BINTIME_TO_TIMEVAL(&th->th_offset, tvp);
403a701e5dfSbluhm membar_consumer();
4043751347eStholo } while (gen == 0 || gen != th->th_generation);
4053751347eStholo }
4063751347eStholo
4073751347eStholo void
getnanotime(struct timespec * tsp)4083751347eStholo getnanotime(struct timespec *tsp)
4093751347eStholo {
4103751347eStholo struct timehands *th;
4113751347eStholo u_int gen;
4123751347eStholo
4133751347eStholo do {
4143751347eStholo th = timehands;
4153751347eStholo gen = th->th_generation;
416a701e5dfSbluhm membar_consumer();
4173751347eStholo *tsp = th->th_nanotime;
418a701e5dfSbluhm membar_consumer();
4193751347eStholo } while (gen == 0 || gen != th->th_generation);
4203751347eStholo }
4213751347eStholo
4223751347eStholo void
getmicrotime(struct timeval * tvp)4233751347eStholo getmicrotime(struct timeval *tvp)
4243751347eStholo {
4253751347eStholo struct timehands *th;
4263751347eStholo u_int gen;
4273751347eStholo
4283751347eStholo do {
4293751347eStholo th = timehands;
4303751347eStholo gen = th->th_generation;
431a701e5dfSbluhm membar_consumer();
4323751347eStholo *tvp = th->th_microtime;
433a701e5dfSbluhm membar_consumer();
4343751347eStholo } while (gen == 0 || gen != th->th_generation);
4353751347eStholo }
4363751347eStholo
4373751347eStholo /*
4383751347eStholo * Initialize a new timecounter and possibly use it.
4393751347eStholo */
4403751347eStholo void
tc_init(struct timecounter * tc)4413751347eStholo tc_init(struct timecounter *tc)
4423751347eStholo {
443875f2e32Scheloha u_int64_t tmp;
4445a8003fbSgrange u_int u;
4453751347eStholo
4463751347eStholo u = tc->tc_frequency / tc->tc_counter_mask;
4473751347eStholo /* XXX: We need some margin here, 10% is a guess */
4483751347eStholo u *= 11;
4493751347eStholo u /= 10;
4503751347eStholo if (tc->tc_quality >= 0) {
4513751347eStholo if (u > hz) {
4523751347eStholo tc->tc_quality = -2000;
453ead574e1Sart printf("Timecounter \"%s\" frequency %lu Hz",
454ead574e1Sart tc->tc_name, (unsigned long)tc->tc_frequency);
4553751347eStholo printf(" -- Insufficient hz, needs at least %u\n", u);
4563751347eStholo }
4573751347eStholo }
4583751347eStholo
459875f2e32Scheloha /* Determine the counter's precision. */
460875f2e32Scheloha for (tmp = 1; (tmp & tc->tc_counter_mask) == 0; tmp <<= 1)
461875f2e32Scheloha continue;
462875f2e32Scheloha tc->tc_precision = tmp;
463875f2e32Scheloha
4648334e679Scheloha SLIST_INSERT_HEAD(&tc_list, tc, tc_next);
4658334e679Scheloha
4663751347eStholo /*
4673751347eStholo * Never automatically use a timecounter with negative quality.
4683751347eStholo * Even though we run on the dummy counter, switching here may be
4691b6d31a4Sguenther * worse since this timecounter may not be monotonic.
4703751347eStholo */
4713751347eStholo if (tc->tc_quality < 0)
4723751347eStholo return;
4733751347eStholo if (tc->tc_quality < timecounter->tc_quality)
4743751347eStholo return;
4753751347eStholo if (tc->tc_quality == timecounter->tc_quality &&
4763751347eStholo tc->tc_frequency < timecounter->tc_frequency)
4773751347eStholo return;
4783751347eStholo (void)tc->tc_get_timecount(tc);
4799e9abf5bSjasper enqueue_randomness(tc->tc_get_timecount(tc));
4809d4264a7Sderaadt
4813751347eStholo timecounter = tc;
4823751347eStholo }
4833751347eStholo
48478156938Scheloha /*
48578156938Scheloha * Change the given timecounter's quality. If it is the active
48678156938Scheloha * counter and it is no longer the best counter, activate the
48778156938Scheloha * best counter.
48878156938Scheloha */
48978156938Scheloha void
tc_reset_quality(struct timecounter * tc,int quality)49078156938Scheloha tc_reset_quality(struct timecounter *tc, int quality)
49178156938Scheloha {
49278156938Scheloha struct timecounter *best = &dummy_timecounter, *tmp;
49378156938Scheloha
49478156938Scheloha if (tc == &dummy_timecounter)
49578156938Scheloha panic("%s: cannot change dummy counter quality", __func__);
49678156938Scheloha
49778156938Scheloha tc->tc_quality = quality;
49878156938Scheloha if (timecounter == tc) {
49978156938Scheloha SLIST_FOREACH(tmp, &tc_list, tc_next) {
50078156938Scheloha if (tmp->tc_quality < 0)
50178156938Scheloha continue;
50278156938Scheloha if (tmp->tc_quality < best->tc_quality)
50378156938Scheloha continue;
50478156938Scheloha if (tmp->tc_quality == best->tc_quality &&
50578156938Scheloha tmp->tc_frequency < best->tc_frequency)
50678156938Scheloha continue;
50778156938Scheloha best = tmp;
50878156938Scheloha }
50978156938Scheloha if (best != tc) {
51078156938Scheloha enqueue_randomness(best->tc_get_timecount(best));
51178156938Scheloha timecounter = best;
5124c0ab428Scheloha printf("timecounter: active counter changed: %s -> %s\n",
5134c0ab428Scheloha tc->tc_name, best->tc_name);
51478156938Scheloha }
51578156938Scheloha }
51678156938Scheloha }
51778156938Scheloha
5183751347eStholo /* Report the frequency of the current timecounter. */
5193751347eStholo u_int64_t
tc_getfrequency(void)5203751347eStholo tc_getfrequency(void)
5213751347eStholo {
5223751347eStholo return (timehands->th_counter->tc_frequency);
5233751347eStholo }
5243751347eStholo
525875f2e32Scheloha /* Report the precision of the current timecounter. */
526875f2e32Scheloha u_int64_t
tc_getprecision(void)527875f2e32Scheloha tc_getprecision(void)
528875f2e32Scheloha {
529875f2e32Scheloha return (timehands->th_counter->tc_precision);
530875f2e32Scheloha }
531875f2e32Scheloha
5323751347eStholo /*
5331b6d31a4Sguenther * Step our concept of UTC, aka the realtime clock.
5341b6d31a4Sguenther * This is done by modifying our estimate of when we booted.
535c54148e4Scheloha *
536c54148e4Scheloha * Any ongoing adjustment is meaningless after a clock jump,
537c54148e4Scheloha * so we zero adjtimedelta here as well.
5383751347eStholo */
5393751347eStholo void
tc_setrealtimeclock(const struct timespec * ts)54024421defSguenther tc_setrealtimeclock(const struct timespec *ts)
5413751347eStholo {
5421fb8cdb7Scheloha struct bintime boottime, old_utc, uptime, utc;
5431fb8cdb7Scheloha struct timespec tmp;
544c54148e4Scheloha int64_t zero = 0;
5453751347eStholo
5461fb8cdb7Scheloha TIMESPEC_TO_BINTIME(ts, &utc);
5471fb8cdb7Scheloha
548af3eeb45Scheloha rw_enter_write(&tc_lock);
549ceab5aefScheloha mtx_enter(&windup_mtx);
5503c2e3f4bScheloha
5511fb8cdb7Scheloha binuptime(&uptime);
5521fb8cdb7Scheloha bintimesub(&utc, &uptime, &boottime);
5531fb8cdb7Scheloha bintimeadd(&timehands->th_boottime, &uptime, &old_utc);
5543751347eStholo /* XXX fiddle all the little crinkly bits around the fiords... */
5551fb8cdb7Scheloha tc_windup(&boottime, NULL, &zero);
5561fb8cdb7Scheloha
557ceab5aefScheloha mtx_leave(&windup_mtx);
558af3eeb45Scheloha rw_exit_write(&tc_lock);
5591d8de610Scheloha
5601d8de610Scheloha enqueue_randomness(ts->tv_sec);
5611d8de610Scheloha
5623751347eStholo if (timestepwarnings) {
5631fb8cdb7Scheloha BINTIME_TO_TIMESPEC(&old_utc, &tmp);
5647952239bSguenther log(LOG_INFO, "Time stepped from %lld.%09ld to %lld.%09ld\n",
5651fb8cdb7Scheloha (long long)tmp.tv_sec, tmp.tv_nsec,
5667952239bSguenther (long long)ts->tv_sec, ts->tv_nsec);
5673751347eStholo }
5683751347eStholo }
5693751347eStholo
5703751347eStholo /*
5711b6d31a4Sguenther * Step the monotonic and realtime clocks, triggering any timeouts that
5721b6d31a4Sguenther * should have occurred across the interval.
5731b6d31a4Sguenther */
5741b6d31a4Sguenther void
tc_setclock(const struct timespec * ts)57524421defSguenther tc_setclock(const struct timespec *ts)
5761b6d31a4Sguenther {
57797c55bccScheloha struct bintime new_naptime, old_naptime, uptime, utc;
578fa5a0c50Scheloha static int first = 1;
5791b6d31a4Sguenther #ifndef SMALL_KERNEL
5806e581dd8Sderaadt struct bintime elapsed;
5811b6d31a4Sguenther long long adj_ticks;
5821b6d31a4Sguenther #endif
5831b6d31a4Sguenther
5841b6d31a4Sguenther /*
5851b6d31a4Sguenther * When we're called for the first time, during boot when
586fa5a0c50Scheloha * the root partition is mounted, we need to set boottime.
5871b6d31a4Sguenther */
588fa5a0c50Scheloha if (first) {
5891b6d31a4Sguenther tc_setrealtimeclock(ts);
590fa5a0c50Scheloha first = 0;
5911b6d31a4Sguenther return;
5921b6d31a4Sguenther }
5931b6d31a4Sguenther
5949e9abf5bSjasper enqueue_randomness(ts->tv_sec);
5951b6d31a4Sguenther
5961fb8cdb7Scheloha TIMESPEC_TO_BINTIME(ts, &utc);
5971fb8cdb7Scheloha
598ceab5aefScheloha mtx_enter(&windup_mtx);
5991fb8cdb7Scheloha
6001fb8cdb7Scheloha bintimesub(&utc, &timehands->th_boottime, &uptime);
60187ba7848Scheloha old_naptime = timehands->th_naptime;
602e98df54aScheloha /* XXX fiddle all the little crinkly bits around the fiords... */
6031fb8cdb7Scheloha tc_windup(NULL, &uptime, NULL);
60497c55bccScheloha new_naptime = timehands->th_naptime;
6051fb8cdb7Scheloha
606ceab5aefScheloha mtx_leave(&windup_mtx);
607e98df54aScheloha
6081b6d31a4Sguenther #ifndef SMALL_KERNEL
6091b6d31a4Sguenther /* convert the bintime to ticks */
61097c55bccScheloha bintimesub(&new_naptime, &old_naptime, &elapsed);
6112f582782Scheloha adj_ticks = BINTIME_TO_NSEC(&elapsed) / tick_nsec;
6121b6d31a4Sguenther if (adj_ticks > 0) {
6131b6d31a4Sguenther if (adj_ticks > INT_MAX)
6141b6d31a4Sguenther adj_ticks = INT_MAX;
615a40acd8aScheloha timeout_adjust_ticks(adj_ticks);
6161b6d31a4Sguenther }
6171b6d31a4Sguenther #endif
6181b6d31a4Sguenther }
6191b6d31a4Sguenther
620d82e6535Spirofti void
tc_update_timekeep(void)621d82e6535Spirofti tc_update_timekeep(void)
622d82e6535Spirofti {
623d82e6535Spirofti static struct timecounter *last_tc = NULL;
624d82e6535Spirofti struct timehands *th;
625d82e6535Spirofti
62604cecb01Scheloha MUTEX_ASSERT_LOCKED(&windup_mtx);
62704cecb01Scheloha
628d82e6535Spirofti if (timekeep == NULL)
629d82e6535Spirofti return;
630d82e6535Spirofti
631d82e6535Spirofti th = timehands;
632d82e6535Spirofti timekeep->tk_generation = 0;
633d82e6535Spirofti membar_producer();
634d82e6535Spirofti timekeep->tk_scale = th->th_scale;
635d82e6535Spirofti timekeep->tk_offset_count = th->th_offset_count;
636d82e6535Spirofti timekeep->tk_offset = th->th_offset;
637d82e6535Spirofti timekeep->tk_naptime = th->th_naptime;
638d82e6535Spirofti timekeep->tk_boottime = th->th_boottime;
639d82e6535Spirofti if (last_tc != th->th_counter) {
640d82e6535Spirofti timekeep->tk_counter_mask = th->th_counter->tc_counter_mask;
641d82e6535Spirofti timekeep->tk_user = th->th_counter->tc_user;
642d82e6535Spirofti last_tc = th->th_counter;
643d82e6535Spirofti }
644d82e6535Spirofti membar_producer();
645d82e6535Spirofti timekeep->tk_generation = th->th_generation;
646d82e6535Spirofti
647d82e6535Spirofti return;
648d82e6535Spirofti }
649d82e6535Spirofti
6501b6d31a4Sguenther /*
6513751347eStholo * Initialize the next struct timehands in the ring and make
6523751347eStholo * it the active timehands. Along the way we might switch to a different
6533751347eStholo * timecounter and/or do seconds processing in NTP. Slightly magic.
6543751347eStholo */
6555a8003fbSgrange void
tc_windup(struct bintime * new_boottime,struct bintime * new_offset,int64_t * new_adjtimedelta)656c54148e4Scheloha tc_windup(struct bintime *new_boottime, struct bintime *new_offset,
657c54148e4Scheloha int64_t *new_adjtimedelta)
6583751347eStholo {
6593751347eStholo struct bintime bt;
660827d5adbScheloha struct timecounter *active_tc;
6613751347eStholo struct timehands *th, *tho;
6623751347eStholo u_int64_t scale;
6633751347eStholo u_int delta, ncount, ogen;
6643751347eStholo
665af3eeb45Scheloha if (new_boottime != NULL || new_adjtimedelta != NULL)
666af3eeb45Scheloha rw_assert_wrlock(&tc_lock);
667ceab5aefScheloha MUTEX_ASSERT_LOCKED(&windup_mtx);
6681d8de610Scheloha
669827d5adbScheloha active_tc = timecounter;
670827d5adbScheloha
6713751347eStholo /*
6723751347eStholo * Make the next timehands a copy of the current one, but do not
6733751347eStholo * overwrite the generation or next pointer. While we update
6743751347eStholo * the contents, the generation must be zero.
6753751347eStholo */
6763751347eStholo tho = timehands;
67763cc33c4Sgkoehler ogen = tho->th_generation;
6783751347eStholo th = tho->th_next;
6793751347eStholo th->th_generation = 0;
680a701e5dfSbluhm membar_producer();
6812955d5bcStedu memcpy(th, tho, offsetof(struct timehands, th_generation));
6823751347eStholo
6833751347eStholo /*
6843751347eStholo * Capture a timecounter delta on the current timecounter and if
6853751347eStholo * changing timecounters, a counter value from the new timecounter.
6863751347eStholo * Update the offset fields accordingly.
6873751347eStholo */
6883751347eStholo delta = tc_delta(th);
689827d5adbScheloha if (th->th_counter != active_tc)
690827d5adbScheloha ncount = active_tc->tc_get_timecount(active_tc);
6913751347eStholo else
6923751347eStholo ncount = 0;
6933751347eStholo th->th_offset_count += delta;
6943751347eStholo th->th_offset_count &= th->th_counter->tc_counter_mask;
69583dc7839Scheloha TIMECOUNT_TO_BINTIME(delta, th->th_scale, &bt);
69683dc7839Scheloha bintimeadd(&th->th_offset, &bt, &th->th_offset);
6973751347eStholo
69887ba7848Scheloha /*
69987ba7848Scheloha * Ignore new offsets that predate the current offset.
70087ba7848Scheloha * If changing the offset, first increase the naptime
70187ba7848Scheloha * accordingly.
70287ba7848Scheloha */
70387ba7848Scheloha if (new_offset != NULL && bintimecmp(&th->th_offset, new_offset, <)) {
70487ba7848Scheloha bintimesub(new_offset, &th->th_offset, &bt);
70587ba7848Scheloha bintimeadd(&th->th_naptime, &bt, &th->th_naptime);
70697c55bccScheloha naptime = th->th_naptime.sec;
70787ba7848Scheloha th->th_offset = *new_offset;
70887ba7848Scheloha }
70987ba7848Scheloha
7103751347eStholo /*
711c54148e4Scheloha * If changing the boot time or clock adjustment, do so before
712c54148e4Scheloha * NTP processing.
7137c21e1f3Scheloha */
714c54148e4Scheloha if (new_boottime != NULL)
7157c21e1f3Scheloha th->th_boottime = *new_boottime;
716fecf25f8Scheloha if (new_adjtimedelta != NULL) {
717c54148e4Scheloha th->th_adjtimedelta = *new_adjtimedelta;
718fecf25f8Scheloha /* Reset the NTP update period. */
719fecf25f8Scheloha bintimesub(&th->th_offset, &th->th_naptime,
720fecf25f8Scheloha &th->th_next_ntp_update);
721fecf25f8Scheloha }
7227c21e1f3Scheloha
7237c21e1f3Scheloha /*
724fecf25f8Scheloha * Deal with NTP second processing. The while-loop normally
7253751347eStholo * iterates at most once, but in extreme situations it might
726fecf25f8Scheloha * keep NTP sane if tc_windup() is not run for several seconds.
7273751347eStholo */
728fecf25f8Scheloha bintimesub(&th->th_offset, &th->th_naptime, &bt);
729fecf25f8Scheloha while (bintimecmp(&th->th_next_ntp_update, &bt, <=)) {
730c54148e4Scheloha ntp_update_second(th);
731fecf25f8Scheloha th->th_next_ntp_update.sec++;
732fecf25f8Scheloha }
7333751347eStholo
7343751347eStholo /* Update the UTC timestamps used by the get*() functions. */
735fecf25f8Scheloha bintimeadd(&th->th_boottime, &th->th_offset, &bt);
73675b45b05Scheloha BINTIME_TO_TIMEVAL(&bt, &th->th_microtime);
73775b45b05Scheloha BINTIME_TO_TIMESPEC(&bt, &th->th_nanotime);
7383751347eStholo
7393751347eStholo /* Now is a good time to change timecounters. */
740827d5adbScheloha if (th->th_counter != active_tc) {
741827d5adbScheloha th->th_counter = active_tc;
7423751347eStholo th->th_offset_count = ncount;
7433751347eStholo }
7443751347eStholo
7453751347eStholo /*-
7463751347eStholo * Recalculate the scaling factor. We want the number of 1/2^64
7473751347eStholo * fractions of a second per period of the hardware counter, taking
7483751347eStholo * into account the th_adjustment factor which the NTP PLL/adjtime(2)
7493751347eStholo * processing provides us with.
7503751347eStholo *
7513751347eStholo * The th_adjustment is nanoseconds per second with 32 bit binary
7523751347eStholo * fraction and we want 64 bit binary fraction of second:
7533751347eStholo *
7543751347eStholo * x = a * 2^32 / 10^9 = a * 4.294967296
7553751347eStholo *
7563751347eStholo * The range of th_adjustment is +/- 5000PPM so inside a 64bit int
7573751347eStholo * we can only multiply by about 850 without overflowing, but that
7583751347eStholo * leaves suitably precise fractions for multiply before divide.
7593751347eStholo *
7603751347eStholo * Divide before multiply with a fraction of 2199/512 results in a
7613751347eStholo * systematic undercompensation of 10PPM of th_adjustment. On a
7623751347eStholo * 5000PPM adjustment this is a 0.05PPM error. This is acceptable.
7633751347eStholo *
7643751347eStholo * We happily sacrifice the lowest of the 64 bits of our result
7653751347eStholo * to the goddess of code clarity.
7663751347eStholo *
7673751347eStholo */
7683751347eStholo scale = (u_int64_t)1 << 63;
7699098a9c7Scheloha scale += \
7709098a9c7Scheloha ((th->th_adjustment + th->th_counter->tc_freq_adj) / 1024) * 2199;
7713751347eStholo scale /= th->th_counter->tc_frequency;
7723751347eStholo th->th_scale = scale * 2;
7733751347eStholo
7743751347eStholo /*
7753751347eStholo * Now that the struct timehands is again consistent, set the new
7763751347eStholo * generation number, making sure to not make it zero.
7773751347eStholo */
7783751347eStholo if (++ogen == 0)
7793751347eStholo ogen = 1;
780a701e5dfSbluhm membar_producer();
7813751347eStholo th->th_generation = ogen;
7823751347eStholo
7833751347eStholo /* Go live with the new struct timehands. */
7843751347eStholo time_second = th->th_microtime.tv_sec;
7853751347eStholo time_uptime = th->th_offset.sec;
786a701e5dfSbluhm membar_producer();
7873751347eStholo timehands = th;
788d82e6535Spirofti
789d82e6535Spirofti tc_update_timekeep();
7903751347eStholo }
7913751347eStholo
7923751347eStholo /* Report or change the active timecounter hardware. */
7933751347eStholo int
sysctl_tc_hardware(void * oldp,size_t * oldlenp,void * newp,size_t newlen)7943751347eStholo sysctl_tc_hardware(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
7953751347eStholo {
7963751347eStholo char newname[32];
7973751347eStholo struct timecounter *newtc, *tc;
7983751347eStholo int error;
7993751347eStholo
8003751347eStholo tc = timecounter;
8013751347eStholo strlcpy(newname, tc->tc_name, sizeof(newname));
8023751347eStholo
8033751347eStholo error = sysctl_string(oldp, oldlenp, newp, newlen, newname, sizeof(newname));
8043751347eStholo if (error != 0 || strcmp(newname, tc->tc_name) == 0)
8053751347eStholo return (error);
8068334e679Scheloha SLIST_FOREACH(newtc, &tc_list, tc_next) {
8073751347eStholo if (strcmp(newname, newtc->tc_name) != 0)
8083751347eStholo continue;
8093751347eStholo
8103751347eStholo /* Warm up new timecounter. */
8113751347eStholo (void)newtc->tc_get_timecount(newtc);
8123751347eStholo (void)newtc->tc_get_timecount(newtc);
8133751347eStholo
814af3eeb45Scheloha rw_enter_write(&tc_lock);
8153751347eStholo timecounter = newtc;
816af3eeb45Scheloha rw_exit_write(&tc_lock);
817af3eeb45Scheloha
8183751347eStholo return (0);
8193751347eStholo }
8203751347eStholo return (EINVAL);
8213751347eStholo }
8223751347eStholo
8233751347eStholo /* Report or change the active timecounter hardware. */
8243751347eStholo int
sysctl_tc_choice(void * oldp,size_t * oldlenp,void * newp,size_t newlen)8253751347eStholo sysctl_tc_choice(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
8263751347eStholo {
8273751347eStholo char buf[32], *spc, *choices;
8283751347eStholo struct timecounter *tc;
8290816f330Sderaadt int error, maxlen;
8303751347eStholo
8318334e679Scheloha if (SLIST_EMPTY(&tc_list))
83208e05d41Scheloha return (sysctl_rdstring(oldp, oldlenp, newp, ""));
83308e05d41Scheloha
8343751347eStholo spc = "";
8353751347eStholo maxlen = 0;
8368334e679Scheloha SLIST_FOREACH(tc, &tc_list, tc_next)
8373751347eStholo maxlen += sizeof(buf);
8383751347eStholo choices = malloc(maxlen, M_TEMP, M_WAITOK);
8393751347eStholo *choices = '\0';
8408334e679Scheloha SLIST_FOREACH(tc, &tc_list, tc_next) {
8410816f330Sderaadt snprintf(buf, sizeof(buf), "%s%s(%d)",
8423751347eStholo spc, tc->tc_name, tc->tc_quality);
8433751347eStholo spc = " ";
8443751347eStholo strlcat(choices, buf, maxlen);
8453751347eStholo }
8463751347eStholo error = sysctl_rdstring(oldp, oldlenp, newp, choices);
847fc62de09Stedu free(choices, M_TEMP, maxlen);
8483751347eStholo return (error);
8493751347eStholo }
8503751347eStholo
8513751347eStholo /*
8523751347eStholo * Timecounters need to be updated every so often to prevent the hardware
8533751347eStholo * counter from overflowing. Updating also recalculates the cached values
8543751347eStholo * used by the get*() family of functions, so their precision depends on
8553751347eStholo * the update frequency.
8563751347eStholo */
8573751347eStholo static int tc_tick;
8583751347eStholo
8593751347eStholo void
tc_ticktock(void)8603751347eStholo tc_ticktock(void)
8613751347eStholo {
8623751347eStholo static int count;
8633751347eStholo
8643751347eStholo if (++count < tc_tick)
8653751347eStholo return;
866ceab5aefScheloha if (!mtx_enter_try(&windup_mtx))
8671d8de610Scheloha return;
8683751347eStholo count = 0;
869c54148e4Scheloha tc_windup(NULL, NULL, NULL);
870ceab5aefScheloha mtx_leave(&windup_mtx);
8713751347eStholo }
8723751347eStholo
8733751347eStholo void
inittimecounter(void)8743751347eStholo inittimecounter(void)
8753751347eStholo {
8764c33e0a9Sderaadt #ifdef DEBUG
8773751347eStholo u_int p;
8784c33e0a9Sderaadt #endif
8793751347eStholo
8803751347eStholo /*
8813751347eStholo * Set the initial timeout to
8823751347eStholo * max(1, <approx. number of hardclock ticks in a millisecond>).
8833751347eStholo * People should probably not use the sysctl to set the timeout
884a3c911baSschwarze * to smaller than its initial value, since that value is the
8853751347eStholo * smallest reasonable one. If they want better timestamps they
8863751347eStholo * should use the non-"get"* functions.
8873751347eStholo */
8883751347eStholo if (hz > 1000)
8893751347eStholo tc_tick = (hz + 500) / 1000;
8903751347eStholo else
8913751347eStholo tc_tick = 1;
892ead574e1Sart #ifdef DEBUG
8934c33e0a9Sderaadt p = (tc_tick * 1000000) / hz;
8943751347eStholo printf("Timecounters tick every %d.%03u msec\n", p / 1000, p % 1000);
895ead574e1Sart #endif
8963751347eStholo
8973751347eStholo /* warm up new timecounter (again) and get rolling. */
8983751347eStholo (void)timecounter->tc_get_timecount(timecounter);
8993751347eStholo (void)timecounter->tc_get_timecount(timecounter);
9003751347eStholo }
9013751347eStholo
902e0324b5eSgnezdo const struct sysctl_bounded_args tc_vars[] = {
903b32486e3Sbluhm { KERN_TIMECOUNTER_TICK, &tc_tick, SYSCTL_INT_READONLY },
904e0324b5eSgnezdo { KERN_TIMECOUNTER_TIMESTEPWARNINGS, ×tepwarnings, 0, 1 },
905e0324b5eSgnezdo };
906e0324b5eSgnezdo
9073751347eStholo /*
9083751347eStholo * Return timecounter-related information.
9093751347eStholo */
9103751347eStholo int
sysctl_tc(int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen)9113751347eStholo sysctl_tc(int *name, u_int namelen, void *oldp, size_t *oldlenp,
9123751347eStholo void *newp, size_t newlen)
9133751347eStholo {
9143751347eStholo if (namelen != 1)
9153751347eStholo return (ENOTDIR);
9163751347eStholo
9173751347eStholo switch (name[0]) {
9183751347eStholo case KERN_TIMECOUNTER_HARDWARE:
9193751347eStholo return (sysctl_tc_hardware(oldp, oldlenp, newp, newlen));
9203751347eStholo case KERN_TIMECOUNTER_CHOICE:
9213751347eStholo return (sysctl_tc_choice(oldp, oldlenp, newp, newlen));
9223751347eStholo default:
923e0324b5eSgnezdo return (sysctl_bounded_arr(tc_vars, nitems(tc_vars), name,
924e0324b5eSgnezdo namelen, oldp, oldlenp, newp, newlen));
9253751347eStholo }
9263751347eStholo /* NOTREACHED */
9273751347eStholo }
9283751347eStholo
929c54148e4Scheloha /*
9309098a9c7Scheloha * Skew the timehands according to any adjtime(2) adjustment.
931c54148e4Scheloha */
9323751347eStholo void
ntp_update_second(struct timehands * th)933c54148e4Scheloha ntp_update_second(struct timehands *th)
9343751347eStholo {
935a1745eadSkettenis int64_t adj;
9363751347eStholo
937c54148e4Scheloha MUTEX_ASSERT_LOCKED(&windup_mtx);
938c54148e4Scheloha
939c54148e4Scheloha if (th->th_adjtimedelta > 0)
940c54148e4Scheloha adj = MIN(5000, th->th_adjtimedelta);
941a1745eadSkettenis else
942c54148e4Scheloha adj = MAX(-5000, th->th_adjtimedelta);
943c54148e4Scheloha th->th_adjtimedelta -= adj;
944c54148e4Scheloha th->th_adjustment = (adj * 1000) << 32;
94517f73788Sotto }
94617f73788Sotto
947af3eeb45Scheloha void
tc_adjfreq(int64_t * old,int64_t * new)94817f73788Sotto tc_adjfreq(int64_t *old, int64_t *new)
94917f73788Sotto {
95017f73788Sotto if (old != NULL) {
951af3eeb45Scheloha rw_assert_anylock(&tc_lock);
95217f73788Sotto *old = timecounter->tc_freq_adj;
95317f73788Sotto }
95417f73788Sotto if (new != NULL) {
955af3eeb45Scheloha rw_assert_wrlock(&tc_lock);
956af3eeb45Scheloha mtx_enter(&windup_mtx);
95717f73788Sotto timecounter->tc_freq_adj = *new;
958af3eeb45Scheloha tc_windup(NULL, NULL, NULL);
959af3eeb45Scheloha mtx_leave(&windup_mtx);
96017f73788Sotto }
9613751347eStholo }
9623c2e3f4bScheloha
9633c2e3f4bScheloha void
tc_adjtime(int64_t * old,int64_t * new)9643c2e3f4bScheloha tc_adjtime(int64_t *old, int64_t *new)
9653c2e3f4bScheloha {
966c54148e4Scheloha struct timehands *th;
967c54148e4Scheloha u_int gen;
968c54148e4Scheloha
969c54148e4Scheloha if (old != NULL) {
970c54148e4Scheloha do {
971c54148e4Scheloha th = timehands;
972c54148e4Scheloha gen = th->th_generation;
973c54148e4Scheloha membar_consumer();
974c54148e4Scheloha *old = th->th_adjtimedelta;
975c54148e4Scheloha membar_consumer();
976c54148e4Scheloha } while (gen == 0 || gen != th->th_generation);
977c54148e4Scheloha }
978c54148e4Scheloha if (new != NULL) {
979af3eeb45Scheloha rw_assert_wrlock(&tc_lock);
980c54148e4Scheloha mtx_enter(&windup_mtx);
981c54148e4Scheloha tc_windup(NULL, NULL, new);
982c54148e4Scheloha mtx_leave(&windup_mtx);
983c54148e4Scheloha }
9843c2e3f4bScheloha }
985