xref: /illumos-gate/usr/src/uts/common/os/softint.c (revision 0173c38a73f34277e0c97a19fedfd25d81ba8380)
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 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/t_lock.h>
32 #include <sys/systm.h>
33 #include <sys/spl.h>
34 #include <sys/cmn_err.h>
35 #include <sys/debug.h>
36 #include <sys/kdi_impl.h>
37 
38 /*
39  * Handle software interrupts through 'softcall' mechanism
40  *
41  * At present softcall mechanism uses a global list headed by softhead.
42  * Entries are added to tail and removed from head so as to preserve FIFO
43  * nature of entries in the softcall list. softcall() takes care of adding
44  * entries to the softtail.
45  *
46  * softint must take care of executing the entries in the FIFO
47  * order. It could be called simultaneously from multiple cpus, however only
48  * one instance of softint should process the softcall list, this is
49  * ensured by
50  * - the state the variable softcall_state will be at time to time.
51  *   (IDLE->PEND->DRAIN->IDLE)
52  *
53  * These states are needed for softcall mechanism since  Solaris has only
54  * one interface(ie. siron ) as of now for
55  * - raising a soft interrupt architecture independently(ie not through
56  *   setsoftint(..) )
57  * - to process the softcall queue.
58  */
59 
60 #define	NSOFTCALLS	200
61 /*
62  * Defined states for softcall processing.
63  */
64 #define	SOFT_IDLE		0x01	/* no processing is needed */
65 #define	SOFT_PEND		0x02	/* softcall list needs processing */
66 #define	SOFT_DRAIN		0x04	/* the list is being processed */
67 
68 typedef struct softcall {
69 	void (*sc_func)(void *);	/* function to call */
70 	void *sc_arg;			/* arg to pass to func */
71 	struct softcall *sc_next;	/* next in list */
72 } softcall_t;
73 
74 static softcall_t softcalls[NSOFTCALLS], *softhead, *softtail, *softfree;
75 static uint_t	softcall_state;
76 
77 /*
78  * protects softcall lists and control variable softcall_state.
79  */
80 static kmutex_t	softcall_lock;
81 
82 static void (*kdi_softcall_func)(void);
83 
84 extern void siron(void);
85 
86 void
87 softcall_init(void)
88 {
89 	softcall_t *sc;
90 
91 	for (sc = softcalls; sc < &softcalls[NSOFTCALLS]; sc++) {
92 		sc->sc_next = softfree;
93 		softfree = sc;
94 	}
95 	mutex_init(&softcall_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
96 }
97 
98 /*
99  * Call function func with argument arg
100  * at some later time at software interrupt priority
101  */
102 void
103 softcall(void (*func)(void *), void *arg)
104 {
105 	softcall_t *sc;
106 
107 	/*
108 	 * protect against cross-calls
109 	 */
110 	mutex_enter(&softcall_lock);
111 	/* coalesce identical softcalls */
112 	for (sc = softhead; sc != 0; sc = sc->sc_next) {
113 		if (sc->sc_func == func && sc->sc_arg == arg) {
114 			mutex_exit(&softcall_lock);
115 			return;
116 		}
117 	}
118 
119 	if ((sc = softfree) == 0)
120 		panic("too many softcalls");
121 	softfree = sc->sc_next;
122 	sc->sc_func = func;
123 	sc->sc_arg = arg;
124 	sc->sc_next = 0;
125 
126 	if (softhead) {
127 		softtail->sc_next = sc;
128 		softtail = sc;
129 		mutex_exit(&softcall_lock);
130 	} else {
131 		softhead = softtail = sc;
132 		if (softcall_state == SOFT_DRAIN)
133 			/*
134 			 * softint is already running; no need to
135 			 * raise a siron. Due to lock protection of
136 			 * softhead / softcall state, we know
137 			 * that softint() will see the new addition to
138 			 * the softhead queue.
139 			 */
140 			mutex_exit(&softcall_lock);
141 		else {
142 			softcall_state = SOFT_PEND;
143 			mutex_exit(&softcall_lock);
144 			siron();
145 		}
146 	}
147 }
148 
149 void
150 kdi_softcall(void (*func)(void))
151 {
152 	kdi_softcall_func = func;
153 
154 	if (softhead == NULL)
155 		siron();
156 }
157 
158 /*
159  * Called to process software interrupts take one off queue, call it,
160  * repeat.
161  *
162  * Note queue may change during call; softcall_lock and state variables
163  * softcall_state ensures that
164  * -we don't have multiple cpus pulling from the list (thus causing
165  *  a violation of FIFO order).
166  * -we don't miss a new entry having been added to the head.
167  * -we don't miss a wakeup.
168  */
169 
170 void
171 softint(void)
172 {
173 	softcall_t *sc;
174 	void (*func)();
175 	caddr_t arg;
176 
177 	/*
178 	 * Check if we are asked to process the softcall list.
179 	 */
180 	mutex_enter(&softcall_lock);
181 	if (softcall_state != SOFT_PEND) {
182 		mutex_exit(&softcall_lock);
183 		goto out;
184 	}
185 	softcall_state = SOFT_DRAIN;
186 
187 	for (;;) {
188 		if ((sc = softhead) != NULL) {
189 			func = sc->sc_func;
190 			arg = sc->sc_arg;
191 			softhead = sc->sc_next;
192 			sc->sc_next = softfree;
193 			softfree = sc;
194 		}
195 		if (sc == NULL) {
196 			softcall_state = SOFT_IDLE;
197 			mutex_exit(&softcall_lock);
198 			break;
199 		}
200 		mutex_exit(&softcall_lock);
201 		func(arg);
202 		mutex_enter(&softcall_lock);
203 	}
204 out:
205 	if ((func = kdi_softcall_func) != NULL) {
206 		kdi_softcall_func = NULL;
207 		func();
208 	}
209 }
210