xref: /netbsd-src/sys/arch/alpha/alpha/interrupt.c (revision f6b913459269559d1ed5a9801802916ffe1112ba)
1 /* $NetBSD: interrupt.c,v 1.100 2021/11/10 16:53:28 msaitoh Exp $ */
2 
3 /*-
4  * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
34  * All rights reserved.
35  *
36  * Authors: Keith Bostic, Chris G. Demetriou
37  *
38  * Permission to use, copy, modify and distribute this software and
39  * its documentation is hereby granted, provided that both the copyright
40  * notice and this permission notice appear in all copies of the
41  * software, derivative works or modified versions, and any portions
42  * thereof, and that both notices appear in supporting documentation.
43  *
44  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
45  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
46  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
47  *
48  * Carnegie Mellon requests users of this software to return to
49  *
50  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
51  *  School of Computer Science
52  *  Carnegie Mellon University
53  *  Pittsburgh PA 15213-3890
54  *
55  * any improvements or extensions that they make and grant Carnegie the
56  * rights to redistribute these changes.
57  */
58 /*
59  * Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center.
60  * Redistribute and modify at will, leaving only this additional copyright
61  * notice.
62  */
63 
64 #include "opt_multiprocessor.h"
65 
66 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
67 
68 __KERNEL_RCSID(0, "$NetBSD: interrupt.c,v 1.100 2021/11/10 16:53:28 msaitoh Exp $");
69 
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/proc.h>
73 #include <sys/vmmeter.h>
74 #include <sys/sched.h>
75 #include <sys/kernel.h>
76 #include <sys/time.h>
77 #include <sys/intr.h>
78 #include <sys/device.h>
79 #include <sys/cpu.h>
80 #include <sys/atomic.h>
81 
82 #include <machine/cpuvar.h>
83 #include <machine/autoconf.h>
84 #include <machine/reg.h>
85 #include <machine/rpb.h>
86 #include <machine/frame.h>
87 #include <machine/cpuconf.h>
88 #include <machine/alpha.h>
89 
90 /* Protected by cpu_lock */
91 struct scbvec scb_iovectab[SCB_VECTOIDX(SCB_SIZE - SCB_IOVECBASE)]
92 							__read_mostly;
93 
94 static void	scb_stray(void *, u_long);
95 
96 void
scb_init(void)97 scb_init(void)
98 {
99 	u_long i;
100 
101 	for (i = 0; i < SCB_NIOVECS; i++) {
102 		scb_iovectab[i].scb_func = scb_stray;
103 		scb_iovectab[i].scb_arg = NULL;
104 	}
105 }
106 
107 static void
scb_stray(void * arg,u_long vec)108 scb_stray(void *arg, u_long vec)
109 {
110 
111 	printf("WARNING: stray interrupt, vector 0x%lx\n", vec);
112 }
113 
114 void
scb_set(u_long vec,void (* func)(void *,u_long),void * arg)115 scb_set(u_long vec, void (*func)(void *, u_long), void *arg)
116 {
117 	u_long idx;
118 
119 	KASSERT(mutex_owned(&cpu_lock));
120 
121 	if (vec < SCB_IOVECBASE || vec >= SCB_SIZE ||
122 	    (vec & (SCB_VECSIZE - 1)) != 0)
123 		panic("scb_set: bad vector 0x%lx", vec);
124 
125 	idx = SCB_VECTOIDX(vec - SCB_IOVECBASE);
126 
127 	if (scb_iovectab[idx].scb_func != scb_stray)
128 		panic("scb_set: vector 0x%lx already occupied", vec);
129 
130 	scb_iovectab[idx].scb_arg = arg;
131 	alpha_mb();
132 	scb_iovectab[idx].scb_func = func;
133 	alpha_mb();
134 }
135 
136 u_long
scb_alloc(void (* func)(void *,u_long),void * arg)137 scb_alloc(void (*func)(void *, u_long), void *arg)
138 {
139 	u_long vec, idx;
140 
141 	KASSERT(mutex_owned(&cpu_lock));
142 
143 	/*
144 	 * Allocate "downwards", to avoid bumping into
145 	 * interrupts which are likely to be at the lower
146 	 * vector numbers.
147 	 */
148 	for (vec = SCB_SIZE - SCB_VECSIZE;
149 	     vec >= SCB_IOVECBASE; vec -= SCB_VECSIZE) {
150 		idx = SCB_VECTOIDX(vec - SCB_IOVECBASE);
151 		if (scb_iovectab[idx].scb_func == scb_stray) {
152 			scb_iovectab[idx].scb_arg = arg;
153 			alpha_mb();
154 			scb_iovectab[idx].scb_func = func;
155 			alpha_mb();
156 			return (vec);
157 		}
158 	}
159 
160 	return (SCB_ALLOC_FAILED);
161 }
162 
163 void
scb_free(u_long vec)164 scb_free(u_long vec)
165 {
166 	u_long idx;
167 
168 	KASSERT(mutex_owned(&cpu_lock));
169 
170 	if (vec < SCB_IOVECBASE || vec >= SCB_SIZE ||
171 	    (vec & (SCB_VECSIZE - 1)) != 0)
172 		panic("scb_free: bad vector 0x%lx", vec);
173 
174 	idx = SCB_VECTOIDX(vec - SCB_IOVECBASE);
175 
176 	if (scb_iovectab[idx].scb_func == scb_stray)
177 		panic("scb_free: vector 0x%lx is empty", vec);
178 
179 	scb_iovectab[idx].scb_func = scb_stray;
180 	alpha_mb();
181 	scb_iovectab[idx].scb_arg = (void *) vec;
182 	alpha_mb();
183 }
184 
185 void
interrupt(unsigned long a0,unsigned long a1,unsigned long a2,struct trapframe * framep)186 interrupt(unsigned long a0, unsigned long a1, unsigned long a2,
187     struct trapframe *framep)
188 {
189 	struct cpu_info *ci = curcpu();
190 	struct cpu_softc *sc = ci->ci_softc;
191 
192 	switch (a0) {
193 	case ALPHA_INTR_XPROC:	/* interprocessor interrupt */
194 #if defined(MULTIPROCESSOR)
195 		ci->ci_intrdepth++;
196 
197 		alpha_ipi_process(ci, framep);
198 
199 		/*
200 		 * Handle inter-console messages if we're the primary
201 		 * CPU.
202 		 */
203 		if (ci->ci_cpuid == hwrpb->rpb_primary_cpu_id &&
204 		    hwrpb->rpb_txrdy != 0)
205 			cpu_iccb_receive();
206 
207 		ci->ci_intrdepth--;
208 #else
209 		printf("WARNING: received interprocessor interrupt!\n");
210 #endif /* MULTIPROCESSOR */
211 		break;
212 
213 	case ALPHA_INTR_CLOCK:	/* clock interrupt */
214 		/*
215 		 * Rather than simply increment the interrupt depth
216 		 * for the clock interrupt, we add 0x10.  Why?  Because
217 		 * while we only call out a single device interrupt
218 		 * level, technically the architecture specification
219 		 * supports two, meaning we could have intrdepth > 1
220 		 * just for device interrupts.
221 		 *
222 		 * Adding 0x10 here means that cpu_intr_p() can check
223 		 * for "intrdepth != 0" for "in interrupt context" and
224 		 * CLKF_INTR() can check "(intrdepth & 0xf) != 0" for
225 		 * "was processing interrupts when the clock interrupt
226 		 * happened".
227 		 */
228 		ci->ci_intrdepth += 0x10;
229 		sc->sc_evcnt_clock.ev_count++;
230 		ci->ci_data.cpu_nintr++;
231 		if (platform.clockintr) {
232 			/*
233 			 * Call hardclock().  This will also call
234 			 * statclock(). On the primary CPU, it
235 			 * will also deal with time-of-day stuff.
236 			 */
237 			(*platform.clockintr)((struct clockframe *)framep);
238 
239 #if defined(MULTIPROCESSOR)
240 			if (alpha_use_cctr) {
241 				cc_hardclock(ci);
242 			}
243 #endif /* MULTIPROCESSOR */
244 
245 			/*
246 			 * If it's time to call the scheduler clock,
247 			 * do so.
248 			 */
249 			if ((++ci->ci_schedstate.spc_schedticks & 0x3f) == 0 &&
250 			    schedhz != 0)
251 				schedclock(ci->ci_curlwp);
252 		}
253 		ci->ci_intrdepth -= 0x10;
254 		break;
255 
256 	case ALPHA_INTR_ERROR:	/* Machine Check or Correctable Error */
257 		ci->ci_intrdepth++;
258 		a0 = alpha_pal_rdmces();
259 		if (platform.mcheck_handler != NULL &&
260 		    (void *)framep->tf_regs[FRAME_PC] != XentArith)
261 			(*platform.mcheck_handler)(a0, framep, a1, a2);
262 		else
263 			machine_check(a0, framep, a1, a2);
264 		ci->ci_intrdepth--;
265 		break;
266 
267 	case ALPHA_INTR_DEVICE:	/* I/O device interrupt */
268 	    {
269 		const int idx = SCB_VECTOIDX(a1 - SCB_IOVECBASE);
270 
271 		KDASSERT(a1 >= SCB_IOVECBASE && a1 < SCB_SIZE);
272 
273 		atomic_inc_ulong(&sc->sc_evcnt_device.ev_count);
274 		ci->ci_intrdepth++;
275 
276 		ci->ci_data.cpu_nintr++;
277 
278 		struct scbvec * const scb = &scb_iovectab[idx];
279 		(*scb->scb_func)(scb->scb_arg, a1);
280 
281 		ci->ci_intrdepth--;
282 		break;
283 	    }
284 
285 	case ALPHA_INTR_PERF:	/* performance counter interrupt */
286 		printf("WARNING: received performance counter interrupt!\n");
287 		break;
288 
289 	case ALPHA_INTR_PASSIVE:
290 #if 0
291 		printf("WARNING: received passive release interrupt vec "
292 		    "0x%lx\n", a1);
293 #endif
294 		break;
295 
296 	default:
297 		printf("unexpected interrupt: type 0x%lx vec 0x%lx "
298 		    "a2 0x%lx"
299 #if defined(MULTIPROCESSOR)
300 		    " cpu %lu"
301 #endif
302 		    "\n", a0, a1, a2
303 #if defined(MULTIPROCESSOR)
304 		    , ci->ci_cpuid
305 #endif
306 		    );
307 		panic("interrupt");
308 		/* NOTREACHED */
309 	}
310 }
311 
312 void
machine_check(unsigned long mces,struct trapframe * framep,unsigned long vector,unsigned long param)313 machine_check(unsigned long mces, struct trapframe *framep,
314     unsigned long vector, unsigned long param)
315 {
316 	const char *type;
317 	struct mchkinfo *mcp;
318 	static struct timeval ratelimit[1];
319 
320 	mcp = &curcpu()->ci_mcinfo;
321 	/* Make sure it's an error we know about. */
322 	if ((mces & (ALPHA_MCES_MIP|ALPHA_MCES_SCE|ALPHA_MCES_PCE)) == 0) {
323 		type = "fatal machine check or error (unknown type)";
324 		goto fatal;
325 	}
326 
327 	/* Machine checks. */
328 	if (mces & ALPHA_MCES_MIP) {
329 		/* If we weren't expecting it, then we punt. */
330 		if (!mcp->mc_expected) {
331 			type = "unexpected machine check";
332 			goto fatal;
333 		}
334 		mcp->mc_expected = 0;
335 		mcp->mc_received = 1;
336 	}
337 
338 	/* System correctable errors. */
339 	if (mces & ALPHA_MCES_SCE)
340 		printf("Warning: received system correctable error.\n");
341 
342 	/* Processor correctable errors. */
343 	if (mces & ALPHA_MCES_PCE)
344 		printf("Warning: received processor correctable error.\n");
345 
346 	/* Clear pending machine checks and correctable errors */
347 	alpha_pal_wrmces(mces);
348 	return;
349 
350 fatal:
351 	alpha_pal_wrmces(mces);
352 	if ((void *)framep->tf_regs[FRAME_PC] == XentArith) {
353 		rlprintf(ratelimit, "Stray machine check\n");
354 		return;
355 	}
356 
357 	printf("\n");
358 	printf("%s:\n", type);
359 	printf("\n");
360 	printf("    mces    = 0x%lx\n", mces);
361 	printf("    vector  = 0x%lx\n", vector);
362 	printf("    param   = 0x%lx\n", param);
363 	printf("    pc      = 0x%lx\n", framep->tf_regs[FRAME_PC]);
364 	printf("    ra      = 0x%lx\n", framep->tf_regs[FRAME_RA]);
365 	printf("    code    = 0x%lx\n", *(unsigned long *)(param + 0x10));
366 	printf("    curlwp = %p\n", curlwp);
367 	if (curlwp != NULL)
368 		printf("        pid = %d.%d, comm = %s\n",
369 		    curproc->p_pid, curlwp->l_lid,
370 		    curproc->p_comm);
371 	printf("\n");
372 	panic("machine check");
373 }
374 
375 int
badaddr(void * addr,size_t size)376 badaddr(void *addr, size_t size)
377 {
378 
379 	return (badaddr_read(addr, size, NULL));
380 }
381 
382 int
badaddr_read(void * addr,size_t size,void * rptr)383 badaddr_read(void *addr, size_t size, void *rptr)
384 {
385 	lwp_t * const l = curlwp;
386 	KPREEMPT_DISABLE(l);
387 
388 	struct mchkinfo *mcp = &curcpu()->ci_mcinfo;
389 	long rcpt;
390 	int rv;
391 
392 	/* Get rid of any stale machine checks that have been waiting.  */
393 	alpha_pal_draina();
394 
395 	/* Tell the trap code to expect a machine check. */
396 	mcp->mc_received = 0;
397 	mcp->mc_expected = 1;
398 
399 	/* Read from the test address, and make sure the read happens. */
400 	alpha_mb();
401 	switch (size) {
402 	case sizeof (uint8_t):
403 		rcpt = *(volatile uint8_t *)addr;
404 		break;
405 
406 	case sizeof (uint16_t):
407 		rcpt = *(volatile uint16_t *)addr;
408 		break;
409 
410 	case sizeof (uint32_t):
411 		rcpt = *(volatile uint32_t *)addr;
412 		break;
413 
414 	case sizeof (uint64_t):
415 		rcpt = *(volatile uint64_t *)addr;
416 		break;
417 
418 	default:
419 		panic("badaddr: invalid size (%ld)", size);
420 	}
421 	alpha_mb();
422 	alpha_mb();	/* MAGIC ON SOME SYSTEMS */
423 
424 	/* Make sure we took the machine check, if we caused one. */
425 	alpha_pal_draina();
426 
427 	/* disallow further machine checks */
428 	mcp->mc_expected = 0;
429 
430 	rv = mcp->mc_received;
431 	mcp->mc_received = 0;
432 
433 	/*
434 	 * And copy back read results (if no fault occurred).
435 	 */
436 	if (rptr && rv == 0) {
437 		switch (size) {
438 		case sizeof (uint8_t):
439 			*(volatile uint8_t *)rptr = rcpt;
440 			break;
441 
442 		case sizeof (uint16_t):
443 			*(volatile uint16_t *)rptr = rcpt;
444 			break;
445 
446 		case sizeof (uint32_t):
447 			*(volatile uint32_t *)rptr = rcpt;
448 			break;
449 
450 		case sizeof (uint64_t):
451 			*(volatile uint64_t *)rptr = rcpt;
452 			break;
453 		}
454 	}
455 
456 	KPREEMPT_ENABLE(l);
457 
458 	/* Return non-zero (i.e. true) if it's a bad address. */
459 	return (rv);
460 }
461 
462 /*
463  * Fast soft interrupt support.
464  */
465 
466 #define	SOFTINT_TO_IPL(si)						\
467 	(ALPHA_PSL_IPL_SOFT_LO + ((ALPHA_IPL2_SOFTINTS >> (si)) & 1))
468 
469 #define	SOFTINTS_ELIGIBLE(ipl)						\
470 	((ALPHA_ALL_SOFTINTS << ((ipl) << 1)) & ALPHA_ALL_SOFTINTS)
471 
472 /* Validate some assumptions the code makes. */
473 __CTASSERT(SOFTINT_TO_IPL(SOFTINT_CLOCK) == ALPHA_PSL_IPL_SOFT_LO);
474 __CTASSERT(SOFTINT_TO_IPL(SOFTINT_BIO) == ALPHA_PSL_IPL_SOFT_LO);
475 __CTASSERT(SOFTINT_TO_IPL(SOFTINT_NET) == ALPHA_PSL_IPL_SOFT_HI);
476 __CTASSERT(SOFTINT_TO_IPL(SOFTINT_SERIAL) == ALPHA_PSL_IPL_SOFT_HI);
477 
478 __CTASSERT(IPL_SOFTCLOCK == ALPHA_PSL_IPL_SOFT_LO);
479 __CTASSERT(IPL_SOFTBIO == ALPHA_PSL_IPL_SOFT_LO);
480 __CTASSERT(IPL_SOFTNET == ALPHA_PSL_IPL_SOFT_HI);
481 __CTASSERT(IPL_SOFTSERIAL == ALPHA_PSL_IPL_SOFT_HI);
482 
483 __CTASSERT(SOFTINT_CLOCK_MASK & 0x3);
484 __CTASSERT(SOFTINT_BIO_MASK & 0x3);
485 __CTASSERT(SOFTINT_NET_MASK & 0xc);
486 __CTASSERT(SOFTINT_SERIAL_MASK & 0xc);
487 __CTASSERT(SOFTINT_COUNT == 4);
488 
489 __CTASSERT((ALPHA_ALL_SOFTINTS & ~0xfUL) == 0);
490 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_NONE) == ALPHA_ALL_SOFTINTS);
491 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTCLOCK) == ALPHA_IPL2_SOFTINTS);
492 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTBIO) == ALPHA_IPL2_SOFTINTS);
493 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTNET) == 0);
494 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTSERIAL) == 0);
495 
496 /*
497  * softint_trigger:
498  *
499  *	Trigger a soft interrupt.
500  */
501 void
softint_trigger(uintptr_t const machdep)502 softint_trigger(uintptr_t const machdep)
503 {
504 	/* No need for an atomic; called at splhigh(). */
505 	KASSERT(alpha_pal_rdps() == ALPHA_PSL_IPL_HIGH);
506 	curcpu()->ci_ssir |= machdep;
507 }
508 
509 /*
510  * softint_init_md:
511  *
512  *	Machine-dependent initialization for a fast soft interrupt thread.
513  */
514 void
softint_init_md(lwp_t * const l,u_int const level,uintptr_t * const machdep)515 softint_init_md(lwp_t * const l, u_int const level, uintptr_t * const machdep)
516 {
517 	lwp_t ** lp = &l->l_cpu->ci_silwps[level];
518 	KASSERT(*lp == NULL || *lp == l);
519 	*lp = l;
520 
521 	const uintptr_t si_bit = __BIT(level);
522 	KASSERT(si_bit & ALPHA_ALL_SOFTINTS);
523 	*machdep = si_bit;
524 }
525 
526 /*
527  * Helper macro.
528  *
529  * Dispatch a softint and then restart the loop so that higher
530  * priority softints are always done first.
531  */
532 #define	DOSOFTINT(level)						\
533 	if (ssir & SOFTINT_##level##_MASK) {				\
534 		ci->ci_ssir &= ~SOFTINT_##level##_MASK;			\
535 		alpha_softint_switchto(l, IPL_SOFT##level,		\
536 		    ci->ci_silwps[SOFTINT_##level]);			\
537 		KASSERT(alpha_pal_rdps() == ALPHA_PSL_IPL_HIGH);	\
538 		continue;						\
539 	}								\
540 
541 /*
542  * alpha_softint_dispatch:
543  *
544  *	Process pending soft interrupts that are eligible to run
545  *	at the specified new IPL.  Must be called at splhigh().
546  */
547 void
alpha_softint_dispatch(int const ipl)548 alpha_softint_dispatch(int const ipl)
549 {
550 	struct lwp * const l = curlwp;
551 	struct cpu_info * const ci = l->l_cpu;
552 	unsigned long ssir;
553 	const unsigned long eligible = SOFTINTS_ELIGIBLE(ipl);
554 
555 	KASSERT(alpha_pal_rdps() == ALPHA_PSL_IPL_HIGH);
556 
557 	for (;;) {
558 		ssir = ci->ci_ssir & eligible;
559 		if (ssir == 0)
560 			break;
561 
562 		DOSOFTINT(SERIAL);
563 		DOSOFTINT(NET);
564 		DOSOFTINT(BIO);
565 		DOSOFTINT(CLOCK);
566 	}
567 }
568 
569 /*
570  * spllower:
571  *
572  *	Lower interrupt priority.  May need to check for software
573  *	interrupts.
574  */
575 void
spllower(int const ipl)576 spllower(int const ipl)
577 {
578 
579 	if (ipl < ALPHA_PSL_IPL_SOFT_HI && curcpu()->ci_ssir) {
580 		(void) alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
581 		alpha_softint_dispatch(ipl);
582 	}
583 	(void) alpha_pal_swpipl(ipl);
584 }
585 
586 /*
587  * cpu_intr_p:
588  *
589  *	Return non-zero if executing in interrupt context.
590  */
591 bool
cpu_intr_p(void)592 cpu_intr_p(void)
593 {
594 
595 	return curcpu()->ci_intrdepth != 0;
596 }
597 
598 void	(*alpha_intr_redistribute)(void);
599 
600 /*
601  * cpu_intr_redistribute:
602  *
603  *	Redistribute interrupts amongst CPUs eligible to handle them.
604  */
605 void
cpu_intr_redistribute(void)606 cpu_intr_redistribute(void)
607 {
608 	if (alpha_intr_redistribute != NULL)
609 		(*alpha_intr_redistribute)();
610 }
611 
612 /*
613  * cpu_intr_count:
614  *
615  *	Return the number of device interrupts this CPU handles.
616  */
617 unsigned int
cpu_intr_count(struct cpu_info * const ci)618 cpu_intr_count(struct cpu_info * const ci)
619 {
620 	return ci->ci_nintrhand;
621 }
622 
623 /*
624  * Security sensitive rate limiting printf
625  */
626 void
rlprintf(struct timeval * t,const char * fmt,...)627 rlprintf(struct timeval *t, const char *fmt, ...)
628 {
629 	va_list ap;
630 	static const struct timeval msgperiod[1] = {{ 5, 0 }};
631 
632 	if (!ratecheck(t, msgperiod))
633 		return;
634 
635 	va_start(ap, fmt);
636 	vprintf(fmt, ap);
637 	va_end(ap);
638 }
639