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