1 /* $OpenBSD: kern_clockintr.c,v 1.47 2023/09/10 03:08:05 cheloha Exp $ */ 2 /* 3 * Copyright (c) 2003 Dale Rahn <drahn@openbsd.org> 4 * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org> 5 * Copyright (c) 2020-2022 Scott Cheloha <cheloha@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/atomic.h> 23 #include <sys/clockintr.h> 24 #include <sys/kernel.h> 25 #include <sys/malloc.h> 26 #include <sys/mutex.h> 27 #include <sys/resourcevar.h> 28 #include <sys/queue.h> 29 #include <sys/sched.h> 30 #include <sys/stdint.h> 31 #include <sys/sysctl.h> 32 #include <sys/time.h> 33 34 /* 35 * Protection for global variables in this file: 36 * 37 * I Immutable after initialization. 38 */ 39 uint32_t clockintr_flags; /* [I] global state + behavior flags */ 40 uint32_t hardclock_period; /* [I] hardclock period (ns) */ 41 uint32_t statclock_avg; /* [I] average statclock period (ns) */ 42 uint32_t statclock_min; /* [I] minimum statclock period (ns) */ 43 uint32_t statclock_mask; /* [I] set of allowed offsets */ 44 45 uint64_t clockintr_advance_random(struct clockintr *, uint64_t, uint32_t); 46 void clockintr_hardclock(struct clockintr *, void *, void *); 47 void clockintr_schedule(struct clockintr *, uint64_t); 48 void clockintr_schedule_locked(struct clockintr *, uint64_t); 49 void clockintr_statclock(struct clockintr *, void *, void *); 50 void clockqueue_intrclock_install(struct clockintr_queue *, 51 const struct intrclock *); 52 uint64_t clockqueue_next(const struct clockintr_queue *); 53 void clockqueue_pend_delete(struct clockintr_queue *, struct clockintr *); 54 void clockqueue_pend_insert(struct clockintr_queue *, struct clockintr *, 55 uint64_t); 56 void clockqueue_reset_intrclock(struct clockintr_queue *); 57 uint64_t nsec_advance(uint64_t *, uint64_t, uint64_t); 58 59 /* 60 * Initialize global state. Set flags and compute intervals. 61 */ 62 void 63 clockintr_init(uint32_t flags) 64 { 65 uint32_t half_avg, var; 66 67 KASSERT(CPU_IS_PRIMARY(curcpu())); 68 KASSERT(clockintr_flags == 0); 69 KASSERT(!ISSET(flags, ~CL_FLAG_MASK)); 70 71 KASSERT(hz > 0 && hz <= 1000000000); 72 hardclock_period = 1000000000 / hz; 73 roundrobin_period = hardclock_period * 10; 74 75 KASSERT(stathz >= 1 && stathz <= 1000000000); 76 77 /* 78 * Compute the average statclock() period. Then find var, the 79 * largest power of two such that var <= statclock_avg / 2. 80 */ 81 statclock_avg = 1000000000 / stathz; 82 half_avg = statclock_avg / 2; 83 for (var = 1U << 31; var > half_avg; var /= 2) 84 continue; 85 86 /* 87 * Set a lower bound for the range using statclock_avg and var. 88 * The mask for that range is just (var - 1). 89 */ 90 statclock_min = statclock_avg - (var / 2); 91 statclock_mask = var - 1; 92 93 SET(clockintr_flags, flags | CL_INIT); 94 } 95 96 /* 97 * Ready the calling CPU for clockintr_dispatch(). If this is our 98 * first time here, install the intrclock, if any, and set necessary 99 * flags. Advance the schedule as needed. 100 */ 101 void 102 clockintr_cpu_init(const struct intrclock *ic) 103 { 104 uint64_t multiplier = 0; 105 struct cpu_info *ci = curcpu(); 106 struct clockintr_queue *cq = &ci->ci_queue; 107 struct schedstate_percpu *spc = &ci->ci_schedstate; 108 int reset_cq_intrclock = 0; 109 110 KASSERT(ISSET(clockintr_flags, CL_INIT)); 111 112 if (ic != NULL) 113 clockqueue_intrclock_install(cq, ic); 114 115 /* TODO: Remove these from struct clockintr_queue. */ 116 if (cq->cq_hardclock == NULL) { 117 cq->cq_hardclock = clockintr_establish(ci, clockintr_hardclock, 118 NULL); 119 if (cq->cq_hardclock == NULL) 120 panic("%s: failed to establish hardclock", __func__); 121 } 122 if (cq->cq_statclock == NULL) { 123 cq->cq_statclock = clockintr_establish(ci, clockintr_statclock, 124 NULL); 125 if (cq->cq_statclock == NULL) 126 panic("%s: failed to establish statclock", __func__); 127 } 128 129 /* 130 * Mask CQ_INTRCLOCK while we're advancing the internal clock 131 * interrupts. We don't want the intrclock to fire until this 132 * thread reaches clockintr_trigger(). 133 */ 134 if (ISSET(cq->cq_flags, CQ_INTRCLOCK)) { 135 CLR(cq->cq_flags, CQ_INTRCLOCK); 136 reset_cq_intrclock = 1; 137 } 138 139 /* 140 * Until we understand scheduler lock contention better, stagger 141 * the hardclock and statclock so they don't all happen at once. 142 * If we have no intrclock it doesn't matter, we have no control 143 * anyway. The primary CPU's starting offset is always zero, so 144 * leave the multiplier zero. 145 */ 146 if (!CPU_IS_PRIMARY(ci) && reset_cq_intrclock) 147 multiplier = CPU_INFO_UNIT(ci); 148 149 /* 150 * The first time we do this, the primary CPU cannot skip any 151 * hardclocks. We can skip hardclocks on subsequent calls because 152 * the global tick value is advanced during inittodr(9) on our 153 * behalf. 154 */ 155 if (CPU_IS_PRIMARY(ci)) { 156 if (cq->cq_hardclock->cl_expiration == 0) 157 clockintr_schedule(cq->cq_hardclock, 0); 158 else 159 clockintr_advance(cq->cq_hardclock, hardclock_period); 160 } else { 161 if (cq->cq_hardclock->cl_expiration == 0) { 162 clockintr_stagger(cq->cq_hardclock, hardclock_period, 163 multiplier, MAXCPUS); 164 } 165 clockintr_advance(cq->cq_hardclock, hardclock_period); 166 } 167 168 /* 169 * We can always advance the statclock. There is no reason to 170 * stagger a randomized statclock. 171 */ 172 if (!ISSET(clockintr_flags, CL_RNDSTAT)) { 173 if (cq->cq_statclock->cl_expiration == 0) { 174 clockintr_stagger(cq->cq_statclock, statclock_avg, 175 multiplier, MAXCPUS); 176 } 177 } 178 clockintr_advance(cq->cq_statclock, statclock_avg); 179 180 /* 181 * XXX Need to find a better place to do this. We can't do it in 182 * sched_init_cpu() because initclocks() runs after it. 183 */ 184 if (spc->spc_itimer->cl_expiration == 0) { 185 clockintr_stagger(spc->spc_itimer, hardclock_period, 186 multiplier, MAXCPUS); 187 } 188 if (spc->spc_profclock->cl_expiration == 0) { 189 clockintr_stagger(spc->spc_profclock, profclock_period, 190 multiplier, MAXCPUS); 191 } 192 if (spc->spc_roundrobin->cl_expiration == 0) { 193 clockintr_stagger(spc->spc_roundrobin, hardclock_period, 194 multiplier, MAXCPUS); 195 } 196 clockintr_advance(spc->spc_roundrobin, roundrobin_period); 197 198 if (reset_cq_intrclock) 199 SET(cq->cq_flags, CQ_INTRCLOCK); 200 } 201 202 /* 203 * If we have an intrclock, trigger it to start the dispatch cycle. 204 */ 205 void 206 clockintr_trigger(void) 207 { 208 struct clockintr_queue *cq = &curcpu()->ci_queue; 209 210 KASSERT(ISSET(cq->cq_flags, CQ_INIT)); 211 212 if (ISSET(cq->cq_flags, CQ_INTRCLOCK)) 213 intrclock_trigger(&cq->cq_intrclock); 214 } 215 216 /* 217 * Run all expired events scheduled on the calling CPU. 218 */ 219 int 220 clockintr_dispatch(void *frame) 221 { 222 uint64_t lateness, run = 0, start; 223 struct cpu_info *ci = curcpu(); 224 struct clockintr *cl, *shadow; 225 struct clockintr_queue *cq = &ci->ci_queue; 226 uint32_t ogen; 227 228 if (cq->cq_dispatch != 0) 229 panic("%s: recursive dispatch", __func__); 230 cq->cq_dispatch = 1; 231 232 splassert(IPL_CLOCK); 233 KASSERT(ISSET(cq->cq_flags, CQ_INIT)); 234 235 mtx_enter(&cq->cq_mtx); 236 237 /* 238 * If nothing is scheduled or we arrived too early, we have 239 * nothing to do. 240 */ 241 start = nsecuptime(); 242 cq->cq_uptime = start; 243 if (TAILQ_EMPTY(&cq->cq_pend)) 244 goto stats; 245 if (cq->cq_uptime < clockqueue_next(cq)) 246 goto rearm; 247 lateness = start - clockqueue_next(cq); 248 249 /* 250 * Dispatch expired events. 251 */ 252 for (;;) { 253 cl = TAILQ_FIRST(&cq->cq_pend); 254 if (cl == NULL) 255 break; 256 if (cq->cq_uptime < cl->cl_expiration) { 257 /* Double-check the time before giving up. */ 258 cq->cq_uptime = nsecuptime(); 259 if (cq->cq_uptime < cl->cl_expiration) 260 break; 261 } 262 263 /* 264 * This clockintr has expired. Initialize a shadow copy 265 * and execute it. 266 */ 267 clockqueue_pend_delete(cq, cl); 268 shadow = &cq->cq_shadow; 269 shadow->cl_expiration = cl->cl_expiration; 270 shadow->cl_arg = cl->cl_arg; 271 shadow->cl_func = cl->cl_func; 272 cq->cq_running = cl; 273 mtx_leave(&cq->cq_mtx); 274 275 shadow->cl_func(shadow, frame, shadow->cl_arg); 276 277 mtx_enter(&cq->cq_mtx); 278 cq->cq_running = NULL; 279 if (ISSET(cl->cl_flags, CLST_IGNORE_SHADOW)) { 280 CLR(cl->cl_flags, CLST_IGNORE_SHADOW); 281 CLR(shadow->cl_flags, CLST_SHADOW_PENDING); 282 } 283 if (ISSET(shadow->cl_flags, CLST_SHADOW_PENDING)) { 284 CLR(shadow->cl_flags, CLST_SHADOW_PENDING); 285 clockqueue_pend_insert(cq, cl, shadow->cl_expiration); 286 } 287 run++; 288 } 289 290 /* 291 * Dispatch complete. 292 */ 293 rearm: 294 /* Rearm the interrupt clock if we have one. */ 295 if (ISSET(cq->cq_flags, CQ_INTRCLOCK)) { 296 if (!TAILQ_EMPTY(&cq->cq_pend)) { 297 intrclock_rearm(&cq->cq_intrclock, 298 clockqueue_next(cq) - cq->cq_uptime); 299 } 300 } 301 stats: 302 /* Update our stats. */ 303 ogen = cq->cq_gen; 304 cq->cq_gen = 0; 305 membar_producer(); 306 cq->cq_stat.cs_dispatched += cq->cq_uptime - start; 307 if (run > 0) { 308 cq->cq_stat.cs_lateness += lateness; 309 cq->cq_stat.cs_prompt++; 310 cq->cq_stat.cs_run += run; 311 } else if (!TAILQ_EMPTY(&cq->cq_pend)) { 312 cq->cq_stat.cs_early++; 313 cq->cq_stat.cs_earliness += clockqueue_next(cq) - cq->cq_uptime; 314 } else 315 cq->cq_stat.cs_spurious++; 316 membar_producer(); 317 cq->cq_gen = MAX(1, ogen + 1); 318 319 mtx_leave(&cq->cq_mtx); 320 321 if (cq->cq_dispatch != 1) 322 panic("%s: unexpected value: %u", __func__, cq->cq_dispatch); 323 cq->cq_dispatch = 0; 324 325 return run > 0; 326 } 327 328 uint64_t 329 clockintr_advance(struct clockintr *cl, uint64_t period) 330 { 331 uint64_t count, expiration; 332 struct clockintr_queue *cq = cl->cl_queue; 333 334 if (cl == &cq->cq_shadow) { 335 count = nsec_advance(&cl->cl_expiration, period, cq->cq_uptime); 336 SET(cl->cl_flags, CLST_SHADOW_PENDING); 337 } else { 338 mtx_enter(&cq->cq_mtx); 339 expiration = cl->cl_expiration; 340 count = nsec_advance(&expiration, period, nsecuptime()); 341 clockintr_schedule_locked(cl, expiration); 342 mtx_leave(&cq->cq_mtx); 343 } 344 return count; 345 } 346 347 uint64_t 348 clockintr_advance_random(struct clockintr *cl, uint64_t min, uint32_t mask) 349 { 350 uint64_t count = 0; 351 struct clockintr_queue *cq = cl->cl_queue; 352 uint32_t off; 353 354 KASSERT(cl == &cq->cq_shadow); 355 356 while (cl->cl_expiration <= cq->cq_uptime) { 357 while ((off = (random() & mask)) == 0) 358 continue; 359 cl->cl_expiration += min + off; 360 count++; 361 } 362 SET(cl->cl_flags, CLST_SHADOW_PENDING); 363 return count; 364 } 365 366 void 367 clockintr_cancel(struct clockintr *cl) 368 { 369 struct clockintr_queue *cq = cl->cl_queue; 370 int was_next; 371 372 if (cl == &cq->cq_shadow) { 373 CLR(cl->cl_flags, CLST_SHADOW_PENDING); 374 return; 375 } 376 377 mtx_enter(&cq->cq_mtx); 378 if (ISSET(cl->cl_flags, CLST_PENDING)) { 379 was_next = cl == TAILQ_FIRST(&cq->cq_pend); 380 clockqueue_pend_delete(cq, cl); 381 if (ISSET(cq->cq_flags, CQ_INTRCLOCK)) { 382 if (was_next && !TAILQ_EMPTY(&cq->cq_pend)) { 383 if (cq == &curcpu()->ci_queue) 384 clockqueue_reset_intrclock(cq); 385 } 386 } 387 } 388 if (cl == cq->cq_running) 389 SET(cl->cl_flags, CLST_IGNORE_SHADOW); 390 mtx_leave(&cq->cq_mtx); 391 } 392 393 struct clockintr * 394 clockintr_establish(struct cpu_info *ci, 395 void (*func)(struct clockintr *, void *, void *), void *arg) 396 { 397 struct clockintr *cl; 398 struct clockintr_queue *cq = &ci->ci_queue; 399 400 cl = malloc(sizeof *cl, M_DEVBUF, M_NOWAIT | M_ZERO); 401 if (cl == NULL) 402 return NULL; 403 cl->cl_arg = arg; 404 cl->cl_func = func; 405 cl->cl_queue = cq; 406 407 mtx_enter(&cq->cq_mtx); 408 TAILQ_INSERT_TAIL(&cq->cq_est, cl, cl_elink); 409 mtx_leave(&cq->cq_mtx); 410 return cl; 411 } 412 413 void 414 clockintr_schedule(struct clockintr *cl, uint64_t expiration) 415 { 416 struct clockintr_queue *cq = cl->cl_queue; 417 418 if (cl == &cq->cq_shadow) { 419 cl->cl_expiration = expiration; 420 SET(cl->cl_flags, CLST_SHADOW_PENDING); 421 } else { 422 mtx_enter(&cq->cq_mtx); 423 clockintr_schedule_locked(cl, expiration); 424 mtx_leave(&cq->cq_mtx); 425 } 426 } 427 428 void 429 clockintr_schedule_locked(struct clockintr *cl, uint64_t expiration) 430 { 431 struct clockintr_queue *cq = cl->cl_queue; 432 433 MUTEX_ASSERT_LOCKED(&cq->cq_mtx); 434 435 if (ISSET(cl->cl_flags, CLST_PENDING)) 436 clockqueue_pend_delete(cq, cl); 437 clockqueue_pend_insert(cq, cl, expiration); 438 if (ISSET(cq->cq_flags, CQ_INTRCLOCK)) { 439 if (cl == TAILQ_FIRST(&cq->cq_pend)) { 440 if (cq == &curcpu()->ci_queue) 441 clockqueue_reset_intrclock(cq); 442 } 443 } 444 if (cl == cq->cq_running) 445 SET(cl->cl_flags, CLST_IGNORE_SHADOW); 446 } 447 448 void 449 clockintr_stagger(struct clockintr *cl, uint64_t period, uint32_t n, 450 uint32_t count) 451 { 452 struct clockintr_queue *cq = cl->cl_queue; 453 454 KASSERT(n < count); 455 456 mtx_enter(&cq->cq_mtx); 457 if (ISSET(cl->cl_flags, CLST_PENDING)) 458 panic("%s: clock interrupt pending", __func__); 459 cl->cl_expiration = period / count * n; 460 mtx_leave(&cq->cq_mtx); 461 } 462 463 void 464 clockintr_hardclock(struct clockintr *cl, void *frame, void *arg) 465 { 466 uint64_t count, i; 467 468 count = clockintr_advance(cl, hardclock_period); 469 for (i = 0; i < count; i++) 470 hardclock(frame); 471 } 472 473 void 474 clockintr_statclock(struct clockintr *cl, void *frame, void *arg) 475 { 476 uint64_t count, i; 477 478 if (ISSET(clockintr_flags, CL_RNDSTAT)) { 479 count = clockintr_advance_random(cl, statclock_min, 480 statclock_mask); 481 } else { 482 count = clockintr_advance(cl, statclock_avg); 483 } 484 for (i = 0; i < count; i++) 485 statclock(frame); 486 } 487 488 void 489 clockqueue_init(struct clockintr_queue *cq) 490 { 491 if (ISSET(cq->cq_flags, CQ_INIT)) 492 return; 493 494 cq->cq_shadow.cl_queue = cq; 495 mtx_init(&cq->cq_mtx, IPL_CLOCK); 496 TAILQ_INIT(&cq->cq_est); 497 TAILQ_INIT(&cq->cq_pend); 498 cq->cq_gen = 1; 499 SET(cq->cq_flags, CQ_INIT); 500 } 501 502 void 503 clockqueue_intrclock_install(struct clockintr_queue *cq, 504 const struct intrclock *ic) 505 { 506 mtx_enter(&cq->cq_mtx); 507 if (!ISSET(cq->cq_flags, CQ_INTRCLOCK)) { 508 cq->cq_intrclock = *ic; 509 SET(cq->cq_flags, CQ_INTRCLOCK); 510 } 511 mtx_leave(&cq->cq_mtx); 512 } 513 514 uint64_t 515 clockqueue_next(const struct clockintr_queue *cq) 516 { 517 MUTEX_ASSERT_LOCKED(&cq->cq_mtx); 518 return TAILQ_FIRST(&cq->cq_pend)->cl_expiration; 519 } 520 521 void 522 clockqueue_pend_delete(struct clockintr_queue *cq, struct clockintr *cl) 523 { 524 MUTEX_ASSERT_LOCKED(&cq->cq_mtx); 525 KASSERT(ISSET(cl->cl_flags, CLST_PENDING)); 526 527 TAILQ_REMOVE(&cq->cq_pend, cl, cl_plink); 528 CLR(cl->cl_flags, CLST_PENDING); 529 } 530 531 532 void 533 clockqueue_pend_insert(struct clockintr_queue *cq, struct clockintr *cl, 534 uint64_t expiration) 535 { 536 struct clockintr *elm; 537 538 MUTEX_ASSERT_LOCKED(&cq->cq_mtx); 539 KASSERT(!ISSET(cl->cl_flags, CLST_PENDING)); 540 541 cl->cl_expiration = expiration; 542 TAILQ_FOREACH(elm, &cq->cq_pend, cl_plink) { 543 if (cl->cl_expiration < elm->cl_expiration) 544 break; 545 } 546 if (elm == NULL) 547 TAILQ_INSERT_TAIL(&cq->cq_pend, cl, cl_plink); 548 else 549 TAILQ_INSERT_BEFORE(elm, cl, cl_plink); 550 SET(cl->cl_flags, CLST_PENDING); 551 } 552 553 void 554 clockqueue_reset_intrclock(struct clockintr_queue *cq) 555 { 556 uint64_t exp, now; 557 558 MUTEX_ASSERT_LOCKED(&cq->cq_mtx); 559 KASSERT(ISSET(cq->cq_flags, CQ_INTRCLOCK)); 560 561 exp = clockqueue_next(cq); 562 now = nsecuptime(); 563 if (now < exp) 564 intrclock_rearm(&cq->cq_intrclock, exp - now); 565 else 566 intrclock_trigger(&cq->cq_intrclock); 567 } 568 569 /* 570 * Advance *next in increments of period until it exceeds now. 571 * Returns the number of increments *next was advanced. 572 * 573 * We check the common cases first to avoid division if possible. 574 * This does no overflow checking. 575 */ 576 uint64_t 577 nsec_advance(uint64_t *next, uint64_t period, uint64_t now) 578 { 579 uint64_t elapsed; 580 581 if (now < *next) 582 return 0; 583 584 if (now < *next + period) { 585 *next += period; 586 return 1; 587 } 588 589 elapsed = (now - *next) / period + 1; 590 *next += period * elapsed; 591 return elapsed; 592 } 593 594 int 595 sysctl_clockintr(int *name, u_int namelen, void *oldp, size_t *oldlenp, 596 void *newp, size_t newlen) 597 { 598 struct clockintr_stat sum, tmp; 599 struct clockintr_queue *cq; 600 struct cpu_info *ci; 601 CPU_INFO_ITERATOR cii; 602 uint32_t gen; 603 604 if (namelen != 1) 605 return ENOTDIR; 606 607 switch (name[0]) { 608 case KERN_CLOCKINTR_STATS: 609 memset(&sum, 0, sizeof sum); 610 CPU_INFO_FOREACH(cii, ci) { 611 cq = &ci->ci_queue; 612 if (!ISSET(cq->cq_flags, CQ_INIT)) 613 continue; 614 do { 615 gen = cq->cq_gen; 616 membar_consumer(); 617 tmp = cq->cq_stat; 618 membar_consumer(); 619 } while (gen == 0 || gen != cq->cq_gen); 620 sum.cs_dispatched += tmp.cs_dispatched; 621 sum.cs_early += tmp.cs_early; 622 sum.cs_earliness += tmp.cs_earliness; 623 sum.cs_lateness += tmp.cs_lateness; 624 sum.cs_prompt += tmp.cs_prompt; 625 sum.cs_run += tmp.cs_run; 626 sum.cs_spurious += tmp.cs_spurious; 627 } 628 return sysctl_rdstruct(oldp, oldlenp, newp, &sum, sizeof sum); 629 default: 630 break; 631 } 632 633 return EINVAL; 634 } 635 636 #ifdef DDB 637 638 #include <machine/db_machdep.h> 639 640 #include <ddb/db_interface.h> 641 #include <ddb/db_output.h> 642 #include <ddb/db_sym.h> 643 644 void db_show_clockintr(const struct clockintr *, const char *, u_int); 645 void db_show_clockintr_cpu(struct cpu_info *); 646 647 void 648 db_show_all_clockintr(db_expr_t addr, int haddr, db_expr_t count, char *modif) 649 { 650 struct timespec now; 651 struct cpu_info *ci; 652 CPU_INFO_ITERATOR cii; 653 654 nanouptime(&now); 655 db_printf("%20s\n", "UPTIME"); 656 db_printf("%10lld.%09ld\n", now.tv_sec, now.tv_nsec); 657 db_printf("\n"); 658 db_printf("%20s %5s %3s %s\n", "EXPIRATION", "STATE", "CPU", "NAME"); 659 CPU_INFO_FOREACH(cii, ci) { 660 if (ISSET(ci->ci_queue.cq_flags, CQ_INIT)) 661 db_show_clockintr_cpu(ci); 662 } 663 } 664 665 void 666 db_show_clockintr_cpu(struct cpu_info *ci) 667 { 668 struct clockintr *elm; 669 struct clockintr_queue *cq = &ci->ci_queue; 670 u_int cpu = CPU_INFO_UNIT(ci); 671 672 if (cq->cq_running != NULL) 673 db_show_clockintr(cq->cq_running, "run", cpu); 674 TAILQ_FOREACH(elm, &cq->cq_pend, cl_plink) 675 db_show_clockintr(elm, "pend", cpu); 676 TAILQ_FOREACH(elm, &cq->cq_est, cl_elink) { 677 if (!ISSET(elm->cl_flags, CLST_PENDING)) 678 db_show_clockintr(elm, "idle", cpu); 679 } 680 } 681 682 void 683 db_show_clockintr(const struct clockintr *cl, const char *state, u_int cpu) 684 { 685 struct timespec ts; 686 char *name; 687 db_expr_t offset; 688 689 NSEC_TO_TIMESPEC(cl->cl_expiration, &ts); 690 db_find_sym_and_offset((vaddr_t)cl->cl_func, &name, &offset); 691 if (name == NULL) 692 name = "?"; 693 db_printf("%10lld.%09ld %5s %3u %s\n", 694 ts.tv_sec, ts.tv_nsec, state, cpu, name); 695 } 696 697 #endif /* DDB */ 698