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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Autovectored Interrupt Configuration and Deconfiguration 31 */ 32 33 #include <sys/param.h> 34 #include <sys/cmn_err.h> 35 #include <sys/trap.h> 36 #include <sys/t_lock.h> 37 #include <sys/avintr.h> 38 #include <sys/kmem.h> 39 #include <sys/machlock.h> 40 #include <sys/systm.h> 41 #include <sys/machsystm.h> 42 #include <sys/sunddi.h> 43 #include <sys/x_call.h> 44 #include <sys/cpuvar.h> 45 #include <sys/atomic.h> 46 #include <sys/smp_impldefs.h> 47 #include <sys/sdt.h> 48 #include <sys/stack.h> 49 #include <sys/ddi_impldefs.h> 50 51 static int insert_av(void *intr_id, struct av_head *vectp, avfunc f, 52 caddr_t arg1, caddr_t arg2, uint64_t *ticksp, int pri_level, 53 dev_info_t *dip); 54 static void remove_av(void *intr_id, struct av_head *vectp, avfunc f, 55 int pri_level, int vect); 56 57 /* 58 * Arrange for a driver to be called when a particular 59 * auto-vectored interrupt occurs. 60 * NOTE: if a device can generate interrupts on more than 61 * one level, or if a driver services devices that interrupt 62 * on more than one level, then the driver should install 63 * itself on each of those levels. 64 */ 65 static char badsoft[] = 66 "add_avintr: bad soft interrupt level %d for driver '%s'\n"; 67 static char multilevel[] = 68 "!IRQ%d is being shared by drivers with different interrupt levels.\n" 69 "This may result in reduced system performance."; 70 static char multilevel2[] = 71 "Cannot register interrupt for '%s' device at IPL %d because it\n" 72 "conflicts with another device using the same vector %d with an IPL\n" 73 "of %d. Reconfigure the conflicting devices to use different vectors."; 74 75 #define MAX_VECT 256 76 struct autovec *nmivect = NULL; 77 struct av_head autovect[MAX_VECT]; 78 struct av_head softvect[LOCK_LEVEL + 1]; 79 kmutex_t av_lock; 80 ddi_softint_hdl_impl_t softlevel1_hdl = 81 {0, NULL, NULL, 0, 0, NULL, NULL, NULL}; 82 83 void 84 set_pending(int pri) 85 { 86 atomic_or_32((uint32_t *)&CPU->cpu_softinfo.st_pending, 1 << pri); 87 } 88 89 /* 90 * register nmi interrupt routine. The first arg is used only to order 91 * various nmi interrupt service routines in the chain. Higher lvls will 92 * be called first 93 */ 94 int 95 add_nmintr(int lvl, avfunc nmintr, char *name, caddr_t arg) 96 { 97 struct autovec *mem; 98 struct autovec *p, *prev = NULL; 99 100 if (nmintr == NULL) { 101 printf("Attempt to add null vect for %s on nmi\n", name); 102 return (0); 103 104 } 105 106 mem = kmem_zalloc(sizeof (struct autovec), KM_SLEEP); 107 mem->av_vector = nmintr; 108 mem->av_intarg1 = arg; 109 mem->av_intarg2 = NULL; 110 mem->av_intr_id = NULL; 111 mem->av_prilevel = lvl; 112 mem->av_dip = NULL; 113 mem->av_link = NULL; 114 115 mutex_enter(&av_lock); 116 117 if (!nmivect) { 118 nmivect = mem; 119 mutex_exit(&av_lock); 120 return (1); 121 } 122 /* find where it goes in list */ 123 for (p = nmivect; p != NULL; p = p->av_link) { 124 if (p->av_vector == nmintr && p->av_intarg1 == arg) { 125 /* 126 * already in list 127 * So? Somebody added the same interrupt twice. 128 */ 129 cmn_err(CE_WARN, "Driver already registered '%s'", 130 name); 131 kmem_free(mem, sizeof (struct autovec)); 132 mutex_exit(&av_lock); 133 return (0); 134 } 135 if (p->av_prilevel < lvl) { 136 if (p == nmivect) { /* it's at head of list */ 137 mem->av_link = p; 138 nmivect = mem; 139 } else { 140 mem->av_link = p; 141 prev->av_link = mem; 142 } 143 mutex_exit(&av_lock); 144 return (1); 145 } 146 prev = p; 147 148 } 149 /* didn't find it, add it to the end */ 150 prev->av_link = mem; 151 mutex_exit(&av_lock); 152 return (1); 153 154 } 155 156 /* 157 * register a hardware interrupt handler. 158 */ 159 int 160 add_avintr(void *intr_id, int lvl, avfunc xxintr, char *name, int vect, 161 caddr_t arg1, caddr_t arg2, uint64_t *ticksp, dev_info_t *dip) 162 { 163 struct av_head *vecp = (struct av_head *)0; 164 avfunc f; 165 int s, vectindex; /* save old spl value */ 166 ushort_t hi_pri; 167 168 if ((f = xxintr) == NULL) { 169 printf("Attempt to add null vect for %s on vector %d\n", 170 name, vect); 171 return (0); 172 173 } 174 vectindex = vect % MAX_VECT; 175 176 vecp = &autovect[vectindex]; 177 178 /* 179 * "hi_pri == 0" implies all entries on list are "unused", 180 * which means that it's OK to just insert this one. 181 */ 182 hi_pri = vecp->avh_hi_pri; 183 if (vecp->avh_link && (hi_pri != 0)) { 184 if (((hi_pri > LOCK_LEVEL) && (lvl < LOCK_LEVEL)) || 185 ((hi_pri < LOCK_LEVEL) && (lvl > LOCK_LEVEL))) { 186 cmn_err(CE_WARN, multilevel2, name, lvl, vect, 187 hi_pri); 188 return (0); 189 } 190 if ((vecp->avh_lo_pri != lvl) || (hi_pri != lvl)) 191 cmn_err(CE_NOTE, multilevel, vect); 192 } 193 194 if (!insert_av(intr_id, vecp, f, arg1, arg2, ticksp, lvl, dip)) 195 return (0); 196 s = splhi(); 197 /* 198 * do what ever machine specific things are necessary 199 * to set priority level (e.g. set picmasks) 200 */ 201 mutex_enter(&av_lock); 202 (*addspl)(vect, lvl, vecp->avh_lo_pri, vecp->avh_hi_pri); 203 mutex_exit(&av_lock); 204 splx(s); 205 return (1); 206 207 } 208 209 void 210 update_avsoftintr_args(void *intr_id, int lvl, caddr_t arg2) 211 { 212 struct autovec *p; 213 struct autovec *target = NULL; 214 struct av_head *vectp = (struct av_head *)&softvect[lvl]; 215 216 for (p = vectp->avh_link; p && p->av_vector; p = p->av_link) { 217 if (p->av_intr_id == intr_id) { 218 target = p; 219 break; 220 } 221 } 222 223 if (target == NULL) 224 return; 225 target->av_intarg2 = arg2; 226 } 227 228 /* 229 * Register a software interrupt handler 230 */ 231 int 232 add_avsoftintr(void *intr_id, int lvl, avfunc xxintr, char *name, 233 caddr_t arg1, caddr_t arg2) 234 { 235 int slvl; 236 237 if ((slvl = slvltovect(lvl)) != -1) 238 return (add_avintr(intr_id, lvl, xxintr, 239 name, slvl, arg1, arg2, NULL, NULL)); 240 241 if (intr_id == NULL) { 242 printf("Attempt to add null intr_id for %s on level %d\n", 243 name, lvl); 244 return (0); 245 } 246 247 if (xxintr == NULL) { 248 printf("Attempt to add null handler for %s on level %d\n", 249 name, lvl); 250 return (0); 251 } 252 253 if (lvl <= 0 || lvl > LOCK_LEVEL) { 254 printf(badsoft, lvl, name); 255 return (0); 256 } 257 if (!insert_av(intr_id, &softvect[lvl], xxintr, arg1, arg2, NULL, 258 lvl, NULL)) { 259 return (0); 260 } 261 return (1); 262 } 263 264 /* insert an interrupt vector into chain */ 265 static int 266 insert_av(void *intr_id, struct av_head *vectp, avfunc f, caddr_t arg1, 267 caddr_t arg2, uint64_t *ticksp, int pri_level, dev_info_t *dip) 268 { 269 /* 270 * Protect rewrites of the list 271 */ 272 struct autovec *p, *mem; 273 274 mem = kmem_zalloc(sizeof (struct autovec), KM_SLEEP); 275 mem->av_vector = f; 276 mem->av_intarg1 = arg1; 277 mem->av_intarg2 = arg2; 278 mem->av_ticksp = ticksp; 279 mem->av_intr_id = intr_id; 280 mem->av_prilevel = pri_level; 281 mem->av_dip = dip; 282 mem->av_link = NULL; 283 284 mutex_enter(&av_lock); 285 286 if (vectp->avh_link == NULL) { /* Nothing on list - put it at head */ 287 vectp->avh_link = mem; 288 vectp->avh_hi_pri = vectp->avh_lo_pri = (ushort_t)pri_level; 289 290 mutex_exit(&av_lock); 291 return (1); 292 } 293 294 /* find where it goes in list */ 295 for (p = vectp->avh_link; p != NULL; p = p->av_link) { 296 if (p->av_vector == NULL) { /* freed struct available */ 297 kmem_free(mem, sizeof (struct autovec)); 298 p->av_intarg1 = arg1; 299 p->av_intarg2 = arg2; 300 p->av_ticksp = ticksp; 301 p->av_intr_id = intr_id; 302 p->av_prilevel = pri_level; 303 p->av_dip = dip; 304 if (pri_level > (int)vectp->avh_hi_pri) { 305 vectp->avh_hi_pri = (ushort_t)pri_level; 306 } 307 if (pri_level < (int)vectp->avh_lo_pri) { 308 vectp->avh_lo_pri = (ushort_t)pri_level; 309 } 310 p->av_vector = f; 311 mutex_exit(&av_lock); 312 return (1); 313 } 314 } 315 /* insert new intpt at beginning of chain */ 316 mem->av_link = vectp->avh_link; 317 vectp->avh_link = mem; 318 if (pri_level > (int)vectp->avh_hi_pri) { 319 vectp->avh_hi_pri = (ushort_t)pri_level; 320 } 321 if (pri_level < (int)vectp->avh_lo_pri) { 322 vectp->avh_lo_pri = (ushort_t)pri_level; 323 } 324 mutex_exit(&av_lock); 325 326 return (1); 327 } 328 329 /* 330 * Remove a driver from the autovector list. 331 */ 332 int 333 rem_avsoftintr(void *intr_id, int lvl, avfunc xxintr) 334 { 335 struct av_head *vecp = (struct av_head *)0; 336 int slvl; 337 338 if (xxintr == NULL) 339 return (0); 340 341 if ((slvl = slvltovect(lvl)) != -1) { 342 rem_avintr(intr_id, lvl, xxintr, slvl); 343 return (1); 344 } 345 346 if (lvl <= 0 && lvl >= LOCK_LEVEL) { 347 return (0); 348 } 349 vecp = &softvect[lvl]; 350 remove_av(intr_id, vecp, xxintr, lvl, 0); 351 352 return (1); 353 } 354 355 void 356 rem_avintr(void *intr_id, int lvl, avfunc xxintr, int vect) 357 { 358 struct av_head *vecp = (struct av_head *)0; 359 avfunc f; 360 int s, vectindex; /* save old spl value */ 361 362 if ((f = xxintr) == NULL) 363 return; 364 365 vectindex = vect % MAX_VECT; 366 vecp = &autovect[vectindex]; 367 remove_av(intr_id, vecp, f, lvl, vect); 368 s = splhi(); 369 mutex_enter(&av_lock); 370 (*delspl)(vect, lvl, vecp->avh_lo_pri, vecp->avh_hi_pri); 371 mutex_exit(&av_lock); 372 splx(s); 373 } 374 375 376 /* 377 * After having made a change to an autovector list, wait until we have 378 * seen each cpu not executing an interrupt at that level--so we know our 379 * change has taken effect completely (no old state in registers, etc). 380 */ 381 void 382 wait_till_seen(int ipl) 383 { 384 int cpu_in_chain, cix; 385 struct cpu *cpup; 386 cpuset_t cpus_to_check; 387 388 CPUSET_ALL(cpus_to_check); 389 do { 390 cpu_in_chain = 0; 391 for (cix = 0; cix < NCPU; cix++) { 392 cpup = cpu[cix]; 393 if (cpup != NULL && CPU_IN_SET(cpus_to_check, cix)) { 394 if (intr_active(cpup, ipl)) { 395 cpu_in_chain = 1; 396 } else { 397 CPUSET_DEL(cpus_to_check, cix); 398 } 399 } 400 } 401 } while (cpu_in_chain); 402 } 403 404 /* remove an interrupt vector from the chain */ 405 static void 406 remove_av(void *intr_id, struct av_head *vectp, avfunc f, int pri_level, 407 int vect) 408 { 409 struct autovec *endp, *p, *target; 410 int lo_pri, hi_pri; 411 int ipl; 412 /* 413 * Protect rewrites of the list 414 */ 415 target = NULL; 416 417 mutex_enter(&av_lock); 418 ipl = pri_level; 419 lo_pri = MAXIPL; 420 hi_pri = 0; 421 for (endp = p = vectp->avh_link; p && p->av_vector; p = p->av_link) { 422 endp = p; 423 if ((p->av_vector == f) && (p->av_intr_id == intr_id)) { 424 /* found the handler */ 425 target = p; 426 continue; 427 } 428 if (p->av_prilevel > hi_pri) 429 hi_pri = p->av_prilevel; 430 if (p->av_prilevel < lo_pri) 431 lo_pri = p->av_prilevel; 432 } 433 if (ipl < hi_pri) 434 ipl = hi_pri; 435 if (target == NULL) { /* not found */ 436 printf("Couldn't remove function %p at %d, %d\n", 437 (void *)f, vect, pri_level); 438 mutex_exit(&av_lock); 439 return; 440 } 441 442 target->av_vector = NULL; 443 target->av_ticksp = NULL; 444 wait_till_seen(ipl); 445 if (endp != target) { /* vector to be removed is not last in chain */ 446 target->av_vector = endp->av_vector; 447 target->av_intarg1 = endp->av_intarg1; 448 target->av_intarg2 = endp->av_intarg2; 449 target->av_ticksp = endp->av_ticksp; 450 target->av_intr_id = endp->av_intr_id; 451 target->av_prilevel = endp->av_prilevel; 452 target->av_dip = endp->av_dip; 453 /* 454 * We have a hole here where the routine corresponding to 455 * endp may not get called. Do a wait_till_seen to take care 456 * of this. 457 */ 458 wait_till_seen(ipl); 459 endp->av_vector = NULL; 460 endp->av_ticksp = NULL; 461 } 462 463 if (lo_pri > hi_pri) { /* the chain is now empty */ 464 /* Leave the unused entries here for probable future use */ 465 vectp->avh_lo_pri = MAXIPL; 466 vectp->avh_hi_pri = 0; 467 } else { 468 if ((int)vectp->avh_lo_pri < lo_pri) 469 vectp->avh_lo_pri = (ushort_t)lo_pri; 470 if ((int)vectp->avh_hi_pri > hi_pri) 471 vectp->avh_hi_pri = (ushort_t)hi_pri; 472 } 473 mutex_exit(&av_lock); 474 wait_till_seen(ipl); 475 } 476 477 /* 478 * Trigger a soft interrupt. 479 */ 480 void 481 siron(void) 482 { 483 softlevel1_hdl.ih_pending = 1; 484 (*setsoftint)(1); 485 } 486 487 /* 488 * Walk the autovector table for this vector, invoking each 489 * interrupt handler as we go. 490 */ 491 492 extern uint64_t intr_get_time(void); 493 494 void 495 av_dispatch_autovect(uint_t vec) 496 { 497 struct autovec *av; 498 499 ASSERT_STACK_ALIGNED(); 500 501 while ((av = autovect[vec].avh_link) != NULL) { 502 uint_t numcalled = 0; 503 uint_t claimed = 0; 504 505 for (; av; av = av->av_link) { 506 uint_t r; 507 uint_t (*intr)() = av->av_vector; 508 caddr_t arg1 = av->av_intarg1; 509 caddr_t arg2 = av->av_intarg2; 510 dev_info_t *dip = av->av_dip; 511 512 numcalled++; 513 if (intr == NULL) 514 break; 515 516 DTRACE_PROBE4(interrupt__start, dev_info_t *, dip, 517 void *, intr, caddr_t, arg1, caddr_t, arg2); 518 r = (*intr)(arg1, arg2); 519 DTRACE_PROBE4(interrupt__complete, dev_info_t *, dip, 520 void *, intr, caddr_t, arg1, uint_t, r); 521 claimed |= r; 522 if (av->av_ticksp && av->av_prilevel <= LOCK_LEVEL) 523 atomic_add_64(av->av_ticksp, intr_get_time()); 524 } 525 526 /* 527 * If there's only one interrupt handler in the chain, 528 * or if no-one claimed the interrupt at all give up now. 529 */ 530 if (numcalled == 1 || claimed == 0) 531 break; 532 } 533 } 534 535 /* 536 * Call every soft interrupt handler we can find at this level once. 537 */ 538 void 539 av_dispatch_softvect(uint_t pil) 540 { 541 struct autovec *av; 542 ddi_softint_hdl_impl_t *hdlp; 543 uint_t (*intr)(); 544 caddr_t arg1; 545 caddr_t arg2; 546 547 ASSERT_STACK_ALIGNED(); 548 ASSERT(pil >= 0 && pil <= PIL_MAX); 549 550 for (av = softvect[pil].avh_link; av; av = av->av_link) { 551 if ((intr = av->av_vector) == NULL) 552 break; 553 arg1 = av->av_intarg1; 554 arg2 = av->av_intarg2; 555 556 hdlp = (ddi_softint_hdl_impl_t *)av->av_intr_id; 557 ASSERT(hdlp); 558 559 if (hdlp->ih_pending) { 560 hdlp->ih_pending = 0; 561 (void) (*intr)(arg1, arg2); 562 } 563 } 564 } 565 566 struct regs; 567 568 /* 569 * Call every NMI handler we know of once. 570 */ 571 void 572 av_dispatch_nmivect(struct regs *rp) 573 { 574 struct autovec *av; 575 576 ASSERT_STACK_ALIGNED(); 577 578 for (av = nmivect; av; av = av->av_link) 579 (void) (av->av_vector)(av->av_intarg1, rp); 580 } 581