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