xref: /openbsd-src/sys/kern/kern_clockintr.c (revision a3464c933063c2d770977c57373583ee9a16e674)
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