kern_intr.c revision 67365
1/*
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/kern/kern_intr.c 67365 2000-10-20 07:58:15Z jhb $
27 *
28 */
29
30
31#include <sys/param.h>
32#include <sys/bus.h>
33#include <sys/rtprio.h>
34#include <sys/systm.h>
35#include <sys/ipl.h>
36#include <sys/interrupt.h>
37#include <sys/kernel.h>
38#include <sys/kthread.h>
39#include <sys/ktr.h>
40#include <sys/malloc.h>
41#include <sys/mutex.h>
42#include <sys/proc.h>
43#include <sys/unistd.h>
44#include <sys/vmmeter.h>
45#include <machine/atomic.h>
46#include <machine/cpu.h>
47
48struct swilist {
49	swihand_t	*sl_handler;
50	struct swilist	*sl_next;
51};
52
53static struct swilist swilists[NSWI];
54u_long softintr_count[NSWI];
55static struct proc *softithd;
56volatile u_int sdelayed;
57volatile u_int spending;
58
59static void start_softintr(void *);
60static void intr_soft(void *);
61
62void
63register_swi(intr, handler)
64	int intr;
65	swihand_t *handler;
66{
67	struct swilist *slp, *slq;
68	int s;
69
70	if (intr < 0 || intr >= NSWI)
71		panic("register_swi: bad intr %d", intr);
72	if (handler == swi_generic || handler == swi_null)
73		panic("register_swi: bad handler %p", (void *)handler);
74	slp = &swilists[intr];
75	s = splhigh();
76	if (shandlers[intr] == swi_null)
77		shandlers[intr] = handler;
78	else {
79		if (slp->sl_next == NULL) {
80			slp->sl_handler = shandlers[intr];
81			shandlers[intr] = swi_generic;
82		}
83		slq = malloc(sizeof(*slq), M_DEVBUF, M_NOWAIT);
84		if (slq == NULL)
85			panic("register_swi: malloc failed");
86		slq->sl_handler = handler;
87		slq->sl_next = NULL;
88		while (slp->sl_next != NULL)
89			slp = slp->sl_next;
90		slp->sl_next = slq;
91	}
92	splx(s);
93}
94
95void
96swi_dispatcher(intr)
97	int intr;
98{
99	struct swilist *slp;
100
101	slp = &swilists[intr];
102	do {
103		(*slp->sl_handler)();
104		slp = slp->sl_next;
105	} while (slp != NULL);
106}
107
108void
109unregister_swi(intr, handler)
110	int intr;
111	swihand_t *handler;
112{
113	struct swilist *slfoundpred, *slp, *slq;
114	int s;
115
116	if (intr < 0 || intr >= NSWI)
117		panic("unregister_swi: bad intr %d", intr);
118	if (handler == swi_generic || handler == swi_null)
119		panic("unregister_swi: bad handler %p", (void *)handler);
120	slp = &swilists[intr];
121	s = splhigh();
122	if (shandlers[intr] == handler)
123		shandlers[intr] = swi_null;
124	else if (slp->sl_next != NULL) {
125		slfoundpred = NULL;
126		for (slq = slp->sl_next; slq != NULL;
127		    slp = slq, slq = slp->sl_next)
128			if (slq->sl_handler == handler)
129				slfoundpred = slp;
130		slp = &swilists[intr];
131		if (slfoundpred != NULL) {
132			slq = slfoundpred->sl_next;
133			slfoundpred->sl_next = slq->sl_next;
134			free(slq, M_DEVBUF);
135		} else if (slp->sl_handler == handler) {
136			slq = slp->sl_next;
137			slp->sl_next = slq->sl_next;
138			slp->sl_handler = slq->sl_handler;
139			free(slq, M_DEVBUF);
140		}
141		if (slp->sl_next == NULL)
142			shandlers[intr] = slp->sl_handler;
143	}
144	splx(s);
145}
146
147int
148ithread_priority(flags)
149	int flags;
150{
151	int pri;
152
153	switch (flags) {
154	case INTR_TYPE_TTY:             /* keyboard or parallel port */
155		pri = PI_TTYLOW;
156		break;
157	case (INTR_TYPE_TTY | INTR_FAST): /* sio */
158		pri = PI_TTYHIGH;
159		break;
160	case INTR_TYPE_BIO:
161		/*
162		 * XXX We need to refine this.  BSD/OS distinguishes
163		 * between tape and disk priorities.
164		 */
165		pri = PI_DISK;
166		break;
167	case INTR_TYPE_NET:
168		pri = PI_NET;
169		break;
170	case INTR_TYPE_CAM:
171		pri = PI_DISK;          /* XXX or PI_CAM? */
172		break;
173	case INTR_TYPE_MISC:
174		pri = PI_DULL;          /* don't care */
175		break;
176	/* We didn't specify an interrupt level. */
177	default:
178		panic("ithread_priority: no interrupt type in flags");
179	}
180
181	return pri;
182}
183
184/*
185 * Schedule the soft interrupt handler thread.
186 */
187void
188sched_softintr(void)
189{
190	atomic_add_int(&cnt.v_intr, 1); /* one more global interrupt */
191
192	/*
193	 * If we don't have an interrupt resource or an interrupt thread for
194	 * this IRQ, log it as a stray interrupt.
195	 */
196	if (softithd == NULL)
197		panic("soft interrupt scheduled too early");
198
199	CTR3(KTR_INTR, "sched_softintr pid %d(%s) spending=0x%x",
200		softithd->p_pid, softithd->p_comm, spending);
201
202	/*
203	 * Get the sched lock and see if the thread is on whichkqs yet.
204	 * If not, put it on there.  In any case, kick everyone so that if
205	 * the new thread is higher priority than their current thread, it
206	 * gets run now.
207	 */
208	mtx_enter(&sched_lock, MTX_SPIN);
209	if (softithd->p_stat == SWAIT) { /* not on run queue */
210		CTR1(KTR_INTR, "sched_softintr: setrunqueue %d",
211		    softithd->p_pid);
212/*		membar_lock(); */
213		softithd->p_stat = SRUN;
214		setrunqueue(softithd);
215		aston();
216	}
217	mtx_exit(&sched_lock, MTX_SPIN);
218#if 0
219	aston();			/* ??? check priorities first? */
220#else
221	need_resched();
222#endif
223}
224
225SYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL)
226
227/*
228 * Start soft interrupt thread.
229 */
230static void
231start_softintr(dummy)
232	void *dummy;
233{
234	int error;
235
236	if (softithd != NULL) {		/* we already have a thread */
237		printf("start_softintr: already running");
238		return;
239	}
240
241	error = kthread_create(intr_soft, NULL, &softithd,
242		RFSTOPPED | RFHIGHPID, "softinterrupt");
243	if (error)
244		panic("start_softintr: kthread_create error %d\n", error);
245
246	softithd->p_rtprio.type = RTP_PRIO_ITHREAD;
247	softithd->p_rtprio.prio = PI_SOFT;	/* soft interrupt */
248	softithd->p_stat = SWAIT;		/* we're idle */
249	softithd->p_flag |= P_NOLOAD;
250}
251
252/*
253 * Software interrupt process code.
254 */
255static void
256intr_soft(dummy)
257	void *dummy;
258{
259	int i;
260	u_int pend;
261
262	/* Main loop */
263	for (;;) {
264		CTR3(KTR_INTR, "intr_soft pid %d(%s) spending=0x%x",
265		    curproc->p_pid, curproc->p_comm, spending);
266
267		/*
268		 * Service interrupts.  If another interrupt arrives
269		 * while we are running, they will set spending to
270		 * denote that we should make another pass.
271		 */
272		pend = atomic_readandclear_int(&spending);
273		while ((i = ffs(pend))) {
274			i--;
275			atomic_add_long(&softintr_count[i], 1);
276			pend &= ~ (1 << i);
277			mtx_enter(&Giant, MTX_DEF);
278			if (shandlers[i] == swi_generic)
279				swi_dispatcher(i);
280			else
281				(shandlers[i])();
282			mtx_exit(&Giant, MTX_DEF);
283		}
284		/*
285		 * Processed all our interrupts.  Now get the sched
286		 * lock.  This may take a while and spending may get
287		 * set again, so we have to check it again.
288		 */
289		mtx_enter(&sched_lock, MTX_SPIN);
290		if (spending == 0) {
291			CTR1(KTR_INTR, "intr_soft pid %d: done",
292			    curproc->p_pid);
293			curproc->p_stat = SWAIT; /* we're idle */
294			mi_switch();
295			CTR1(KTR_INTR, "intr_soft pid %d: resumed",
296			    curproc->p_pid);
297		}
298		mtx_exit(&sched_lock, MTX_SPIN);
299	}
300}
301
302/*
303 * Bits in the spending bitmap variable must be set atomically because
304 * spending may be manipulated by interrupts or other cpu's without holding
305 * any locks.
306 *
307 * Note: setbits uses a locked or, making simple cases MP safe.
308 */
309#define DO_SETBITS(name, var, bits) \
310void name(void)					\
311{						\
312	atomic_set_int(var, bits);		\
313	sched_softintr();			\
314}
315
316#define DO_SETBITS_AND_NO_MORE(name, var, bits)	\
317void name(void)					\
318{						\
319	atomic_set_int(var, bits);		\
320}
321
322DO_SETBITS(setsoftcamnet,&spending, SWI_CAMNET_PENDING)
323DO_SETBITS(setsoftcambio,&spending, SWI_CAMBIO_PENDING)
324DO_SETBITS(setsoftclock, &spending, SWI_CLOCK_PENDING)
325DO_SETBITS(setsoftnet,   &spending, SWI_NET_PENDING)
326DO_SETBITS(setsofttty,   &spending, SWI_TTY_PENDING)
327DO_SETBITS(setsoftvm,	 &spending, SWI_VM_PENDING)
328DO_SETBITS(setsofttq,	 &spending, SWI_TQ_PENDING)
329
330DO_SETBITS_AND_NO_MORE(schedsoftcamnet, &sdelayed, SWI_CAMNET_PENDING)
331DO_SETBITS_AND_NO_MORE(schedsoftcambio, &sdelayed, SWI_CAMBIO_PENDING)
332DO_SETBITS_AND_NO_MORE(schedsoftnet, &sdelayed, SWI_NET_PENDING)
333DO_SETBITS_AND_NO_MORE(schedsofttty, &sdelayed, SWI_TTY_PENDING)
334DO_SETBITS_AND_NO_MORE(schedsoftvm, &sdelayed, SWI_VM_PENDING)
335DO_SETBITS_AND_NO_MORE(schedsofttq, &sdelayed, SWI_TQ_PENDING)
336
337void
338setdelayed(void)
339{
340	int pend;
341
342	pend = atomic_readandclear_int(&sdelayed);
343	if (pend != 0) {
344		atomic_set_int(&spending, pend);
345		sched_softintr();
346	}
347}
348
349intrmask_t
350softclockpending(void)
351{
352	return (spending & SWI_CLOCK_PENDING);
353}
354
355/*
356 * Dummy spl calls.  The only reason for these is to not break
357 * all the code which expects to call them.
358 */
359void spl0 (void) {}
360void splx (intrmask_t x) {}
361intrmask_t  splq(intrmask_t mask) { return 0; }
362intrmask_t  splbio(void) { return 0; }
363intrmask_t  splcam(void) { return 0; }
364intrmask_t  splclock(void) { return 0; }
365intrmask_t  splhigh(void) { return 0; }
366intrmask_t  splimp(void) { return 0; }
367intrmask_t  splnet(void) { return 0; }
368intrmask_t  splsoftcam(void) { return 0; }
369intrmask_t  splsoftcambio(void) { return 0; }
370intrmask_t  splsoftcamnet(void) { return 0; }
371intrmask_t  splsoftclock(void) { return 0; }
372intrmask_t  splsofttty(void) { return 0; }
373intrmask_t  splsoftvm(void) { return 0; }
374intrmask_t  splsofttq(void) { return 0; }
375intrmask_t  splstatclock(void) { return 0; }
376intrmask_t  spltty(void) { return 0; }
377intrmask_t  splvm(void) { return 0; }
378