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