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 50 typedef struct av_softinfo { 51 cpuset_t av_pending; /* pending bitmasks */ 52 } av_softinfo_t; 53 54 static void insert_av(void *intr_id, struct av_head *vectp, avfunc f, 55 caddr_t arg1, caddr_t arg2, uint64_t *ticksp, int pri_level, 56 dev_info_t *dip); 57 static void remove_av(void *intr_id, struct av_head *vectp, avfunc f, 58 int pri_level, int vect); 59 60 /* 61 * Arrange for a driver to be called when a particular 62 * auto-vectored interrupt occurs. 63 * NOTE: if a device can generate interrupts on more than 64 * one level, or if a driver services devices that interrupt 65 * on more than one level, then the driver should install 66 * itself on each of those levels. 67 */ 68 static char badsoft[] = 69 "add_avintr: bad soft interrupt level %d for driver '%s'\n"; 70 static char multilevel[] = 71 "!IRQ%d is being shared by drivers with different interrupt levels.\n" 72 "This may result in reduced system performance."; 73 static char multilevel2[] = 74 "Cannot register interrupt for '%s' device at IPL %d because it\n" 75 "conflicts with another device using the same vector %d with an IPL\n" 76 "of %d. Reconfigure the conflicting devices to use different vectors."; 77 78 #define MAX_VECT 256 79 struct autovec *nmivect = NULL; 80 struct av_head autovect[MAX_VECT]; 81 struct av_head softvect[LOCK_LEVEL + 1]; 82 kmutex_t av_lock; 83 ddi_softint_hdl_impl_t softlevel1_hdl = 84 {0, NULL, NULL, NULL, 0, NULL, NULL, NULL}; 85 86 87 /* 88 * clear/check softint pending flag corresponding for 89 * the current CPU 90 */ 91 void 92 av_clear_softint_pending(av_softinfo_t *infop) 93 { 94 CPUSET_ATOMIC_DEL(infop->av_pending, CPU->cpu_seqid); 95 } 96 97 boolean_t 98 av_check_softint_pending(av_softinfo_t *infop, boolean_t check_all) 99 { 100 if (check_all) 101 return (!CPUSET_ISNULL(infop->av_pending)); 102 else 103 return (CPU_IN_SET(infop->av_pending, CPU->cpu_seqid) != 0); 104 } 105 106 /* 107 * It first sets our av softint pending bit for the current CPU, 108 * then it sets the CPU softint pending bit for pri. 109 */ 110 void 111 av_set_softint_pending(int pri, av_softinfo_t *infop) 112 { 113 CPUSET_ATOMIC_ADD(infop->av_pending, CPU->cpu_seqid); 114 115 atomic_or_32((uint32_t *)&CPU->cpu_softinfo.st_pending, 1 << pri); 116 } 117 118 /* 119 * register nmi interrupt routine. The first arg is used only to order 120 * various nmi interrupt service routines in the chain. Higher lvls will 121 * be called first 122 */ 123 int 124 add_nmintr(int lvl, avfunc nmintr, char *name, caddr_t arg) 125 { 126 struct autovec *mem; 127 struct autovec *p, *prev = NULL; 128 129 if (nmintr == NULL) { 130 printf("Attempt to add null vect for %s on nmi\n", name); 131 return (0); 132 133 } 134 135 mem = kmem_zalloc(sizeof (struct autovec), KM_SLEEP); 136 mem->av_vector = nmintr; 137 mem->av_intarg1 = arg; 138 mem->av_intarg2 = NULL; 139 mem->av_intr_id = NULL; 140 mem->av_prilevel = lvl; 141 mem->av_dip = NULL; 142 mem->av_link = NULL; 143 144 mutex_enter(&av_lock); 145 146 if (!nmivect) { 147 nmivect = mem; 148 mutex_exit(&av_lock); 149 return (1); 150 } 151 /* find where it goes in list */ 152 for (p = nmivect; p != NULL; p = p->av_link) { 153 if (p->av_vector == nmintr && p->av_intarg1 == arg) { 154 /* 155 * already in list 156 * So? Somebody added the same interrupt twice. 157 */ 158 cmn_err(CE_WARN, "Driver already registered '%s'", 159 name); 160 kmem_free(mem, sizeof (struct autovec)); 161 mutex_exit(&av_lock); 162 return (0); 163 } 164 if (p->av_prilevel < lvl) { 165 if (p == nmivect) { /* it's at head of list */ 166 mem->av_link = p; 167 nmivect = mem; 168 } else { 169 mem->av_link = p; 170 prev->av_link = mem; 171 } 172 mutex_exit(&av_lock); 173 return (1); 174 } 175 prev = p; 176 177 } 178 /* didn't find it, add it to the end */ 179 prev->av_link = mem; 180 mutex_exit(&av_lock); 181 return (1); 182 183 } 184 185 /* 186 * register a hardware interrupt handler. 187 */ 188 int 189 add_avintr(void *intr_id, int lvl, avfunc xxintr, char *name, int vect, 190 caddr_t arg1, caddr_t arg2, uint64_t *ticksp, dev_info_t *dip) 191 { 192 struct av_head *vecp = (struct av_head *)0; 193 avfunc f; 194 int s, vectindex; /* save old spl value */ 195 ushort_t hi_pri; 196 197 if ((f = xxintr) == NULL) { 198 printf("Attempt to add null vect for %s on vector %d\n", 199 name, vect); 200 return (0); 201 202 } 203 vectindex = vect % MAX_VECT; 204 205 vecp = &autovect[vectindex]; 206 207 /* 208 * "hi_pri == 0" implies all entries on list are "unused", 209 * which means that it's OK to just insert this one. 210 */ 211 hi_pri = vecp->avh_hi_pri; 212 if (vecp->avh_link && (hi_pri != 0)) { 213 if (((hi_pri > LOCK_LEVEL) && (lvl < LOCK_LEVEL)) || 214 ((hi_pri < LOCK_LEVEL) && (lvl > LOCK_LEVEL))) { 215 cmn_err(CE_WARN, multilevel2, name, lvl, vect, 216 hi_pri); 217 return (0); 218 } 219 if ((vecp->avh_lo_pri != lvl) || (hi_pri != lvl)) 220 cmn_err(CE_NOTE, multilevel, vect); 221 } 222 223 insert_av(intr_id, vecp, f, arg1, arg2, ticksp, lvl, dip); 224 s = splhi(); 225 /* 226 * do what ever machine specific things are necessary 227 * to set priority level (e.g. set picmasks) 228 */ 229 mutex_enter(&av_lock); 230 (*addspl)(vect, lvl, vecp->avh_lo_pri, vecp->avh_hi_pri); 231 mutex_exit(&av_lock); 232 splx(s); 233 return (1); 234 235 } 236 237 void 238 update_avsoftintr_args(void *intr_id, int lvl, caddr_t arg2) 239 { 240 struct autovec *p; 241 struct autovec *target = NULL; 242 struct av_head *vectp = (struct av_head *)&softvect[lvl]; 243 244 for (p = vectp->avh_link; p && p->av_vector; p = p->av_link) { 245 if (p->av_intr_id == intr_id) { 246 target = p; 247 break; 248 } 249 } 250 251 if (target == NULL) 252 return; 253 target->av_intarg2 = arg2; 254 } 255 256 /* 257 * Register a software interrupt handler 258 */ 259 int 260 add_avsoftintr(void *intr_id, int lvl, avfunc xxintr, char *name, 261 caddr_t arg1, caddr_t arg2) 262 { 263 int slvl; 264 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)intr_id; 265 266 if ((slvl = slvltovect(lvl)) != -1) 267 return (add_avintr(intr_id, lvl, xxintr, 268 name, slvl, arg1, arg2, NULL, NULL)); 269 270 if (intr_id == NULL) { 271 printf("Attempt to add null intr_id for %s on level %d\n", 272 name, lvl); 273 return (0); 274 } 275 276 if (xxintr == NULL) { 277 printf("Attempt to add null handler for %s on level %d\n", 278 name, lvl); 279 return (0); 280 } 281 282 if (lvl <= 0 || lvl > LOCK_LEVEL) { 283 printf(badsoft, lvl, name); 284 return (0); 285 } 286 287 if (hdlp->ih_pending == NULL) { 288 hdlp->ih_pending = 289 kmem_zalloc(sizeof (av_softinfo_t), KM_SLEEP); 290 } 291 292 insert_av(intr_id, &softvect[lvl], xxintr, arg1, arg2, NULL, lvl, NULL); 293 294 return (1); 295 } 296 297 /* insert an interrupt vector into chain */ 298 static void 299 insert_av(void *intr_id, struct av_head *vectp, avfunc f, caddr_t arg1, 300 caddr_t arg2, uint64_t *ticksp, int pri_level, dev_info_t *dip) 301 { 302 /* 303 * Protect rewrites of the list 304 */ 305 struct autovec *p, *mem; 306 307 mem = kmem_zalloc(sizeof (struct autovec), KM_SLEEP); 308 mem->av_vector = f; 309 mem->av_intarg1 = arg1; 310 mem->av_intarg2 = arg2; 311 mem->av_ticksp = ticksp; 312 mem->av_intr_id = intr_id; 313 mem->av_prilevel = pri_level; 314 mem->av_dip = dip; 315 mem->av_link = NULL; 316 317 mutex_enter(&av_lock); 318 319 if (vectp->avh_link == NULL) { /* Nothing on list - put it at head */ 320 vectp->avh_link = mem; 321 vectp->avh_hi_pri = vectp->avh_lo_pri = (ushort_t)pri_level; 322 323 mutex_exit(&av_lock); 324 return; 325 } 326 327 /* find where it goes in list */ 328 for (p = vectp->avh_link; p != NULL; p = p->av_link) { 329 if (p->av_vector == NULL) { /* freed struct available */ 330 kmem_free(mem, sizeof (struct autovec)); 331 p->av_intarg1 = arg1; 332 p->av_intarg2 = arg2; 333 p->av_ticksp = ticksp; 334 p->av_intr_id = intr_id; 335 p->av_prilevel = pri_level; 336 p->av_dip = dip; 337 if (pri_level > (int)vectp->avh_hi_pri) { 338 vectp->avh_hi_pri = (ushort_t)pri_level; 339 } 340 if (pri_level < (int)vectp->avh_lo_pri) { 341 vectp->avh_lo_pri = (ushort_t)pri_level; 342 } 343 p->av_vector = f; 344 mutex_exit(&av_lock); 345 return; 346 } 347 } 348 /* insert new intpt at beginning of chain */ 349 mem->av_link = vectp->avh_link; 350 vectp->avh_link = mem; 351 if (pri_level > (int)vectp->avh_hi_pri) { 352 vectp->avh_hi_pri = (ushort_t)pri_level; 353 } 354 if (pri_level < (int)vectp->avh_lo_pri) { 355 vectp->avh_lo_pri = (ushort_t)pri_level; 356 } 357 mutex_exit(&av_lock); 358 } 359 360 static int 361 av_rem_softintr(void *intr_id, int lvl, avfunc xxintr, boolean_t rem_softinfo) 362 { 363 struct av_head *vecp = (struct av_head *)0; 364 int slvl; 365 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)intr_id; 366 av_softinfo_t *infop = (av_softinfo_t *)hdlp->ih_pending; 367 368 if (xxintr == NULL) 369 return (0); 370 371 if ((slvl = slvltovect(lvl)) != -1) { 372 rem_avintr(intr_id, lvl, xxintr, slvl); 373 return (1); 374 } 375 376 if (lvl <= 0 && lvl >= LOCK_LEVEL) { 377 return (0); 378 } 379 vecp = &softvect[lvl]; 380 remove_av(intr_id, vecp, xxintr, lvl, 0); 381 382 if (rem_softinfo) { 383 kmem_free(infop, sizeof (av_softinfo_t)); 384 hdlp->ih_pending = NULL; 385 } 386 387 return (1); 388 } 389 390 int 391 av_softint_movepri(void *intr_id, int old_lvl) 392 { 393 int ret; 394 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)intr_id; 395 396 ret = add_avsoftintr(intr_id, hdlp->ih_pri, hdlp->ih_cb_func, 397 DEVI(hdlp->ih_dip)->devi_name, hdlp->ih_cb_arg1, hdlp->ih_cb_arg2); 398 399 if (ret) { 400 (void) av_rem_softintr(intr_id, old_lvl, hdlp->ih_cb_func, 401 B_FALSE); 402 } 403 404 return (ret); 405 } 406 407 /* 408 * Remove a driver from the autovector list. 409 */ 410 int 411 rem_avsoftintr(void *intr_id, int lvl, avfunc xxintr) 412 { 413 return (av_rem_softintr(intr_id, lvl, xxintr, B_TRUE)); 414 } 415 416 void 417 rem_avintr(void *intr_id, int lvl, avfunc xxintr, int vect) 418 { 419 struct av_head *vecp = (struct av_head *)0; 420 avfunc f; 421 int s, vectindex; /* save old spl value */ 422 423 if ((f = xxintr) == NULL) 424 return; 425 426 vectindex = vect % MAX_VECT; 427 vecp = &autovect[vectindex]; 428 remove_av(intr_id, vecp, f, lvl, vect); 429 s = splhi(); 430 mutex_enter(&av_lock); 431 (*delspl)(vect, lvl, vecp->avh_lo_pri, vecp->avh_hi_pri); 432 mutex_exit(&av_lock); 433 splx(s); 434 } 435 436 437 /* 438 * After having made a change to an autovector list, wait until we have 439 * seen each cpu not executing an interrupt at that level--so we know our 440 * change has taken effect completely (no old state in registers, etc). 441 */ 442 void 443 wait_till_seen(int ipl) 444 { 445 int cpu_in_chain, cix; 446 struct cpu *cpup; 447 cpuset_t cpus_to_check; 448 449 CPUSET_ALL(cpus_to_check); 450 do { 451 cpu_in_chain = 0; 452 for (cix = 0; cix < NCPU; cix++) { 453 cpup = cpu[cix]; 454 if (cpup != NULL && CPU_IN_SET(cpus_to_check, cix)) { 455 if (intr_active(cpup, ipl)) { 456 cpu_in_chain = 1; 457 } else { 458 CPUSET_DEL(cpus_to_check, cix); 459 } 460 } 461 } 462 } while (cpu_in_chain); 463 } 464 465 /* remove an interrupt vector from the chain */ 466 static void 467 remove_av(void *intr_id, struct av_head *vectp, avfunc f, int pri_level, 468 int vect) 469 { 470 struct autovec *endp, *p, *target; 471 int lo_pri, hi_pri; 472 int ipl; 473 /* 474 * Protect rewrites of the list 475 */ 476 target = NULL; 477 478 mutex_enter(&av_lock); 479 ipl = pri_level; 480 lo_pri = MAXIPL; 481 hi_pri = 0; 482 for (endp = p = vectp->avh_link; p && p->av_vector; p = p->av_link) { 483 endp = p; 484 if ((p->av_vector == f) && (p->av_intr_id == intr_id)) { 485 /* found the handler */ 486 target = p; 487 continue; 488 } 489 if (p->av_prilevel > hi_pri) 490 hi_pri = p->av_prilevel; 491 if (p->av_prilevel < lo_pri) 492 lo_pri = p->av_prilevel; 493 } 494 if (ipl < hi_pri) 495 ipl = hi_pri; 496 if (target == NULL) { /* not found */ 497 printf("Couldn't remove function %p at %d, %d\n", 498 (void *)f, vect, pri_level); 499 mutex_exit(&av_lock); 500 return; 501 } 502 503 target->av_vector = NULL; 504 target->av_ticksp = NULL; 505 wait_till_seen(ipl); 506 if (endp != target) { /* vector to be removed is not last in chain */ 507 target->av_vector = endp->av_vector; 508 target->av_intarg1 = endp->av_intarg1; 509 target->av_intarg2 = endp->av_intarg2; 510 target->av_ticksp = endp->av_ticksp; 511 target->av_intr_id = endp->av_intr_id; 512 target->av_prilevel = endp->av_prilevel; 513 target->av_dip = endp->av_dip; 514 /* 515 * We have a hole here where the routine corresponding to 516 * endp may not get called. Do a wait_till_seen to take care 517 * of this. 518 */ 519 wait_till_seen(ipl); 520 endp->av_vector = NULL; 521 endp->av_ticksp = NULL; 522 } 523 524 if (lo_pri > hi_pri) { /* the chain is now empty */ 525 /* Leave the unused entries here for probable future use */ 526 vectp->avh_lo_pri = MAXIPL; 527 vectp->avh_hi_pri = 0; 528 } else { 529 if ((int)vectp->avh_lo_pri < lo_pri) 530 vectp->avh_lo_pri = (ushort_t)lo_pri; 531 if ((int)vectp->avh_hi_pri > hi_pri) 532 vectp->avh_hi_pri = (ushort_t)hi_pri; 533 } 534 mutex_exit(&av_lock); 535 wait_till_seen(ipl); 536 } 537 538 /* 539 * Trigger a soft interrupt. 540 */ 541 void 542 siron(void) 543 { 544 (*setsoftint)(1, softlevel1_hdl.ih_pending); 545 } 546 547 /* 548 * Walk the autovector table for this vector, invoking each 549 * interrupt handler as we go. 550 */ 551 552 extern uint64_t intr_get_time(void); 553 554 void 555 av_dispatch_autovect(uint_t vec) 556 { 557 struct autovec *av; 558 559 ASSERT_STACK_ALIGNED(); 560 561 while ((av = autovect[vec].avh_link) != NULL) { 562 uint_t numcalled = 0; 563 uint_t claimed = 0; 564 565 for (; av; av = av->av_link) { 566 uint_t r; 567 uint_t (*intr)() = av->av_vector; 568 caddr_t arg1 = av->av_intarg1; 569 caddr_t arg2 = av->av_intarg2; 570 dev_info_t *dip = av->av_dip; 571 572 if (intr == NULL) 573 break; 574 575 DTRACE_PROBE4(interrupt__start, dev_info_t *, dip, 576 void *, intr, caddr_t, arg1, caddr_t, arg2); 577 r = (*intr)(arg1, arg2); 578 DTRACE_PROBE4(interrupt__complete, dev_info_t *, dip, 579 void *, intr, caddr_t, arg1, uint_t, r); 580 numcalled++; 581 claimed |= r; 582 if (av->av_ticksp && av->av_prilevel <= LOCK_LEVEL) 583 atomic_add_64(av->av_ticksp, intr_get_time()); 584 } 585 586 /* 587 * If there's only one interrupt handler in the chain, 588 * or if no-one claimed the interrupt at all give up now. 589 */ 590 if (numcalled == 1 || claimed == 0) 591 break; 592 } 593 } 594 595 /* 596 * Call every soft interrupt handler we can find at this level once. 597 */ 598 void 599 av_dispatch_softvect(uint_t pil) 600 { 601 struct autovec *av; 602 ddi_softint_hdl_impl_t *hdlp; 603 uint_t (*intr)(); 604 caddr_t arg1; 605 caddr_t arg2; 606 607 ASSERT_STACK_ALIGNED(); 608 ASSERT(pil >= 0 && pil <= PIL_MAX); 609 610 for (av = softvect[pil].avh_link; av; av = av->av_link) { 611 if ((intr = av->av_vector) == NULL) 612 break; 613 arg1 = av->av_intarg1; 614 arg2 = av->av_intarg2; 615 616 hdlp = (ddi_softint_hdl_impl_t *)av->av_intr_id; 617 ASSERT(hdlp); 618 619 /* 620 * Each cpu has its own pending bit in hdlp->ih_pending, 621 * here av_check/clear_softint_pending is just checking 622 * and clearing the pending bit for the current cpu, who 623 * has just triggered a softint. 624 */ 625 if (av_check_softint_pending(hdlp->ih_pending, B_FALSE)) { 626 av_clear_softint_pending(hdlp->ih_pending); 627 (void) (*intr)(arg1, arg2); 628 } 629 } 630 } 631 632 struct regs; 633 634 /* 635 * Call every NMI handler we know of once. 636 */ 637 void 638 av_dispatch_nmivect(struct regs *rp) 639 { 640 struct autovec *av; 641 642 ASSERT_STACK_ALIGNED(); 643 644 for (av = nmivect; av; av = av->av_link) 645 (void) (av->av_vector)(av->av_intarg1, rp); 646 } 647