| File: | kern/kern_tc.c |
| Warning: | line 944, column 35 The result of the left shift is undefined because the left operand is negative |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: kern_tc.c,v 1.82 2023/02/04 19:19:36 cheloha Exp $ */ | ||||
| 2 | |||||
| 3 | /* | ||||
| 4 | * Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org> | ||||
| 5 | * | ||||
| 6 | * Permission to use, copy, modify, and distribute this software for any | ||||
| 7 | * purpose with or without fee is hereby granted, provided that the above | ||||
| 8 | * copyright notice and this permission notice appear in all copies. | ||||
| 9 | * | ||||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
| 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
| 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
| 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
| 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
| 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
| 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
| 17 | */ | ||||
| 18 | |||||
| 19 | /* | ||||
| 20 | * If we meet some day, and you think this stuff is worth it, you | ||||
| 21 | * can buy me a beer in return. Poul-Henning Kamp | ||||
| 22 | */ | ||||
| 23 | |||||
| 24 | #include <sys/param.h> | ||||
| 25 | #include <sys/atomic.h> | ||||
| 26 | #include <sys/kernel.h> | ||||
| 27 | #include <sys/mutex.h> | ||||
| 28 | #include <sys/rwlock.h> | ||||
| 29 | #include <sys/stdint.h> | ||||
| 30 | #include <sys/timeout.h> | ||||
| 31 | #include <sys/sysctl.h> | ||||
| 32 | #include <sys/syslog.h> | ||||
| 33 | #include <sys/systm.h> | ||||
| 34 | #include <sys/timetc.h> | ||||
| 35 | #include <sys/queue.h> | ||||
| 36 | #include <sys/malloc.h> | ||||
| 37 | |||||
| 38 | u_int dummy_get_timecount(struct timecounter *); | ||||
| 39 | |||||
| 40 | int sysctl_tc_hardware(void *, size_t *, void *, size_t); | ||||
| 41 | int sysctl_tc_choice(void *, size_t *, void *, size_t); | ||||
| 42 | |||||
| 43 | /* | ||||
| 44 | * Implement a dummy timecounter which we can use until we get a real one | ||||
| 45 | * in the air. This allows the console and other early stuff to use | ||||
| 46 | * time services. | ||||
| 47 | */ | ||||
| 48 | |||||
| 49 | u_int | ||||
| 50 | dummy_get_timecount(struct timecounter *tc) | ||||
| 51 | { | ||||
| 52 | static u_int now; | ||||
| 53 | |||||
| 54 | return atomic_inc_int_nv(&now)_atomic_add_int_nv((&now), 1); | ||||
| 55 | } | ||||
| 56 | |||||
| 57 | static struct timecounter dummy_timecounter = { | ||||
| 58 | .tc_get_timecount = dummy_get_timecount, | ||||
| 59 | .tc_counter_mask = ~0u, | ||||
| 60 | .tc_frequency = 1000000, | ||||
| 61 | .tc_name = "dummy", | ||||
| 62 | .tc_quality = -1000000, | ||||
| 63 | .tc_priv = NULL((void *)0), | ||||
| 64 | .tc_user = 0, | ||||
| 65 | }; | ||||
| 66 | |||||
| 67 | /* | ||||
| 68 | * Locks used to protect struct members, global variables in this file: | ||||
| 69 | * I immutable after initialization | ||||
| 70 | * T tc_lock | ||||
| 71 | * W windup_mtx | ||||
| 72 | */ | ||||
| 73 | |||||
| 74 | struct timehands { | ||||
| 75 | /* These fields must be initialized by the driver. */ | ||||
| 76 | struct timecounter *th_counter; /* [W] */ | ||||
| 77 | int64_t th_adjtimedelta; /* [T,W] */ | ||||
| 78 | struct bintime th_next_ntp_update; /* [T,W] */ | ||||
| 79 | int64_t th_adjustment; /* [W] */ | ||||
| 80 | u_int64_t th_scale; /* [W] */ | ||||
| 81 | u_int th_offset_count; /* [W] */ | ||||
| 82 | struct bintime th_boottime; /* [T,W] */ | ||||
| 83 | struct bintime th_offset; /* [W] */ | ||||
| 84 | struct bintime th_naptime; /* [W] */ | ||||
| 85 | struct timeval th_microtime; /* [W] */ | ||||
| 86 | struct timespec th_nanotime; /* [W] */ | ||||
| 87 | /* Fields not to be copied in tc_windup start with th_generation. */ | ||||
| 88 | volatile u_int th_generation; /* [W] */ | ||||
| 89 | struct timehands *th_next; /* [I] */ | ||||
| 90 | }; | ||||
| 91 | |||||
| 92 | static struct timehands th0; | ||||
| 93 | static struct timehands th1 = { | ||||
| 94 | .th_next = &th0 | ||||
| 95 | }; | ||||
| 96 | static struct timehands th0 = { | ||||
| 97 | .th_counter = &dummy_timecounter, | ||||
| 98 | .th_scale = UINT64_MAX0xffffffffffffffffULL / 1000000, | ||||
| 99 | .th_offset = { .sec = 1, .frac = 0 }, | ||||
| 100 | .th_generation = 1, | ||||
| 101 | .th_next = &th1 | ||||
| 102 | }; | ||||
| 103 | |||||
| 104 | struct rwlock tc_lock = RWLOCK_INITIALIZER("tc_lock"){ 0, "tc_lock" }; | ||||
| 105 | |||||
| 106 | /* | ||||
| 107 | * tc_windup() must be called before leaving this mutex. | ||||
| 108 | */ | ||||
| 109 | struct mutex windup_mtx = MUTEX_INITIALIZER(IPL_CLOCK){ ((void *)0), ((((0xc)) > 0x0 && ((0xc)) < 0x9 ) ? 0x9 : ((0xc))), 0x0 }; | ||||
| 110 | |||||
| 111 | static struct timehands *volatile timehands = &th0; /* [W] */ | ||||
| 112 | struct timecounter *timecounter = &dummy_timecounter; /* [T] */ | ||||
| 113 | static SLIST_HEAD(, timecounter)struct { struct timecounter *slh_first; } tc_list = SLIST_HEAD_INITIALIZER(tc_list){ ((void *)0) }; | ||||
| 114 | |||||
| 115 | /* | ||||
| 116 | * These are updated from tc_windup(). They are useful when | ||||
| 117 | * examining kernel core dumps. | ||||
| 118 | */ | ||||
| 119 | volatile time_t naptime = 0; | ||||
| 120 | volatile time_t time_second = 1; | ||||
| 121 | volatile time_t time_uptime = 0; | ||||
| 122 | |||||
| 123 | static int timestepwarnings; | ||||
| 124 | |||||
| 125 | void ntp_update_second(struct timehands *); | ||||
| 126 | void tc_windup(struct bintime *, struct bintime *, int64_t *); | ||||
| 127 | |||||
| 128 | /* | ||||
| 129 | * Return the difference between the timehands' counter value now and what | ||||
| 130 | * was when we copied it to the timehands' offset_count. | ||||
| 131 | */ | ||||
| 132 | static __inline u_int | ||||
| 133 | tc_delta(struct timehands *th) | ||||
| 134 | { | ||||
| 135 | struct timecounter *tc; | ||||
| 136 | |||||
| 137 | tc = th->th_counter; | ||||
| 138 | return ((tc->tc_get_timecount(tc) - th->th_offset_count) & | ||||
| 139 | tc->tc_counter_mask); | ||||
| 140 | } | ||||
| 141 | |||||
| 142 | /* | ||||
| 143 | * Functions for reading the time. We have to loop until we are sure that | ||||
| 144 | * the timehands that we operated on was not updated under our feet. See | ||||
| 145 | * the comment in <sys/time.h> for a description of these functions. | ||||
| 146 | */ | ||||
| 147 | |||||
| 148 | void | ||||
| 149 | binboottime(struct bintime *bt) | ||||
| 150 | { | ||||
| 151 | struct timehands *th; | ||||
| 152 | u_int gen; | ||||
| 153 | |||||
| 154 | do { | ||||
| 155 | th = timehands; | ||||
| 156 | gen = th->th_generation; | ||||
| 157 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 158 | *bt = th->th_boottime; | ||||
| 159 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 160 | } while (gen == 0 || gen != th->th_generation); | ||||
| 161 | } | ||||
| 162 | |||||
| 163 | void | ||||
| 164 | microboottime(struct timeval *tvp) | ||||
| 165 | { | ||||
| 166 | struct bintime bt; | ||||
| 167 | |||||
| 168 | binboottime(&bt); | ||||
| 169 | BINTIME_TO_TIMEVAL(&bt, tvp); | ||||
| 170 | } | ||||
| 171 | |||||
| 172 | void | ||||
| 173 | nanoboottime(struct timespec *tsp) | ||||
| 174 | { | ||||
| 175 | struct bintime bt; | ||||
| 176 | |||||
| 177 | binboottime(&bt); | ||||
| 178 | BINTIME_TO_TIMESPEC(&bt, tsp); | ||||
| 179 | } | ||||
| 180 | |||||
| 181 | void | ||||
| 182 | binuptime(struct bintime *bt) | ||||
| 183 | { | ||||
| 184 | struct timehands *th; | ||||
| 185 | u_int gen; | ||||
| 186 | |||||
| 187 | do { | ||||
| 188 | th = timehands; | ||||
| 189 | gen = th->th_generation; | ||||
| 190 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 191 | TIMECOUNT_TO_BINTIME(tc_delta(th), th->th_scale, bt); | ||||
| 192 | bintimeadd(bt, &th->th_offset, bt); | ||||
| 193 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 194 | } while (gen == 0 || gen != th->th_generation); | ||||
| 195 | } | ||||
| 196 | |||||
| 197 | void | ||||
| 198 | getbinuptime(struct bintime *bt) | ||||
| 199 | { | ||||
| 200 | struct timehands *th; | ||||
| 201 | u_int gen; | ||||
| 202 | |||||
| 203 | do { | ||||
| 204 | th = timehands; | ||||
| 205 | gen = th->th_generation; | ||||
| 206 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 207 | *bt = th->th_offset; | ||||
| 208 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 209 | } while (gen == 0 || gen != th->th_generation); | ||||
| 210 | } | ||||
| 211 | |||||
| 212 | void | ||||
| 213 | nanouptime(struct timespec *tsp) | ||||
| 214 | { | ||||
| 215 | struct bintime bt; | ||||
| 216 | |||||
| 217 | binuptime(&bt); | ||||
| 218 | BINTIME_TO_TIMESPEC(&bt, tsp); | ||||
| 219 | } | ||||
| 220 | |||||
| 221 | void | ||||
| 222 | microuptime(struct timeval *tvp) | ||||
| 223 | { | ||||
| 224 | struct bintime bt; | ||||
| 225 | |||||
| 226 | binuptime(&bt); | ||||
| 227 | BINTIME_TO_TIMEVAL(&bt, tvp); | ||||
| 228 | } | ||||
| 229 | |||||
| 230 | time_t | ||||
| 231 | getuptime(void) | ||||
| 232 | { | ||||
| 233 | #if defined(__LP64__1) | ||||
| 234 | return time_uptime; /* atomic */ | ||||
| 235 | #else | ||||
| 236 | time_t now; | ||||
| 237 | struct timehands *th; | ||||
| 238 | u_int gen; | ||||
| 239 | |||||
| 240 | do { | ||||
| 241 | th = timehands; | ||||
| 242 | gen = th->th_generation; | ||||
| 243 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 244 | now = th->th_offset.sec; | ||||
| 245 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 246 | } while (gen == 0 || gen != th->th_generation); | ||||
| 247 | |||||
| 248 | return now; | ||||
| 249 | #endif | ||||
| 250 | } | ||||
| 251 | |||||
| 252 | uint64_t | ||||
| 253 | nsecuptime(void) | ||||
| 254 | { | ||||
| 255 | struct bintime bt; | ||||
| 256 | |||||
| 257 | binuptime(&bt); | ||||
| 258 | return BINTIME_TO_NSEC(&bt); | ||||
| 259 | } | ||||
| 260 | |||||
| 261 | uint64_t | ||||
| 262 | getnsecuptime(void) | ||||
| 263 | { | ||||
| 264 | struct bintime bt; | ||||
| 265 | |||||
| 266 | getbinuptime(&bt); | ||||
| 267 | return BINTIME_TO_NSEC(&bt); | ||||
| 268 | } | ||||
| 269 | |||||
| 270 | void | ||||
| 271 | binruntime(struct bintime *bt) | ||||
| 272 | { | ||||
| 273 | struct timehands *th; | ||||
| 274 | u_int gen; | ||||
| 275 | |||||
| 276 | do { | ||||
| 277 | th = timehands; | ||||
| 278 | gen = th->th_generation; | ||||
| 279 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 280 | TIMECOUNT_TO_BINTIME(tc_delta(th), th->th_scale, bt); | ||||
| 281 | bintimeadd(bt, &th->th_offset, bt); | ||||
| 282 | bintimesub(bt, &th->th_naptime, bt); | ||||
| 283 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 284 | } while (gen == 0 || gen != th->th_generation); | ||||
| 285 | } | ||||
| 286 | |||||
| 287 | void | ||||
| 288 | nanoruntime(struct timespec *ts) | ||||
| 289 | { | ||||
| 290 | struct bintime bt; | ||||
| 291 | |||||
| 292 | binruntime(&bt); | ||||
| 293 | BINTIME_TO_TIMESPEC(&bt, ts); | ||||
| 294 | } | ||||
| 295 | |||||
| 296 | void | ||||
| 297 | getbinruntime(struct bintime *bt) | ||||
| 298 | { | ||||
| 299 | struct timehands *th; | ||||
| 300 | u_int gen; | ||||
| 301 | |||||
| 302 | do { | ||||
| 303 | th = timehands; | ||||
| 304 | gen = th->th_generation; | ||||
| 305 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 306 | bintimesub(&th->th_offset, &th->th_naptime, bt); | ||||
| 307 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 308 | } while (gen == 0 || gen != th->th_generation); | ||||
| 309 | } | ||||
| 310 | |||||
| 311 | uint64_t | ||||
| 312 | getnsecruntime(void) | ||||
| 313 | { | ||||
| 314 | struct bintime bt; | ||||
| 315 | |||||
| 316 | getbinruntime(&bt); | ||||
| 317 | return BINTIME_TO_NSEC(&bt); | ||||
| 318 | } | ||||
| 319 | |||||
| 320 | void | ||||
| 321 | bintime(struct bintime *bt) | ||||
| 322 | { | ||||
| 323 | struct timehands *th; | ||||
| 324 | u_int gen; | ||||
| 325 | |||||
| 326 | do { | ||||
| 327 | th = timehands; | ||||
| 328 | gen = th->th_generation; | ||||
| 329 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 330 | TIMECOUNT_TO_BINTIME(tc_delta(th), th->th_scale, bt); | ||||
| 331 | bintimeadd(bt, &th->th_offset, bt); | ||||
| 332 | bintimeadd(bt, &th->th_boottime, bt); | ||||
| 333 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 334 | } while (gen == 0 || gen != th->th_generation); | ||||
| 335 | } | ||||
| 336 | |||||
| 337 | void | ||||
| 338 | nanotime(struct timespec *tsp) | ||||
| 339 | { | ||||
| 340 | struct bintime bt; | ||||
| 341 | |||||
| 342 | bintime(&bt); | ||||
| 343 | BINTIME_TO_TIMESPEC(&bt, tsp); | ||||
| 344 | } | ||||
| 345 | |||||
| 346 | void | ||||
| 347 | microtime(struct timeval *tvp) | ||||
| 348 | { | ||||
| 349 | struct bintime bt; | ||||
| 350 | |||||
| 351 | bintime(&bt); | ||||
| 352 | BINTIME_TO_TIMEVAL(&bt, tvp); | ||||
| 353 | } | ||||
| 354 | |||||
| 355 | time_t | ||||
| 356 | gettime(void) | ||||
| 357 | { | ||||
| 358 | #if defined(__LP64__1) | ||||
| 359 | return time_second; /* atomic */ | ||||
| 360 | #else | ||||
| 361 | time_t now; | ||||
| 362 | struct timehands *th; | ||||
| 363 | u_int gen; | ||||
| 364 | |||||
| 365 | do { | ||||
| 366 | th = timehands; | ||||
| 367 | gen = th->th_generation; | ||||
| 368 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 369 | now = th->th_microtime.tv_sec; | ||||
| 370 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 371 | } while (gen == 0 || gen != th->th_generation); | ||||
| 372 | |||||
| 373 | return now; | ||||
| 374 | #endif | ||||
| 375 | } | ||||
| 376 | |||||
| 377 | void | ||||
| 378 | getnanouptime(struct timespec *tsp) | ||||
| 379 | { | ||||
| 380 | struct timehands *th; | ||||
| 381 | u_int gen; | ||||
| 382 | |||||
| 383 | do { | ||||
| 384 | th = timehands; | ||||
| 385 | gen = th->th_generation; | ||||
| 386 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 387 | BINTIME_TO_TIMESPEC(&th->th_offset, tsp); | ||||
| 388 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 389 | } while (gen == 0 || gen != th->th_generation); | ||||
| 390 | } | ||||
| 391 | |||||
| 392 | void | ||||
| 393 | getmicrouptime(struct timeval *tvp) | ||||
| 394 | { | ||||
| 395 | struct timehands *th; | ||||
| 396 | u_int gen; | ||||
| 397 | |||||
| 398 | do { | ||||
| 399 | th = timehands; | ||||
| 400 | gen = th->th_generation; | ||||
| 401 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 402 | BINTIME_TO_TIMEVAL(&th->th_offset, tvp); | ||||
| 403 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 404 | } while (gen == 0 || gen != th->th_generation); | ||||
| 405 | } | ||||
| 406 | |||||
| 407 | void | ||||
| 408 | getnanotime(struct timespec *tsp) | ||||
| 409 | { | ||||
| 410 | struct timehands *th; | ||||
| 411 | u_int gen; | ||||
| 412 | |||||
| 413 | do { | ||||
| 414 | th = timehands; | ||||
| 415 | gen = th->th_generation; | ||||
| 416 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 417 | *tsp = th->th_nanotime; | ||||
| 418 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 419 | } while (gen == 0 || gen != th->th_generation); | ||||
| 420 | } | ||||
| 421 | |||||
| 422 | void | ||||
| 423 | getmicrotime(struct timeval *tvp) | ||||
| 424 | { | ||||
| 425 | struct timehands *th; | ||||
| 426 | u_int gen; | ||||
| 427 | |||||
| 428 | do { | ||||
| 429 | th = timehands; | ||||
| 430 | gen = th->th_generation; | ||||
| 431 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 432 | *tvp = th->th_microtime; | ||||
| 433 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 434 | } while (gen == 0 || gen != th->th_generation); | ||||
| 435 | } | ||||
| 436 | |||||
| 437 | /* | ||||
| 438 | * Initialize a new timecounter and possibly use it. | ||||
| 439 | */ | ||||
| 440 | void | ||||
| 441 | tc_init(struct timecounter *tc) | ||||
| 442 | { | ||||
| 443 | u_int64_t tmp; | ||||
| 444 | u_int u; | ||||
| 445 | |||||
| 446 | u = tc->tc_frequency / tc->tc_counter_mask; | ||||
| 447 | /* XXX: We need some margin here, 10% is a guess */ | ||||
| 448 | u *= 11; | ||||
| 449 | u /= 10; | ||||
| 450 | if (tc->tc_quality >= 0) { | ||||
| 451 | if (u > hz) { | ||||
| 452 | tc->tc_quality = -2000; | ||||
| 453 | printf("Timecounter \"%s\" frequency %lu Hz", | ||||
| 454 | tc->tc_name, (unsigned long)tc->tc_frequency); | ||||
| 455 | printf(" -- Insufficient hz, needs at least %u\n", u); | ||||
| 456 | } | ||||
| 457 | } | ||||
| 458 | |||||
| 459 | /* Determine the counter's precision. */ | ||||
| 460 | for (tmp = 1; (tmp & tc->tc_counter_mask) == 0; tmp <<= 1) | ||||
| 461 | continue; | ||||
| 462 | tc->tc_precision = tmp; | ||||
| 463 | |||||
| 464 | SLIST_INSERT_HEAD(&tc_list, tc, tc_next)do { (tc)->tc_next.sle_next = (&tc_list)->slh_first ; (&tc_list)->slh_first = (tc); } while (0); | ||||
| 465 | |||||
| 466 | /* | ||||
| 467 | * Never automatically use a timecounter with negative quality. | ||||
| 468 | * Even though we run on the dummy counter, switching here may be | ||||
| 469 | * worse since this timecounter may not be monotonic. | ||||
| 470 | */ | ||||
| 471 | if (tc->tc_quality < 0) | ||||
| 472 | return; | ||||
| 473 | if (tc->tc_quality < timecounter->tc_quality) | ||||
| 474 | return; | ||||
| 475 | if (tc->tc_quality == timecounter->tc_quality && | ||||
| 476 | tc->tc_frequency < timecounter->tc_frequency) | ||||
| 477 | return; | ||||
| 478 | (void)tc->tc_get_timecount(tc); | ||||
| 479 | enqueue_randomness(tc->tc_get_timecount(tc)); | ||||
| 480 | |||||
| 481 | timecounter = tc; | ||||
| 482 | } | ||||
| 483 | |||||
| 484 | /* | ||||
| 485 | * Change the given timecounter's quality. If it is the active | ||||
| 486 | * counter and it is no longer the best counter, activate the | ||||
| 487 | * best counter. | ||||
| 488 | */ | ||||
| 489 | void | ||||
| 490 | tc_reset_quality(struct timecounter *tc, int quality) | ||||
| 491 | { | ||||
| 492 | struct timecounter *best = &dummy_timecounter, *tmp; | ||||
| 493 | |||||
| 494 | if (tc == &dummy_timecounter) | ||||
| 495 | panic("%s: cannot change dummy counter quality", __func__); | ||||
| 496 | |||||
| 497 | tc->tc_quality = quality; | ||||
| 498 | if (timecounter == tc) { | ||||
| 499 | SLIST_FOREACH(tmp, &tc_list, tc_next)for((tmp) = ((&tc_list)->slh_first); (tmp) != ((void * )0); (tmp) = ((tmp)->tc_next.sle_next)) { | ||||
| 500 | if (tmp->tc_quality < 0) | ||||
| 501 | continue; | ||||
| 502 | if (tmp->tc_quality < best->tc_quality) | ||||
| 503 | continue; | ||||
| 504 | if (tmp->tc_quality == best->tc_quality && | ||||
| 505 | tmp->tc_frequency < best->tc_frequency) | ||||
| 506 | continue; | ||||
| 507 | best = tmp; | ||||
| 508 | } | ||||
| 509 | if (best != tc) { | ||||
| 510 | enqueue_randomness(best->tc_get_timecount(best)); | ||||
| 511 | timecounter = best; | ||||
| 512 | printf("timecounter: active counter changed: %s -> %s\n", | ||||
| 513 | tc->tc_name, best->tc_name); | ||||
| 514 | } | ||||
| 515 | } | ||||
| 516 | } | ||||
| 517 | |||||
| 518 | /* Report the frequency of the current timecounter. */ | ||||
| 519 | u_int64_t | ||||
| 520 | tc_getfrequency(void) | ||||
| 521 | { | ||||
| 522 | return (timehands->th_counter->tc_frequency); | ||||
| 523 | } | ||||
| 524 | |||||
| 525 | /* Report the precision of the current timecounter. */ | ||||
| 526 | u_int64_t | ||||
| 527 | tc_getprecision(void) | ||||
| 528 | { | ||||
| 529 | return (timehands->th_counter->tc_precision); | ||||
| 530 | } | ||||
| 531 | |||||
| 532 | /* | ||||
| 533 | * Step our concept of UTC, aka the realtime clock. | ||||
| 534 | * This is done by modifying our estimate of when we booted. | ||||
| 535 | * | ||||
| 536 | * Any ongoing adjustment is meaningless after a clock jump, | ||||
| 537 | * so we zero adjtimedelta here as well. | ||||
| 538 | */ | ||||
| 539 | void | ||||
| 540 | tc_setrealtimeclock(const struct timespec *ts) | ||||
| 541 | { | ||||
| 542 | struct bintime boottime, old_utc, uptime, utc; | ||||
| 543 | struct timespec tmp; | ||||
| 544 | int64_t zero = 0; | ||||
| 545 | |||||
| 546 | TIMESPEC_TO_BINTIME(ts, &utc); | ||||
| 547 | |||||
| 548 | rw_enter_write(&tc_lock); | ||||
| 549 | mtx_enter(&windup_mtx); | ||||
| 550 | |||||
| 551 | binuptime(&uptime); | ||||
| 552 | bintimesub(&utc, &uptime, &boottime); | ||||
| 553 | bintimeadd(&timehands->th_boottime, &uptime, &old_utc); | ||||
| 554 | /* XXX fiddle all the little crinkly bits around the fiords... */ | ||||
| 555 | tc_windup(&boottime, NULL((void *)0), &zero); | ||||
| 556 | |||||
| 557 | mtx_leave(&windup_mtx); | ||||
| 558 | rw_exit_write(&tc_lock); | ||||
| 559 | |||||
| 560 | enqueue_randomness(ts->tv_sec); | ||||
| 561 | |||||
| 562 | if (timestepwarnings) { | ||||
| 563 | BINTIME_TO_TIMESPEC(&old_utc, &tmp); | ||||
| 564 | log(LOG_INFO6, "Time stepped from %lld.%09ld to %lld.%09ld\n", | ||||
| 565 | (long long)tmp.tv_sec, tmp.tv_nsec, | ||||
| 566 | (long long)ts->tv_sec, ts->tv_nsec); | ||||
| 567 | } | ||||
| 568 | } | ||||
| 569 | |||||
| 570 | /* | ||||
| 571 | * Step the monotonic and realtime clocks, triggering any timeouts that | ||||
| 572 | * should have occurred across the interval. | ||||
| 573 | */ | ||||
| 574 | void | ||||
| 575 | tc_setclock(const struct timespec *ts) | ||||
| 576 | { | ||||
| 577 | struct bintime new_naptime, old_naptime, uptime, utc; | ||||
| 578 | static int first = 1; | ||||
| 579 | #ifndef SMALL_KERNEL | ||||
| 580 | struct bintime elapsed; | ||||
| 581 | long long adj_ticks; | ||||
| 582 | #endif | ||||
| 583 | |||||
| 584 | /* | ||||
| 585 | * When we're called for the first time, during boot when | ||||
| 586 | * the root partition is mounted, we need to set boottime. | ||||
| 587 | */ | ||||
| 588 | if (first) { | ||||
| 589 | tc_setrealtimeclock(ts); | ||||
| 590 | first = 0; | ||||
| 591 | return; | ||||
| 592 | } | ||||
| 593 | |||||
| 594 | enqueue_randomness(ts->tv_sec); | ||||
| 595 | |||||
| 596 | TIMESPEC_TO_BINTIME(ts, &utc); | ||||
| 597 | |||||
| 598 | mtx_enter(&windup_mtx); | ||||
| 599 | |||||
| 600 | bintimesub(&utc, &timehands->th_boottime, &uptime); | ||||
| 601 | old_naptime = timehands->th_naptime; | ||||
| 602 | /* XXX fiddle all the little crinkly bits around the fiords... */ | ||||
| 603 | tc_windup(NULL((void *)0), &uptime, NULL((void *)0)); | ||||
| 604 | new_naptime = timehands->th_naptime; | ||||
| 605 | |||||
| 606 | mtx_leave(&windup_mtx); | ||||
| 607 | |||||
| 608 | #ifndef SMALL_KERNEL | ||||
| 609 | /* convert the bintime to ticks */ | ||||
| 610 | bintimesub(&new_naptime, &old_naptime, &elapsed); | ||||
| 611 | adj_ticks = BINTIME_TO_NSEC(&elapsed) / tick_nsec; | ||||
| 612 | if (adj_ticks > 0) { | ||||
| 613 | if (adj_ticks > INT_MAX0x7fffffff) | ||||
| 614 | adj_ticks = INT_MAX0x7fffffff; | ||||
| 615 | timeout_adjust_ticks(adj_ticks); | ||||
| 616 | } | ||||
| 617 | #endif | ||||
| 618 | } | ||||
| 619 | |||||
| 620 | void | ||||
| 621 | tc_update_timekeep(void) | ||||
| 622 | { | ||||
| 623 | static struct timecounter *last_tc = NULL((void *)0); | ||||
| 624 | struct timehands *th; | ||||
| 625 | |||||
| 626 | MUTEX_ASSERT_LOCKED(&windup_mtx)do { if (((&windup_mtx)->mtx_owner != ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof (struct cpu_info, ci_self))); __ci;})) && !(panicstr || db_active)) panic("mutex %p not held in %s", (&windup_mtx ), __func__); } while (0); | ||||
| 627 | |||||
| 628 | if (timekeep == NULL((void *)0)) | ||||
| 629 | return; | ||||
| 630 | |||||
| 631 | th = timehands; | ||||
| 632 | timekeep->tk_generation = 0; | ||||
| 633 | membar_producer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 634 | timekeep->tk_scale = th->th_scale; | ||||
| 635 | timekeep->tk_offset_count = th->th_offset_count; | ||||
| 636 | timekeep->tk_offset = th->th_offset; | ||||
| 637 | timekeep->tk_naptime = th->th_naptime; | ||||
| 638 | timekeep->tk_boottime = th->th_boottime; | ||||
| 639 | if (last_tc != th->th_counter) { | ||||
| 640 | timekeep->tk_counter_mask = th->th_counter->tc_counter_mask; | ||||
| 641 | timekeep->tk_user = th->th_counter->tc_user; | ||||
| 642 | last_tc = th->th_counter; | ||||
| 643 | } | ||||
| 644 | membar_producer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 645 | timekeep->tk_generation = th->th_generation; | ||||
| 646 | |||||
| 647 | return; | ||||
| 648 | } | ||||
| 649 | |||||
| 650 | /* | ||||
| 651 | * Initialize the next struct timehands in the ring and make | ||||
| 652 | * it the active timehands. Along the way we might switch to a different | ||||
| 653 | * timecounter and/or do seconds processing in NTP. Slightly magic. | ||||
| 654 | */ | ||||
| 655 | void | ||||
| 656 | tc_windup(struct bintime *new_boottime, struct bintime *new_offset, | ||||
| 657 | int64_t *new_adjtimedelta) | ||||
| 658 | { | ||||
| 659 | struct bintime bt; | ||||
| 660 | struct timecounter *active_tc; | ||||
| 661 | struct timehands *th, *tho; | ||||
| 662 | u_int64_t scale; | ||||
| 663 | u_int delta, ncount, ogen; | ||||
| 664 | |||||
| 665 | if (new_boottime
| ||||
| 666 | rw_assert_wrlock(&tc_lock); | ||||
| 667 | MUTEX_ASSERT_LOCKED(&windup_mtx)do { if (((&windup_mtx)->mtx_owner != ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof (struct cpu_info, ci_self))); __ci;})) && !(panicstr || db_active)) panic("mutex %p not held in %s", (&windup_mtx ), __func__); } while (0); | ||||
| 668 | |||||
| 669 | active_tc = timecounter; | ||||
| 670 | |||||
| 671 | /* | ||||
| 672 | * Make the next timehands a copy of the current one, but do not | ||||
| 673 | * overwrite the generation or next pointer. While we update | ||||
| 674 | * the contents, the generation must be zero. | ||||
| 675 | */ | ||||
| 676 | tho = timehands; | ||||
| 677 | ogen = tho->th_generation; | ||||
| 678 | th = tho->th_next; | ||||
| 679 | th->th_generation = 0; | ||||
| 680 | membar_producer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 681 | memcpy(th, tho, offsetof(struct timehands, th_generation))__builtin_memcpy((th), (tho), (__builtin_offsetof(struct timehands , th_generation))); | ||||
| 682 | |||||
| 683 | /* | ||||
| 684 | * Capture a timecounter delta on the current timecounter and if | ||||
| 685 | * changing timecounters, a counter value from the new timecounter. | ||||
| 686 | * Update the offset fields accordingly. | ||||
| 687 | */ | ||||
| 688 | delta = tc_delta(th); | ||||
| 689 | if (th->th_counter != active_tc) | ||||
| 690 | ncount = active_tc->tc_get_timecount(active_tc); | ||||
| 691 | else | ||||
| 692 | ncount = 0; | ||||
| 693 | th->th_offset_count += delta; | ||||
| 694 | th->th_offset_count &= th->th_counter->tc_counter_mask; | ||||
| 695 | TIMECOUNT_TO_BINTIME(delta, th->th_scale, &bt); | ||||
| 696 | bintimeadd(&th->th_offset, &bt, &th->th_offset); | ||||
| 697 | |||||
| 698 | /* | ||||
| 699 | * Ignore new offsets that predate the current offset. | ||||
| 700 | * If changing the offset, first increase the naptime | ||||
| 701 | * accordingly. | ||||
| 702 | */ | ||||
| 703 | if (new_offset
th->th_offset)->frac < (new_offset)->frac : (& th->th_offset)->sec < (new_offset)->sec)) { | ||||
| 704 | bintimesub(new_offset, &th->th_offset, &bt); | ||||
| 705 | bintimeadd(&th->th_naptime, &bt, &th->th_naptime); | ||||
| 706 | naptime = th->th_naptime.sec; | ||||
| 707 | th->th_offset = *new_offset; | ||||
| 708 | } | ||||
| 709 | |||||
| 710 | /* | ||||
| 711 | * If changing the boot time or clock adjustment, do so before | ||||
| 712 | * NTP processing. | ||||
| 713 | */ | ||||
| 714 | if (new_boottime
| ||||
| 715 | th->th_boottime = *new_boottime; | ||||
| 716 | if (new_adjtimedelta
| ||||
| 717 | th->th_adjtimedelta = *new_adjtimedelta; | ||||
| 718 | /* Reset the NTP update period. */ | ||||
| 719 | bintimesub(&th->th_offset, &th->th_naptime, | ||||
| 720 | &th->th_next_ntp_update); | ||||
| 721 | } | ||||
| 722 | |||||
| 723 | /* | ||||
| 724 | * Deal with NTP second processing. The while-loop normally | ||||
| 725 | * iterates at most once, but in extreme situations it might | ||||
| 726 | * keep NTP sane if tc_windup() is not run for several seconds. | ||||
| 727 | */ | ||||
| 728 | bintimesub(&th->th_offset, &th->th_naptime, &bt); | ||||
| 729 | while (bintimecmp(&th->th_next_ntp_update, &bt, <=)((&th->th_next_ntp_update)->sec == (&bt)->sec ? (&th->th_next_ntp_update)->frac <= (&bt)-> frac : (&th->th_next_ntp_update)->sec <= (&bt )->sec)) { | ||||
| 730 | ntp_update_second(th); | ||||
| 731 | th->th_next_ntp_update.sec++; | ||||
| 732 | } | ||||
| 733 | |||||
| 734 | /* Update the UTC timestamps used by the get*() functions. */ | ||||
| 735 | bintimeadd(&th->th_boottime, &th->th_offset, &bt); | ||||
| 736 | BINTIME_TO_TIMEVAL(&bt, &th->th_microtime); | ||||
| 737 | BINTIME_TO_TIMESPEC(&bt, &th->th_nanotime); | ||||
| 738 | |||||
| 739 | /* Now is a good time to change timecounters. */ | ||||
| 740 | if (th->th_counter != active_tc) { | ||||
| 741 | th->th_counter = active_tc; | ||||
| 742 | th->th_offset_count = ncount; | ||||
| 743 | } | ||||
| 744 | |||||
| 745 | /*- | ||||
| 746 | * Recalculate the scaling factor. We want the number of 1/2^64 | ||||
| 747 | * fractions of a second per period of the hardware counter, taking | ||||
| 748 | * into account the th_adjustment factor which the NTP PLL/adjtime(2) | ||||
| 749 | * processing provides us with. | ||||
| 750 | * | ||||
| 751 | * The th_adjustment is nanoseconds per second with 32 bit binary | ||||
| 752 | * fraction and we want 64 bit binary fraction of second: | ||||
| 753 | * | ||||
| 754 | * x = a * 2^32 / 10^9 = a * 4.294967296 | ||||
| 755 | * | ||||
| 756 | * The range of th_adjustment is +/- 5000PPM so inside a 64bit int | ||||
| 757 | * we can only multiply by about 850 without overflowing, but that | ||||
| 758 | * leaves suitably precise fractions for multiply before divide. | ||||
| 759 | * | ||||
| 760 | * Divide before multiply with a fraction of 2199/512 results in a | ||||
| 761 | * systematic undercompensation of 10PPM of th_adjustment. On a | ||||
| 762 | * 5000PPM adjustment this is a 0.05PPM error. This is acceptable. | ||||
| 763 | * | ||||
| 764 | * We happily sacrifice the lowest of the 64 bits of our result | ||||
| 765 | * to the goddess of code clarity. | ||||
| 766 | * | ||||
| 767 | */ | ||||
| 768 | scale = (u_int64_t)1 << 63; | ||||
| 769 | scale += \ | ||||
| 770 | ((th->th_adjustment + th->th_counter->tc_freq_adj) / 1024) * 2199; | ||||
| 771 | scale /= th->th_counter->tc_frequency; | ||||
| 772 | th->th_scale = scale * 2; | ||||
| 773 | |||||
| 774 | /* | ||||
| 775 | * Now that the struct timehands is again consistent, set the new | ||||
| 776 | * generation number, making sure to not make it zero. | ||||
| 777 | */ | ||||
| 778 | if (++ogen == 0) | ||||
| 779 | ogen = 1; | ||||
| 780 | membar_producer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 781 | th->th_generation = ogen; | ||||
| 782 | |||||
| 783 | /* Go live with the new struct timehands. */ | ||||
| 784 | time_second = th->th_microtime.tv_sec; | ||||
| 785 | time_uptime = th->th_offset.sec; | ||||
| 786 | membar_producer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 787 | timehands = th; | ||||
| 788 | |||||
| 789 | tc_update_timekeep(); | ||||
| 790 | } | ||||
| 791 | |||||
| 792 | /* Report or change the active timecounter hardware. */ | ||||
| 793 | int | ||||
| 794 | sysctl_tc_hardware(void *oldp, size_t *oldlenp, void *newp, size_t newlen) | ||||
| 795 | { | ||||
| 796 | char newname[32]; | ||||
| 797 | struct timecounter *newtc, *tc; | ||||
| 798 | int error; | ||||
| 799 | |||||
| 800 | tc = timecounter; | ||||
| 801 | strlcpy(newname, tc->tc_name, sizeof(newname)); | ||||
| 802 | |||||
| 803 | error = sysctl_string(oldp, oldlenp, newp, newlen, newname, sizeof(newname)); | ||||
| 804 | if (error != 0 || strcmp(newname, tc->tc_name) == 0) | ||||
| 805 | return (error); | ||||
| 806 | SLIST_FOREACH(newtc, &tc_list, tc_next)for((newtc) = ((&tc_list)->slh_first); (newtc) != ((void *)0); (newtc) = ((newtc)->tc_next.sle_next)) { | ||||
| 807 | if (strcmp(newname, newtc->tc_name) != 0) | ||||
| 808 | continue; | ||||
| 809 | |||||
| 810 | /* Warm up new timecounter. */ | ||||
| 811 | (void)newtc->tc_get_timecount(newtc); | ||||
| 812 | (void)newtc->tc_get_timecount(newtc); | ||||
| 813 | |||||
| 814 | rw_enter_write(&tc_lock); | ||||
| 815 | timecounter = newtc; | ||||
| 816 | rw_exit_write(&tc_lock); | ||||
| 817 | |||||
| 818 | return (0); | ||||
| 819 | } | ||||
| 820 | return (EINVAL22); | ||||
| 821 | } | ||||
| 822 | |||||
| 823 | /* Report or change the active timecounter hardware. */ | ||||
| 824 | int | ||||
| 825 | sysctl_tc_choice(void *oldp, size_t *oldlenp, void *newp, size_t newlen) | ||||
| 826 | { | ||||
| 827 | char buf[32], *spc, *choices; | ||||
| 828 | struct timecounter *tc; | ||||
| 829 | int error, maxlen; | ||||
| 830 | |||||
| 831 | if (SLIST_EMPTY(&tc_list)(((&tc_list)->slh_first) == ((void *)0))) | ||||
| 832 | return (sysctl_rdstring(oldp, oldlenp, newp, "")); | ||||
| 833 | |||||
| 834 | spc = ""; | ||||
| 835 | maxlen = 0; | ||||
| 836 | SLIST_FOREACH(tc, &tc_list, tc_next)for((tc) = ((&tc_list)->slh_first); (tc) != ((void *)0 ); (tc) = ((tc)->tc_next.sle_next)) | ||||
| 837 | maxlen += sizeof(buf); | ||||
| 838 | choices = malloc(maxlen, M_TEMP127, M_WAITOK0x0001); | ||||
| 839 | *choices = '\0'; | ||||
| 840 | SLIST_FOREACH(tc, &tc_list, tc_next)for((tc) = ((&tc_list)->slh_first); (tc) != ((void *)0 ); (tc) = ((tc)->tc_next.sle_next)) { | ||||
| 841 | snprintf(buf, sizeof(buf), "%s%s(%d)", | ||||
| 842 | spc, tc->tc_name, tc->tc_quality); | ||||
| 843 | spc = " "; | ||||
| 844 | strlcat(choices, buf, maxlen); | ||||
| 845 | } | ||||
| 846 | error = sysctl_rdstring(oldp, oldlenp, newp, choices); | ||||
| 847 | free(choices, M_TEMP127, maxlen); | ||||
| 848 | return (error); | ||||
| 849 | } | ||||
| 850 | |||||
| 851 | /* | ||||
| 852 | * Timecounters need to be updated every so often to prevent the hardware | ||||
| 853 | * counter from overflowing. Updating also recalculates the cached values | ||||
| 854 | * used by the get*() family of functions, so their precision depends on | ||||
| 855 | * the update frequency. | ||||
| 856 | */ | ||||
| 857 | static int tc_tick; | ||||
| 858 | |||||
| 859 | void | ||||
| 860 | tc_ticktock(void) | ||||
| 861 | { | ||||
| 862 | static int count; | ||||
| 863 | |||||
| 864 | if (++count < tc_tick) | ||||
| 865 | return; | ||||
| 866 | if (!mtx_enter_try(&windup_mtx)) | ||||
| 867 | return; | ||||
| 868 | count = 0; | ||||
| 869 | tc_windup(NULL((void *)0), NULL((void *)0), NULL((void *)0)); | ||||
| 870 | mtx_leave(&windup_mtx); | ||||
| 871 | } | ||||
| 872 | |||||
| 873 | void | ||||
| 874 | inittimecounter(void) | ||||
| 875 | { | ||||
| 876 | #ifdef DEBUG | ||||
| 877 | u_int p; | ||||
| 878 | #endif | ||||
| 879 | |||||
| 880 | /* | ||||
| 881 | * Set the initial timeout to | ||||
| 882 | * max(1, <approx. number of hardclock ticks in a millisecond>). | ||||
| 883 | * People should probably not use the sysctl to set the timeout | ||||
| 884 | * to smaller than its initial value, since that value is the | ||||
| 885 | * smallest reasonable one. If they want better timestamps they | ||||
| 886 | * should use the non-"get"* functions. | ||||
| 887 | */ | ||||
| 888 | if (hz > 1000) | ||||
| 889 | tc_tick = (hz + 500) / 1000; | ||||
| 890 | else | ||||
| 891 | tc_tick = 1; | ||||
| 892 | #ifdef DEBUG | ||||
| 893 | p = (tc_tick * 1000000) / hz; | ||||
| 894 | printf("Timecounters tick every %d.%03u msec\n", p / 1000, p % 1000); | ||||
| 895 | #endif | ||||
| 896 | |||||
| 897 | /* warm up new timecounter (again) and get rolling. */ | ||||
| 898 | (void)timecounter->tc_get_timecount(timecounter); | ||||
| 899 | (void)timecounter->tc_get_timecount(timecounter); | ||||
| 900 | } | ||||
| 901 | |||||
| 902 | const struct sysctl_bounded_args tc_vars[] = { | ||||
| 903 | { KERN_TIMECOUNTER_TICK1, &tc_tick, SYSCTL_INT_READONLY1,0 }, | ||||
| 904 | { KERN_TIMECOUNTER_TIMESTEPWARNINGS2, ×tepwarnings, 0, 1 }, | ||||
| 905 | }; | ||||
| 906 | |||||
| 907 | /* | ||||
| 908 | * Return timecounter-related information. | ||||
| 909 | */ | ||||
| 910 | int | ||||
| 911 | sysctl_tc(int *name, u_int namelen, void *oldp, size_t *oldlenp, | ||||
| 912 | void *newp, size_t newlen) | ||||
| 913 | { | ||||
| 914 | if (namelen != 1) | ||||
| 915 | return (ENOTDIR20); | ||||
| 916 | |||||
| 917 | switch (name[0]) { | ||||
| 918 | case KERN_TIMECOUNTER_HARDWARE3: | ||||
| 919 | return (sysctl_tc_hardware(oldp, oldlenp, newp, newlen)); | ||||
| 920 | case KERN_TIMECOUNTER_CHOICE4: | ||||
| 921 | return (sysctl_tc_choice(oldp, oldlenp, newp, newlen)); | ||||
| 922 | default: | ||||
| 923 | return (sysctl_bounded_arr(tc_vars, nitems(tc_vars)(sizeof((tc_vars)) / sizeof((tc_vars)[0])), name, | ||||
| 924 | namelen, oldp, oldlenp, newp, newlen)); | ||||
| 925 | } | ||||
| 926 | /* NOTREACHED */ | ||||
| 927 | } | ||||
| 928 | |||||
| 929 | /* | ||||
| 930 | * Skew the timehands according to any adjtime(2) adjustment. | ||||
| 931 | */ | ||||
| 932 | void | ||||
| 933 | ntp_update_second(struct timehands *th) | ||||
| 934 | { | ||||
| 935 | int64_t adj; | ||||
| 936 | |||||
| 937 | MUTEX_ASSERT_LOCKED(&windup_mtx)do { if (((&windup_mtx)->mtx_owner != ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof (struct cpu_info, ci_self))); __ci;})) && !(panicstr || db_active)) panic("mutex %p not held in %s", (&windup_mtx ), __func__); } while (0); | ||||
| 938 | |||||
| 939 | if (th->th_adjtimedelta > 0) | ||||
| 940 | adj = MIN(5000, th->th_adjtimedelta)(((5000)<(th->th_adjtimedelta))?(5000):(th->th_adjtimedelta )); | ||||
| 941 | else | ||||
| 942 | adj = MAX(-5000, th->th_adjtimedelta)(((-5000)>(th->th_adjtimedelta))?(-5000):(th->th_adjtimedelta )); | ||||
| 943 | th->th_adjtimedelta -= adj; | ||||
| 944 | th->th_adjustment = (adj * 1000) << 32; | ||||
| |||||
| 945 | } | ||||
| 946 | |||||
| 947 | void | ||||
| 948 | tc_adjfreq(int64_t *old, int64_t *new) | ||||
| 949 | { | ||||
| 950 | if (old != NULL((void *)0)) { | ||||
| 951 | rw_assert_anylock(&tc_lock); | ||||
| 952 | *old = timecounter->tc_freq_adj; | ||||
| 953 | } | ||||
| 954 | if (new != NULL((void *)0)) { | ||||
| 955 | rw_assert_wrlock(&tc_lock); | ||||
| 956 | mtx_enter(&windup_mtx); | ||||
| 957 | timecounter->tc_freq_adj = *new; | ||||
| 958 | tc_windup(NULL((void *)0), NULL((void *)0), NULL((void *)0)); | ||||
| 959 | mtx_leave(&windup_mtx); | ||||
| 960 | } | ||||
| 961 | } | ||||
| 962 | |||||
| 963 | void | ||||
| 964 | tc_adjtime(int64_t *old, int64_t *new) | ||||
| 965 | { | ||||
| 966 | struct timehands *th; | ||||
| 967 | u_int gen; | ||||
| 968 | |||||
| 969 | if (old != NULL((void *)0)) { | ||||
| |||||
| 970 | do { | ||||
| 971 | th = timehands; | ||||
| 972 | gen = th->th_generation; | ||||
| 973 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 974 | *old = th->th_adjtimedelta; | ||||
| 975 | membar_consumer()do { __asm volatile("" ::: "memory"); } while (0); | ||||
| 976 | } while (gen == 0 || gen != th->th_generation); | ||||
| 977 | } | ||||
| 978 | if (new != NULL((void *)0)) { | ||||
| 979 | rw_assert_wrlock(&tc_lock); | ||||
| 980 | mtx_enter(&windup_mtx); | ||||
| 981 | tc_windup(NULL((void *)0), NULL((void *)0), new); | ||||
| 982 | mtx_leave(&windup_mtx); | ||||
| 983 | } | ||||
| 984 | } |