xref: /illumos-gate/usr/src/uts/common/io/avintr.c (revision 64cfc8ed0c5da83f827c424588bfe709e3fb5a7a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Autovectored Interrupt Configuration and Deconfiguration
30  */
31 
32 #include <sys/param.h>
33 #include <sys/cmn_err.h>
34 #include <sys/trap.h>
35 #include <sys/t_lock.h>
36 #include <sys/avintr.h>
37 #include <sys/kmem.h>
38 #include <sys/machlock.h>
39 #include <sys/systm.h>
40 #include <sys/machsystm.h>
41 #include <sys/sunddi.h>
42 #include <sys/x_call.h>
43 #include <sys/cpuvar.h>
44 #include <sys/atomic.h>
45 #include <sys/smp_impldefs.h>
46 #include <sys/sdt.h>
47 #include <sys/stack.h>
48 #include <sys/ddi_impldefs.h>
49 #ifdef __xpv
50 #include <sys/evtchn_impl.h>
51 #endif
52 
53 typedef struct av_softinfo {
54 	cpuset_t	av_pending;	/* pending bitmasks */
55 } av_softinfo_t;
56 
57 static void insert_av(void *intr_id, struct av_head *vectp, avfunc f,
58 	caddr_t arg1, caddr_t arg2, uint64_t *ticksp, int pri_level,
59 	dev_info_t *dip);
60 static void remove_av(void *intr_id, struct av_head *vectp, avfunc f,
61 	int pri_level, int vect);
62 
63 /*
64  * Arrange for a driver to be called when a particular
65  * auto-vectored interrupt occurs.
66  * NOTE: if a device can generate interrupts on more than
67  * one level, or if a driver services devices that interrupt
68  * on more than one level, then the driver should install
69  * itself on each of those levels.
70  */
71 static char badsoft[] =
72 	"add_avintr: bad soft interrupt level %d for driver '%s'\n";
73 static char multilevel[] =
74 	"!IRQ%d is being shared by drivers with different interrupt levels.\n"
75 	"This may result in reduced system performance.";
76 static char multilevel2[] =
77 	"Cannot register interrupt for '%s' device at IPL %d because it\n"
78 	"conflicts with another device using the same vector %d with an IPL\n"
79 	"of %d. Reconfigure the conflicting devices to use different vectors.";
80 
81 #ifdef __xpv
82 #define	MAX_VECT	NR_IRQS
83 #else
84 #define	MAX_VECT	256
85 #endif
86 
87 struct autovec *nmivect = NULL;
88 struct av_head autovect[MAX_VECT];
89 struct av_head softvect[LOCK_LEVEL + 1];
90 kmutex_t av_lock;
91 /*
92  * These are software interrupt handlers dedicated to ddi timer.
93  * The interrupt levels up to 10 are supported, but high interrupts
94  * must not be used there.
95  */
96 ddi_softint_hdl_impl_t softlevel_hdl[DDI_IPL_10] = {
97 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 1 */
98 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 2 */
99 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 3 */
100 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 4 */
101 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 5 */
102 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 6 */
103 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 7 */
104 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 8 */
105 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 9 */
106 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL}, /* level 10 */
107 };
108 ddi_softint_hdl_impl_t softlevel1_hdl =
109 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL};
110 
111 /*
112  * clear/check softint pending flag corresponding for
113  * the current CPU
114  */
115 void
116 av_clear_softint_pending(av_softinfo_t *infop)
117 {
118 	CPUSET_ATOMIC_DEL(infop->av_pending, CPU->cpu_seqid);
119 }
120 
121 boolean_t
122 av_check_softint_pending(av_softinfo_t *infop, boolean_t check_all)
123 {
124 	if (check_all)
125 		return (!CPUSET_ISNULL(infop->av_pending));
126 	else
127 		return (CPU_IN_SET(infop->av_pending, CPU->cpu_seqid) != 0);
128 }
129 
130 /*
131  * This is the wrapper function which is generally used to set a softint
132  * pending
133  */
134 void
135 av_set_softint_pending(int pri, av_softinfo_t *infop)
136 {
137 	kdi_av_set_softint_pending(pri, infop);
138 }
139 
140 /*
141  * This is kmdb's private entry point to setsoftint called from kdi_siron
142  * It first sets our av softint pending bit for the current CPU,
143  * then it sets the CPU softint pending bit for pri.
144  */
145 void
146 kdi_av_set_softint_pending(int pri, av_softinfo_t *infop)
147 {
148 	CPUSET_ATOMIC_ADD(infop->av_pending, CPU->cpu_seqid);
149 
150 	atomic_or_32((uint32_t *)&CPU->cpu_softinfo.st_pending, 1 << pri);
151 }
152 
153 /*
154  * register nmi interrupt routine. The first arg is used only to order
155  * various nmi interrupt service routines in the chain. Higher lvls will
156  * be called first
157  */
158 int
159 add_nmintr(int lvl, avfunc nmintr, char *name, caddr_t arg)
160 {
161 	struct autovec  *mem;
162 	struct autovec *p, *prev = NULL;
163 
164 	if (nmintr == NULL) {
165 		printf("Attempt to add null vect for %s on nmi\n", name);
166 		return (0);
167 
168 	}
169 
170 	mem = kmem_zalloc(sizeof (struct autovec), KM_SLEEP);
171 	mem->av_vector = nmintr;
172 	mem->av_intarg1 = arg;
173 	mem->av_intarg2 = NULL;
174 	mem->av_intr_id = NULL;
175 	mem->av_prilevel = lvl;
176 	mem->av_dip = NULL;
177 	mem->av_link = NULL;
178 
179 	mutex_enter(&av_lock);
180 
181 	if (!nmivect) {
182 		nmivect = mem;
183 		mutex_exit(&av_lock);
184 		return (1);
185 	}
186 	/* find where it goes in list */
187 	for (p = nmivect; p != NULL; p = p->av_link) {
188 		if (p->av_vector == nmintr && p->av_intarg1 == arg) {
189 			/*
190 			 * already in list
191 			 * So? Somebody added the same interrupt twice.
192 			 */
193 			cmn_err(CE_WARN, "Driver already registered '%s'",
194 			    name);
195 			kmem_free(mem, sizeof (struct autovec));
196 			mutex_exit(&av_lock);
197 			return (0);
198 		}
199 		if (p->av_prilevel < lvl) {
200 			if (p == nmivect) {   /* it's at head of list */
201 				mem->av_link = p;
202 				nmivect = mem;
203 			} else {
204 				mem->av_link = p;
205 				prev->av_link = mem;
206 			}
207 			mutex_exit(&av_lock);
208 			return (1);
209 		}
210 		prev = p;
211 
212 	}
213 	/* didn't find it, add it to the end */
214 	prev->av_link = mem;
215 	mutex_exit(&av_lock);
216 	return (1);
217 
218 }
219 
220 /*
221  * register a hardware interrupt handler.
222  */
223 int
224 add_avintr(void *intr_id, int lvl, avfunc xxintr, char *name, int vect,
225     caddr_t arg1, caddr_t arg2, uint64_t *ticksp, dev_info_t *dip)
226 {
227 	struct av_head *vecp = (struct av_head *)0;
228 	avfunc f;
229 	int s, vectindex;			/* save old spl value */
230 	ushort_t hi_pri;
231 
232 	if ((f = xxintr) == NULL) {
233 		printf("Attempt to add null vect for %s on vector %d\n",
234 		    name, vect);
235 		return (0);
236 
237 	}
238 	vectindex = vect % MAX_VECT;
239 
240 	vecp = &autovect[vectindex];
241 
242 	/*
243 	 * "hi_pri == 0" implies all entries on list are "unused",
244 	 * which means that it's OK to just insert this one.
245 	 */
246 	hi_pri = vecp->avh_hi_pri;
247 	if (vecp->avh_link && (hi_pri != 0)) {
248 		if (((hi_pri > LOCK_LEVEL) && (lvl < LOCK_LEVEL)) ||
249 		    ((hi_pri < LOCK_LEVEL) && (lvl > LOCK_LEVEL))) {
250 			cmn_err(CE_WARN, multilevel2, name, lvl, vect,
251 			    hi_pri);
252 			return (0);
253 		}
254 		if ((vecp->avh_lo_pri != lvl) || (hi_pri != lvl))
255 			cmn_err(CE_NOTE, multilevel, vect);
256 	}
257 
258 	insert_av(intr_id, vecp, f, arg1, arg2, ticksp, lvl, dip);
259 	s = splhi();
260 	/*
261 	 * do what ever machine specific things are necessary
262 	 * to set priority level (e.g. set picmasks)
263 	 */
264 	mutex_enter(&av_lock);
265 	(*addspl)(vect, lvl, vecp->avh_lo_pri, vecp->avh_hi_pri);
266 	mutex_exit(&av_lock);
267 	splx(s);
268 	return (1);
269 
270 }
271 
272 void
273 update_avsoftintr_args(void *intr_id, int lvl, caddr_t arg2)
274 {
275 	struct autovec *p;
276 	struct autovec *target = NULL;
277 	struct av_head *vectp = (struct av_head *)&softvect[lvl];
278 
279 	for (p = vectp->avh_link; p && p->av_vector; p = p->av_link) {
280 		if (p->av_intr_id == intr_id) {
281 			target = p;
282 			break;
283 		}
284 	}
285 
286 	if (target == NULL)
287 		return;
288 	target->av_intarg2 = arg2;
289 }
290 
291 /*
292  * Register a software interrupt handler
293  */
294 int
295 add_avsoftintr(void *intr_id, int lvl, avfunc xxintr, char *name,
296     caddr_t arg1, caddr_t arg2)
297 {
298 	int slvl;
299 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)intr_id;
300 
301 	if ((slvl = slvltovect(lvl)) != -1)
302 		return (add_avintr(intr_id, lvl, xxintr,
303 		    name, slvl, arg1, arg2, NULL, NULL));
304 
305 	if (intr_id == NULL) {
306 		printf("Attempt to add null intr_id for %s on level %d\n",
307 		    name, lvl);
308 		return (0);
309 	}
310 
311 	if (xxintr == NULL) {
312 		printf("Attempt to add null handler for %s on level %d\n",
313 		    name, lvl);
314 		return (0);
315 	}
316 
317 	if (lvl <= 0 || lvl > LOCK_LEVEL) {
318 		printf(badsoft, lvl, name);
319 		return (0);
320 	}
321 
322 	if (hdlp->ih_pending == NULL) {
323 		hdlp->ih_pending =
324 		    kmem_zalloc(sizeof (av_softinfo_t), KM_SLEEP);
325 	}
326 
327 	insert_av(intr_id, &softvect[lvl], xxintr, arg1, arg2, NULL, lvl, NULL);
328 
329 	return (1);
330 }
331 
332 /* insert an interrupt vector into chain */
333 static void
334 insert_av(void *intr_id, struct av_head *vectp, avfunc f, caddr_t arg1,
335     caddr_t arg2, uint64_t *ticksp, int pri_level, dev_info_t *dip)
336 {
337 	/*
338 	 * Protect rewrites of the list
339 	 */
340 	struct autovec *p, *mem;
341 
342 	mem = kmem_zalloc(sizeof (struct autovec), KM_SLEEP);
343 	mem->av_vector = f;
344 	mem->av_intarg1 = arg1;
345 	mem->av_intarg2 = arg2;
346 	mem->av_ticksp = ticksp;
347 	mem->av_intr_id = intr_id;
348 	mem->av_prilevel = pri_level;
349 	mem->av_dip = dip;
350 	mem->av_link = NULL;
351 
352 	mutex_enter(&av_lock);
353 
354 	if (vectp->avh_link == NULL) {	/* Nothing on list - put it at head */
355 		vectp->avh_link = mem;
356 		vectp->avh_hi_pri = vectp->avh_lo_pri = (ushort_t)pri_level;
357 
358 		mutex_exit(&av_lock);
359 		return;
360 	}
361 
362 	/* find where it goes in list */
363 	for (p = vectp->avh_link; p != NULL; p = p->av_link) {
364 		if (p->av_vector == NULL) {	/* freed struct available */
365 			p->av_intarg1 = arg1;
366 			p->av_intarg2 = arg2;
367 			p->av_ticksp = ticksp;
368 			p->av_intr_id = intr_id;
369 			p->av_prilevel = pri_level;
370 			p->av_dip = dip;
371 			if (pri_level > (int)vectp->avh_hi_pri) {
372 				vectp->avh_hi_pri = (ushort_t)pri_level;
373 			}
374 			if (pri_level < (int)vectp->avh_lo_pri) {
375 				vectp->avh_lo_pri = (ushort_t)pri_level;
376 			}
377 			/*
378 			 * To prevent calling service routine before args
379 			 * and ticksp are ready fill in vector last.
380 			 */
381 			p->av_vector = f;
382 			mutex_exit(&av_lock);
383 			kmem_free(mem, sizeof (struct autovec));
384 			return;
385 		}
386 	}
387 	/* insert new intpt at beginning of chain */
388 	mem->av_link = vectp->avh_link;
389 	vectp->avh_link = mem;
390 	if (pri_level > (int)vectp->avh_hi_pri) {
391 		vectp->avh_hi_pri = (ushort_t)pri_level;
392 	}
393 	if (pri_level < (int)vectp->avh_lo_pri) {
394 		vectp->avh_lo_pri = (ushort_t)pri_level;
395 	}
396 	mutex_exit(&av_lock);
397 }
398 
399 static int
400 av_rem_softintr(void *intr_id, int lvl, avfunc xxintr, boolean_t rem_softinfo)
401 {
402 	struct av_head *vecp = (struct av_head *)0;
403 	int slvl;
404 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)intr_id;
405 	av_softinfo_t *infop = (av_softinfo_t *)hdlp->ih_pending;
406 
407 	if (xxintr == NULL)
408 		return (0);
409 
410 	if ((slvl = slvltovect(lvl)) != -1) {
411 		rem_avintr(intr_id, lvl, xxintr, slvl);
412 		return (1);
413 	}
414 
415 	if (lvl <= 0 && lvl >= LOCK_LEVEL) {
416 		return (0);
417 	}
418 	vecp = &softvect[lvl];
419 	remove_av(intr_id, vecp, xxintr, lvl, 0);
420 
421 	if (rem_softinfo) {
422 		kmem_free(infop, sizeof (av_softinfo_t));
423 		hdlp->ih_pending = NULL;
424 	}
425 
426 	return (1);
427 }
428 
429 int
430 av_softint_movepri(void *intr_id, int old_lvl)
431 {
432 	int ret;
433 	ddi_softint_hdl_impl_t	*hdlp = (ddi_softint_hdl_impl_t *)intr_id;
434 
435 	ret = add_avsoftintr(intr_id, hdlp->ih_pri, hdlp->ih_cb_func,
436 	    DEVI(hdlp->ih_dip)->devi_name, hdlp->ih_cb_arg1, hdlp->ih_cb_arg2);
437 
438 	if (ret) {
439 		(void) av_rem_softintr(intr_id, old_lvl, hdlp->ih_cb_func,
440 		    B_FALSE);
441 	}
442 
443 	return (ret);
444 }
445 
446 /*
447  * Remove a driver from the autovector list.
448  */
449 int
450 rem_avsoftintr(void *intr_id, int lvl, avfunc xxintr)
451 {
452 	return (av_rem_softintr(intr_id, lvl, xxintr, B_TRUE));
453 }
454 
455 void
456 rem_avintr(void *intr_id, int lvl, avfunc xxintr, int vect)
457 {
458 	struct av_head *vecp = (struct av_head *)0;
459 	avfunc f;
460 	int s, vectindex;			/* save old spl value */
461 
462 	if ((f = xxintr) == NULL)
463 		return;
464 
465 	vectindex = vect % MAX_VECT;
466 	vecp = &autovect[vectindex];
467 	remove_av(intr_id, vecp, f, lvl, vect);
468 	s = splhi();
469 	mutex_enter(&av_lock);
470 	(*delspl)(vect, lvl, vecp->avh_lo_pri, vecp->avh_hi_pri);
471 	mutex_exit(&av_lock);
472 	splx(s);
473 }
474 
475 
476 /*
477  * After having made a change to an autovector list, wait until we have
478  * seen each cpu not executing an interrupt at that level--so we know our
479  * change has taken effect completely (no old state in registers, etc).
480  */
481 void
482 wait_till_seen(int ipl)
483 {
484 	int cpu_in_chain, cix;
485 	struct cpu *cpup;
486 	cpuset_t cpus_to_check;
487 
488 	CPUSET_ALL(cpus_to_check);
489 	do {
490 		cpu_in_chain = 0;
491 		for (cix = 0; cix < NCPU; cix++) {
492 			cpup = cpu[cix];
493 			if (cpup != NULL && CPU_IN_SET(cpus_to_check, cix)) {
494 				if (intr_active(cpup, ipl)) {
495 					cpu_in_chain = 1;
496 				} else {
497 					CPUSET_DEL(cpus_to_check, cix);
498 				}
499 			}
500 		}
501 	} while (cpu_in_chain);
502 }
503 
504 static uint64_t dummy_tick;
505 
506 /* remove an interrupt vector from the chain */
507 static void
508 remove_av(void *intr_id, struct av_head *vectp, avfunc f, int pri_level,
509 	int vect)
510 {
511 	struct autovec *p, *target;
512 	int	lo_pri, hi_pri;
513 	int	ipl;
514 	/*
515 	 * Protect rewrites of the list
516 	 */
517 	target = NULL;
518 
519 	mutex_enter(&av_lock);
520 	ipl = pri_level;
521 	lo_pri = MAXIPL;
522 	hi_pri = 0;
523 	for (p = vectp->avh_link; p; p = p->av_link) {
524 		if ((p->av_vector == f) && (p->av_intr_id == intr_id)) {
525 			/* found the handler */
526 			target = p;
527 			continue;
528 		}
529 		if (p->av_vector != NULL) {
530 			if (p->av_prilevel > hi_pri)
531 				hi_pri = p->av_prilevel;
532 			if (p->av_prilevel < lo_pri)
533 				lo_pri = p->av_prilevel;
534 		}
535 	}
536 	if (ipl < hi_pri)
537 		ipl = hi_pri;
538 	if (target == NULL) {	/* not found */
539 		printf("Couldn't remove function %p at %d, %d\n",
540 		    (void *)f, vect, pri_level);
541 		mutex_exit(&av_lock);
542 		return;
543 	}
544 
545 	/*
546 	 * This drops the handler from the chain, it can no longer be called.
547 	 * However, there is no guarantee that the handler is not currently
548 	 * still executing.
549 	 */
550 	target->av_vector = NULL;
551 	/*
552 	 * There is a race where we could be just about to pick up the ticksp
553 	 * pointer to increment it after returning from the service routine
554 	 * in av_dispatch_autovect.  Rather than NULL it out let's just point
555 	 * it off to something safe so that any final tick update attempt
556 	 * won't fault.
557 	 */
558 	target->av_ticksp = &dummy_tick;
559 	wait_till_seen(ipl);
560 
561 	if (lo_pri > hi_pri) {	/* the chain is now empty */
562 		/* Leave the unused entries here for probable future use */
563 		vectp->avh_lo_pri = MAXIPL;
564 		vectp->avh_hi_pri = 0;
565 	} else {
566 		if ((int)vectp->avh_lo_pri < lo_pri)
567 			vectp->avh_lo_pri = (ushort_t)lo_pri;
568 		if ((int)vectp->avh_hi_pri > hi_pri)
569 			vectp->avh_hi_pri = (ushort_t)hi_pri;
570 	}
571 	mutex_exit(&av_lock);
572 	wait_till_seen(ipl);
573 }
574 
575 /*
576  * kmdb uses siron (and thus setsoftint) while the world is stopped in order to
577  * inform its driver component that there's work to be done.  We need to keep
578  * DTrace from instrumenting kmdb's siron and setsoftint.  We duplicate siron,
579  * giving kmdb's version a kdi prefix to keep DTrace at bay.   We also
580  * provide a version of the various setsoftint functions available for kmdb to
581  * use using a kdi_ prefix while the main *setsoftint() functionality is
582  * implemented as a wrapper.  This allows tracing, while still providing a
583  * way for kmdb to sneak in unmolested.
584  */
585 void
586 kdi_siron(void)
587 {
588 	(*kdisetsoftint)(1, softlevel1_hdl.ih_pending);
589 }
590 
591 /*
592  * Trigger a soft interrupt.
593  */
594 void
595 siron(void)
596 {
597 	/* Level 1 software interrupt */
598 	(*setsoftint)(1, softlevel1_hdl.ih_pending);
599 }
600 
601 /*
602  * Trigger software interrupts dedicated to ddi timer.
603  */
604 void
605 sir_on(int level)
606 {
607 	ASSERT(level >= DDI_IPL_1 && level <= DDI_IPL_10);
608 	(*setsoftint)(level, softlevel_hdl[level-1].ih_pending);
609 }
610 
611 /*
612  * The handler which is executed on the target CPU.
613  */
614 /*ARGSUSED*/
615 static int
616 siron_poke_intr(xc_arg_t a1, xc_arg_t a2, xc_arg_t a3)
617 {
618 	siron();
619 	return (0);
620 }
621 
622 /*
623  * May get called from softcall to poke CPUs.
624  */
625 void
626 siron_poke_cpu(cpuset_t poke)
627 {
628 	int cpuid = CPU->cpu_id;
629 
630 	/*
631 	 * If we are poking to ourself then we can simply
632 	 * generate level1 using siron()
633 	 */
634 	if (CPU_IN_SET(poke, cpuid)) {
635 		siron();
636 		CPUSET_DEL(poke, cpuid);
637 		if (CPUSET_ISNULL(poke))
638 			return;
639 	}
640 
641 	xc_call(0, 0, 0, X_CALL_MEDPRI, poke, (xc_func_t)siron_poke_intr);
642 }
643 
644 /*
645  * Walk the autovector table for this vector, invoking each
646  * interrupt handler as we go.
647  */
648 
649 extern uint64_t intr_get_time(void);
650 
651 void
652 av_dispatch_autovect(uint_t vec)
653 {
654 	struct autovec *av;
655 
656 	ASSERT_STACK_ALIGNED();
657 
658 	while ((av = autovect[vec].avh_link) != NULL) {
659 		uint_t numcalled = 0;
660 		uint_t claimed = 0;
661 
662 		for (; av; av = av->av_link) {
663 			uint_t r;
664 			uint_t (*intr)() = av->av_vector;
665 			caddr_t arg1 = av->av_intarg1;
666 			caddr_t arg2 = av->av_intarg2;
667 			dev_info_t *dip = av->av_dip;
668 
669 			/*
670 			 * We must walk the entire chain.  Removed handlers
671 			 * may be anywhere in the chain.
672 			 */
673 			if (intr == NULL)
674 				continue;
675 
676 			DTRACE_PROBE4(interrupt__start, dev_info_t *, dip,
677 			    void *, intr, caddr_t, arg1, caddr_t, arg2);
678 			r = (*intr)(arg1, arg2);
679 			DTRACE_PROBE4(interrupt__complete, dev_info_t *, dip,
680 			    void *, intr, caddr_t, arg1, uint_t, r);
681 			numcalled++;
682 			claimed |= r;
683 			if (av->av_ticksp && av->av_prilevel <= LOCK_LEVEL)
684 				atomic_add_64(av->av_ticksp, intr_get_time());
685 		}
686 
687 		/*
688 		 * If there's only one interrupt handler in the chain,
689 		 * or if no-one claimed the interrupt at all give up now.
690 		 */
691 		if (numcalled == 1 || claimed == 0)
692 			break;
693 	}
694 }
695 
696 /*
697  * Call every soft interrupt handler we can find at this level once.
698  */
699 void
700 av_dispatch_softvect(uint_t pil)
701 {
702 	struct autovec *av;
703 	ddi_softint_hdl_impl_t	*hdlp;
704 	uint_t (*intr)();
705 	caddr_t arg1;
706 	caddr_t arg2;
707 
708 	ASSERT_STACK_ALIGNED();
709 	ASSERT(pil >= 0 && pil <= PIL_MAX);
710 
711 	for (av = softvect[pil].avh_link; av; av = av->av_link) {
712 		/*
713 		 * We must walk the entire chain.  Removed handlers
714 		 * may be anywhere in the chain.
715 		 */
716 		if ((intr = av->av_vector) == NULL)
717 			continue;
718 		arg1 = av->av_intarg1;
719 		arg2 = av->av_intarg2;
720 
721 		hdlp = (ddi_softint_hdl_impl_t *)av->av_intr_id;
722 		ASSERT(hdlp);
723 
724 		/*
725 		 * Each cpu has its own pending bit in hdlp->ih_pending,
726 		 * here av_check/clear_softint_pending is just checking
727 		 * and clearing the pending bit for the current cpu, who
728 		 * has just triggered a softint.
729 		 */
730 		if (av_check_softint_pending(hdlp->ih_pending, B_FALSE)) {
731 			av_clear_softint_pending(hdlp->ih_pending);
732 			(void) (*intr)(arg1, arg2);
733 		}
734 	}
735 }
736 
737 struct regs;
738 
739 /*
740  * Call every NMI handler we know of once.
741  */
742 void
743 av_dispatch_nmivect(struct regs *rp)
744 {
745 	struct autovec *av;
746 
747 	ASSERT_STACK_ALIGNED();
748 
749 	for (av = nmivect; av; av = av->av_link)
750 		(void) (av->av_vector)(av->av_intarg1, rp);
751 }
752