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.
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.
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]
22 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Autovectored Interrupt Configuration and Deconfiguration
29 #include <sys/param.h>
30 #include <sys/cmn_err.h>
32 #include <sys/t_lock.h>
33 #include <sys/avintr.h>
35 #include <sys/machlock.h>
36 #include <sys/systm.h>
37 #include <sys/machsystm.h>
38 #include <sys/sunddi.h>
39 #include <sys/x_call.h>
40 #include <sys/cpuvar.h>
41 #include <sys/atomic.h>
42 #include <sys/smp_impldefs.h>
44 #include <sys/stack.h>
45 #include <sys/ddi_impldefs.h>
47 #include <sys/evtchn_impl.h>
50 typedef struct av_softinfo
{
51 cpuset_t av_pending
; /* pending bitmasks */
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
,
57 static void remove_av(void *intr_id
, struct av_head
*vectp
, avfunc f
,
58 int pri_level
, int vect
);
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.
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.";
79 #define MAX_VECT NR_IRQS
84 struct autovec
*nmivect
= NULL
;
85 struct av_head autovect
[MAX_VECT
];
86 struct av_head softvect
[LOCK_LEVEL
+ 1];
89 * These are software interrupt handlers dedicated to ddi timer.
90 * The interrupt levels up to 10 are supported, but high interrupts
91 * must not be used there.
93 ddi_softint_hdl_impl_t softlevel_hdl
[DDI_IPL_10
] = {
94 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 1 */
95 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 2 */
96 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 3 */
97 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 4 */
98 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 5 */
99 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 6 */
100 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 7 */
101 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 8 */
102 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 9 */
103 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
}, /* level 10 */
105 ddi_softint_hdl_impl_t softlevel1_hdl
=
106 {0, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
};
109 * clear/check softint pending flag corresponding for
113 av_clear_softint_pending(av_softinfo_t
*infop
)
115 CPUSET_ATOMIC_DEL(infop
->av_pending
, CPU
->cpu_seqid
);
119 av_check_softint_pending(av_softinfo_t
*infop
, boolean_t check_all
)
122 return (!CPUSET_ISNULL(infop
->av_pending
));
124 return (CPU_IN_SET(infop
->av_pending
, CPU
->cpu_seqid
) != 0);
128 * This is the wrapper function which is generally used to set a softint
132 av_set_softint_pending(int pri
, av_softinfo_t
*infop
)
134 kdi_av_set_softint_pending(pri
, infop
);
138 * This is kmdb's private entry point to setsoftint called from kdi_siron
139 * It first sets our av softint pending bit for the current CPU,
140 * then it sets the CPU softint pending bit for pri.
143 kdi_av_set_softint_pending(int pri
, av_softinfo_t
*infop
)
145 CPUSET_ATOMIC_ADD(infop
->av_pending
, CPU
->cpu_seqid
);
147 atomic_or_32((uint32_t *)&CPU
->cpu_softinfo
.st_pending
, 1 << pri
);
151 * register nmi interrupt routine. The first arg is used only to order
152 * various nmi interrupt service routines in the chain. Higher lvls will
156 add_nmintr(int lvl
, avfunc nmintr
, char *name
, caddr_t arg
)
159 struct autovec
*p
, *prev
= NULL
;
161 if (nmintr
== NULL
) {
162 printf("Attempt to add null vect for %s on nmi\n", name
);
167 mem
= kmem_zalloc(sizeof (struct autovec
), KM_SLEEP
);
168 mem
->av_vector
= nmintr
;
169 mem
->av_intarg1
= arg
;
170 mem
->av_intarg2
= NULL
;
171 mem
->av_intr_id
= NULL
;
172 mem
->av_prilevel
= lvl
;
176 mutex_enter(&av_lock
);
180 mutex_exit(&av_lock
);
183 /* find where it goes in list */
184 for (p
= nmivect
; p
!= NULL
; p
= p
->av_link
) {
185 if (p
->av_vector
== nmintr
&& p
->av_intarg1
== arg
) {
188 * So? Somebody added the same interrupt twice.
190 cmn_err(CE_WARN
, "Driver already registered '%s'",
192 kmem_free(mem
, sizeof (struct autovec
));
193 mutex_exit(&av_lock
);
196 if (p
->av_prilevel
< lvl
) {
197 if (p
== nmivect
) { /* it's at head of list */
204 mutex_exit(&av_lock
);
210 /* didn't find it, add it to the end */
212 mutex_exit(&av_lock
);
218 * register a hardware interrupt handler.
220 * The autovect data structure only supports globally 256 interrupts.
221 * In order to support 256 * #LocalAPIC interrupts, a new PSM module
222 * apix is introduced. It defines PSM private data structures for the
223 * interrupt handlers. The PSM module initializes addintr to a PSM
224 * private function so that it could override add_avintr() to operate
225 * on its private data structures.
228 add_avintr(void *intr_id
, int lvl
, avfunc xxintr
, char *name
, int vect
,
229 caddr_t arg1
, caddr_t arg2
, uint64_t *ticksp
, dev_info_t
*dip
)
231 struct av_head
*vecp
= (struct av_head
*)0;
233 int s
, vectindex
; /* save old spl value */
237 return ((*addintr
)(intr_id
, lvl
, xxintr
, name
, vect
,
238 arg1
, arg2
, ticksp
, dip
));
241 if ((f
= xxintr
) == NULL
) {
242 printf("Attempt to add null vect for %s on vector %d\n",
247 vectindex
= vect
% MAX_VECT
;
249 vecp
= &autovect
[vectindex
];
252 * "hi_pri == 0" implies all entries on list are "unused",
253 * which means that it's OK to just insert this one.
255 hi_pri
= vecp
->avh_hi_pri
;
256 if (vecp
->avh_link
&& (hi_pri
!= 0)) {
257 if (((hi_pri
> LOCK_LEVEL
) && (lvl
< LOCK_LEVEL
)) ||
258 ((hi_pri
< LOCK_LEVEL
) && (lvl
> LOCK_LEVEL
))) {
259 cmn_err(CE_WARN
, multilevel2
, name
, lvl
, vect
,
263 if ((vecp
->avh_lo_pri
!= lvl
) || (hi_pri
!= lvl
))
264 cmn_err(CE_NOTE
, multilevel
, vect
);
267 insert_av(intr_id
, vecp
, f
, arg1
, arg2
, ticksp
, lvl
, dip
);
270 * do what ever machine specific things are necessary
271 * to set priority level (e.g. set picmasks)
273 mutex_enter(&av_lock
);
274 (*addspl
)(vect
, lvl
, vecp
->avh_lo_pri
, vecp
->avh_hi_pri
);
275 mutex_exit(&av_lock
);
282 update_avsoftintr_args(void *intr_id
, int lvl
, caddr_t arg2
)
285 struct autovec
*target
= NULL
;
286 struct av_head
*vectp
= (struct av_head
*)&softvect
[lvl
];
288 for (p
= vectp
->avh_link
; p
&& p
->av_vector
; p
= p
->av_link
) {
289 if (p
->av_intr_id
== intr_id
) {
297 target
->av_intarg2
= arg2
;
301 * Register a software interrupt handler
304 add_avsoftintr(void *intr_id
, int lvl
, avfunc xxintr
, char *name
,
305 caddr_t arg1
, caddr_t arg2
)
308 ddi_softint_hdl_impl_t
*hdlp
= (ddi_softint_hdl_impl_t
*)intr_id
;
310 if ((slvl
= slvltovect(lvl
)) != -1)
311 return (add_avintr(intr_id
, lvl
, xxintr
,
312 name
, slvl
, arg1
, arg2
, NULL
, NULL
));
314 if (intr_id
== NULL
) {
315 printf("Attempt to add null intr_id for %s on level %d\n",
320 if (xxintr
== NULL
) {
321 printf("Attempt to add null handler for %s on level %d\n",
326 if (lvl
<= 0 || lvl
> LOCK_LEVEL
) {
327 printf(badsoft
, lvl
, name
);
331 if (hdlp
->ih_pending
== NULL
) {
333 kmem_zalloc(sizeof (av_softinfo_t
), KM_SLEEP
);
336 insert_av(intr_id
, &softvect
[lvl
], xxintr
, arg1
, arg2
, NULL
, lvl
, NULL
);
342 * insert an interrupt vector into chain by its priority from high
346 insert_av(void *intr_id
, struct av_head
*vectp
, avfunc f
, caddr_t arg1
,
347 caddr_t arg2
, uint64_t *ticksp
, int pri_level
, dev_info_t
*dip
)
350 * Protect rewrites of the list
352 struct autovec
*p
, *prep
, *mem
;
354 mem
= kmem_zalloc(sizeof (struct autovec
), KM_SLEEP
);
356 mem
->av_intarg1
= arg1
;
357 mem
->av_intarg2
= arg2
;
358 mem
->av_ticksp
= ticksp
;
359 mem
->av_intr_id
= intr_id
;
360 mem
->av_prilevel
= pri_level
;
364 mutex_enter(&av_lock
);
366 if (vectp
->avh_link
== NULL
) { /* Nothing on list - put it at head */
367 vectp
->avh_link
= mem
;
368 vectp
->avh_hi_pri
= vectp
->avh_lo_pri
= (ushort_t
)pri_level
;
370 mutex_exit(&av_lock
);
374 /* find where it goes in list */
376 for (p
= vectp
->avh_link
; p
!= NULL
; p
= p
->av_link
) {
377 if (p
->av_vector
&& p
->av_prilevel
<= pri_level
)
382 if (prep
->av_vector
== NULL
) { /* freed struct available */
384 p
->av_intarg1
= arg1
;
385 p
->av_intarg2
= arg2
;
386 p
->av_ticksp
= ticksp
;
387 p
->av_intr_id
= intr_id
;
388 p
->av_prilevel
= pri_level
;
390 if (pri_level
> (int)vectp
->avh_hi_pri
) {
391 vectp
->avh_hi_pri
= (ushort_t
)pri_level
;
393 if (pri_level
< (int)vectp
->avh_lo_pri
) {
394 vectp
->avh_lo_pri
= (ushort_t
)pri_level
;
397 * To prevent calling service routine before args
398 * and ticksp are ready fill in vector last.
401 mutex_exit(&av_lock
);
402 kmem_free(mem
, sizeof (struct autovec
));
406 mem
->av_link
= prep
->av_link
;
409 /* insert new intpt at beginning of chain */
410 mem
->av_link
= vectp
->avh_link
;
411 vectp
->avh_link
= mem
;
413 if (pri_level
> (int)vectp
->avh_hi_pri
) {
414 vectp
->avh_hi_pri
= (ushort_t
)pri_level
;
416 if (pri_level
< (int)vectp
->avh_lo_pri
) {
417 vectp
->avh_lo_pri
= (ushort_t
)pri_level
;
419 mutex_exit(&av_lock
);
423 av_rem_softintr(void *intr_id
, int lvl
, avfunc xxintr
, boolean_t rem_softinfo
)
425 struct av_head
*vecp
= (struct av_head
*)0;
427 ddi_softint_hdl_impl_t
*hdlp
= (ddi_softint_hdl_impl_t
*)intr_id
;
428 av_softinfo_t
*infop
= (av_softinfo_t
*)hdlp
->ih_pending
;
433 if ((slvl
= slvltovect(lvl
)) != -1) {
434 rem_avintr(intr_id
, lvl
, xxintr
, slvl
);
438 if (lvl
<= 0 && lvl
>= LOCK_LEVEL
) {
441 vecp
= &softvect
[lvl
];
442 remove_av(intr_id
, vecp
, xxintr
, lvl
, 0);
445 kmem_free(infop
, sizeof (av_softinfo_t
));
446 hdlp
->ih_pending
= NULL
;
453 av_softint_movepri(void *intr_id
, int old_lvl
)
456 ddi_softint_hdl_impl_t
*hdlp
= (ddi_softint_hdl_impl_t
*)intr_id
;
458 ret
= add_avsoftintr(intr_id
, hdlp
->ih_pri
, hdlp
->ih_cb_func
,
459 DEVI(hdlp
->ih_dip
)->devi_name
, hdlp
->ih_cb_arg1
, hdlp
->ih_cb_arg2
);
462 (void) av_rem_softintr(intr_id
, old_lvl
, hdlp
->ih_cb_func
,
470 * Remove a driver from the autovector list.
473 rem_avsoftintr(void *intr_id
, int lvl
, avfunc xxintr
)
475 return (av_rem_softintr(intr_id
, lvl
, xxintr
, B_TRUE
));
479 * Remove specified interrupt handler.
481 * PSM module could initialize remintr to some PSM private function
482 * so that it could override rem_avintr() to operate on its private
486 rem_avintr(void *intr_id
, int lvl
, avfunc xxintr
, int vect
)
488 struct av_head
*vecp
= (struct av_head
*)0;
490 int s
, vectindex
; /* save old spl value */
493 (*remintr
)(intr_id
, lvl
, xxintr
, vect
);
497 if ((f
= xxintr
) == NULL
)
500 vectindex
= vect
% MAX_VECT
;
501 vecp
= &autovect
[vectindex
];
502 remove_av(intr_id
, vecp
, f
, lvl
, vect
);
504 mutex_enter(&av_lock
);
505 (*delspl
)(vect
, lvl
, vecp
->avh_lo_pri
, vecp
->avh_hi_pri
);
506 mutex_exit(&av_lock
);
512 * After having made a change to an autovector list, wait until we have
513 * seen each cpu not executing an interrupt at that level--so we know our
514 * change has taken effect completely (no old state in registers, etc).
517 wait_till_seen(int ipl
)
519 int cpu_in_chain
, cix
;
521 cpuset_t cpus_to_check
;
523 CPUSET_ALL(cpus_to_check
);
526 for (cix
= 0; cix
< NCPU
; cix
++) {
528 if (cpup
!= NULL
&& CPU_IN_SET(cpus_to_check
, cix
)) {
529 if (INTR_ACTIVE(cpup
, ipl
)) {
532 CPUSET_DEL(cpus_to_check
, cix
);
536 } while (cpu_in_chain
);
539 static uint64_t dummy_tick
;
541 /* remove an interrupt vector from the chain */
543 remove_av(void *intr_id
, struct av_head
*vectp
, avfunc f
, int pri_level
,
546 struct autovec
*p
, *target
;
550 * Protect rewrites of the list
554 mutex_enter(&av_lock
);
558 for (p
= vectp
->avh_link
; p
; p
= p
->av_link
) {
559 if ((p
->av_vector
== f
) && (p
->av_intr_id
== intr_id
)) {
560 /* found the handler */
564 if (p
->av_vector
!= NULL
) {
565 if (p
->av_prilevel
> hi_pri
)
566 hi_pri
= p
->av_prilevel
;
567 if (p
->av_prilevel
< lo_pri
)
568 lo_pri
= p
->av_prilevel
;
573 if (target
== NULL
) { /* not found */
574 printf("Couldn't remove function %p at %d, %d\n",
575 (void *)f
, vect
, pri_level
);
576 mutex_exit(&av_lock
);
581 * This drops the handler from the chain, it can no longer be called.
582 * However, there is no guarantee that the handler is not currently
585 target
->av_vector
= NULL
;
587 * There is a race where we could be just about to pick up the ticksp
588 * pointer to increment it after returning from the service routine
589 * in av_dispatch_autovect. Rather than NULL it out let's just point
590 * it off to something safe so that any final tick update attempt
593 target
->av_ticksp
= &dummy_tick
;
596 if (lo_pri
> hi_pri
) { /* the chain is now empty */
597 /* Leave the unused entries here for probable future use */
598 vectp
->avh_lo_pri
= MAXIPL
;
599 vectp
->avh_hi_pri
= 0;
601 if ((int)vectp
->avh_lo_pri
< lo_pri
)
602 vectp
->avh_lo_pri
= (ushort_t
)lo_pri
;
603 if ((int)vectp
->avh_hi_pri
> hi_pri
)
604 vectp
->avh_hi_pri
= (ushort_t
)hi_pri
;
606 mutex_exit(&av_lock
);
611 * kmdb uses siron (and thus setsoftint) while the world is stopped in order to
612 * inform its driver component that there's work to be done. We need to keep
613 * DTrace from instrumenting kmdb's siron and setsoftint. We duplicate siron,
614 * giving kmdb's version a kdi prefix to keep DTrace at bay. We also
615 * provide a version of the various setsoftint functions available for kmdb to
616 * use using a kdi_ prefix while the main *setsoftint() functionality is
617 * implemented as a wrapper. This allows tracing, while still providing a
618 * way for kmdb to sneak in unmolested.
623 (*kdisetsoftint
)(1, softlevel1_hdl
.ih_pending
);
627 * Trigger a soft interrupt.
632 /* Level 1 software interrupt */
633 (*setsoftint
)(1, softlevel1_hdl
.ih_pending
);
637 * Trigger software interrupts dedicated to ddi timer.
642 ASSERT(level
>= DDI_IPL_1
&& level
<= DDI_IPL_10
);
643 (*setsoftint
)(level
, softlevel_hdl
[level
-1].ih_pending
);
647 * The handler which is executed on the target CPU.
651 siron_poke_intr(xc_arg_t a1
, xc_arg_t a2
, xc_arg_t a3
)
658 * May get called from softcall to poke CPUs.
661 siron_poke_cpu(cpuset_t poke
)
663 int cpuid
= CPU
->cpu_id
;
666 * If we are poking to ourself then we can simply
667 * generate level1 using siron()
669 if (CPU_IN_SET(poke
, cpuid
)) {
671 CPUSET_DEL(poke
, cpuid
);
672 if (CPUSET_ISNULL(poke
))
676 xc_call(0, 0, 0, CPUSET2BV(poke
), (xc_func_t
)siron_poke_intr
);
680 * Walk the autovector table for this vector, invoking each
681 * interrupt handler as we go.
684 extern uint64_t intr_get_time(void);
687 av_dispatch_autovect(uint_t vec
)
691 ASSERT_STACK_ALIGNED();
693 while ((av
= autovect
[vec
].avh_link
) != NULL
) {
694 uint_t numcalled
= 0;
697 for (; av
; av
= av
->av_link
) {
699 uint_t (*intr
)() = av
->av_vector
;
700 caddr_t arg1
= av
->av_intarg1
;
701 caddr_t arg2
= av
->av_intarg2
;
702 dev_info_t
*dip
= av
->av_dip
;
705 * We must walk the entire chain. Removed handlers
706 * may be anywhere in the chain.
711 DTRACE_PROBE4(interrupt__start
, dev_info_t
*, dip
,
712 void *, intr
, caddr_t
, arg1
, caddr_t
, arg2
);
713 r
= (*intr
)(arg1
, arg2
);
714 DTRACE_PROBE4(interrupt__complete
, dev_info_t
*, dip
,
715 void *, intr
, caddr_t
, arg1
, uint_t
, r
);
718 if (av
->av_ticksp
&& av
->av_prilevel
<= LOCK_LEVEL
)
719 atomic_add_64(av
->av_ticksp
, intr_get_time());
723 * If there's only one interrupt handler in the chain,
724 * or if no-one claimed the interrupt at all give up now.
726 if (numcalled
== 1 || claimed
== 0)
732 * Call every soft interrupt handler we can find at this level once.
735 av_dispatch_softvect(uint_t pil
)
738 ddi_softint_hdl_impl_t
*hdlp
;
743 ASSERT_STACK_ALIGNED();
744 ASSERT3U(pil
, <=, PIL_MAX
);
746 for (av
= softvect
[pil
].avh_link
; av
; av
= av
->av_link
) {
748 * We must walk the entire chain. Removed handlers
749 * may be anywhere in the chain.
751 if ((intr
= av
->av_vector
) == NULL
)
753 arg1
= av
->av_intarg1
;
754 arg2
= av
->av_intarg2
;
756 hdlp
= (ddi_softint_hdl_impl_t
*)av
->av_intr_id
;
760 * Each cpu has its own pending bit in hdlp->ih_pending,
761 * here av_check/clear_softint_pending is just checking
762 * and clearing the pending bit for the current cpu, who
763 * has just triggered a softint.
765 if (av_check_softint_pending(hdlp
->ih_pending
, B_FALSE
)) {
766 av_clear_softint_pending(hdlp
->ih_pending
);
767 (void) (*intr
)(arg1
, arg2
);
775 * Call every NMI handler we know of once.
778 av_dispatch_nmivect(struct regs
*rp
)
782 ASSERT_STACK_ALIGNED();
784 for (av
= nmivect
; av
; av
= av
->av_link
)
785 (void) (av
->av_vector
)(av
->av_intarg1
, rp
);