kern_intr.c revision 69586
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 69586 2000-12-05 00:36:00Z jake $
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#include <machine/md_var.h>
48
49#include <net/netisr.h>		/* prototype for legacy_setsoftnet */
50
51struct intrhand *net_ih;
52struct intrhand *vm_ih;
53struct intrhand *softclock_ih;
54struct ithd	*clk_ithd;
55struct ithd	*tty_ithd;
56
57static void start_softintr(void *);
58static void swi_net(void *);
59
60int
61ithread_priority(flags)
62	int flags;
63{
64	int pri;
65
66	flags &= ~INTR_MPSAFE;
67	switch (flags) {
68	case INTR_TYPE_TTY:             /* keyboard or parallel port */
69		pri = PI_TTYLOW;
70		break;
71	case (INTR_TYPE_TTY | INTR_FAST): /* sio */
72		pri = PI_TTYHIGH;
73		break;
74	case INTR_TYPE_BIO:
75		/*
76		 * XXX We need to refine this.  BSD/OS distinguishes
77		 * between tape and disk priorities.
78		 */
79		pri = PI_DISK;
80		break;
81	case INTR_TYPE_NET:
82		pri = PI_NET;
83		break;
84	case INTR_TYPE_CAM:
85		pri = PI_DISK;          /* XXX or PI_CAM? */
86		break;
87	case INTR_TYPE_MISC:
88		pri = PI_DULL;          /* don't care */
89		break;
90	/* We didn't specify an interrupt level. */
91	default:
92		panic("ithread_priority: no interrupt type in flags");
93	}
94
95	return pri;
96}
97
98void sithd_loop(void *);
99
100struct intrhand *
101sinthand_add(const char *name, struct ithd **ithdp, driver_intr_t handler,
102	    void *arg, int pri, int flags)
103{
104	struct proc *p;
105	struct ithd *ithd;
106	struct intrhand *ih;
107	struct intrhand *this_ih;
108
109	ithd = (ithdp != NULL) ? *ithdp : NULL;
110
111
112	if (ithd == NULL) {
113		int error;
114		ithd = malloc(sizeof (struct ithd), M_DEVBUF, M_WAITOK | M_ZERO);
115		error = kthread_create(sithd_loop, NULL, &p,
116			RFSTOPPED | RFHIGHPID, "swi%d: %s", pri, name);
117		if (error)
118			panic("inthand_add: Can't create interrupt thread");
119		ithd->it_proc = p;
120		p->p_ithd = ithd;
121		p->p_rtprio.type = RTP_PRIO_ITHREAD;
122		p->p_rtprio.prio = pri + PI_SOFT;	/* soft interrupt */
123		p->p_stat = SWAIT;			/* we're idle */
124		/* XXX - some hacks are _really_ gross */
125		if (pri == SWI_CLOCK)
126			p->p_flag |= P_NOLOAD;
127		if (ithdp != NULL)
128			*ithdp = ithd;
129	}
130	this_ih = malloc(sizeof (struct intrhand), M_DEVBUF, M_WAITOK | M_ZERO);
131	this_ih->ih_handler = handler;
132	this_ih->ih_argument = arg;
133	this_ih->ih_flags = flags;
134	this_ih->ih_ithd = ithd;
135	this_ih->ih_name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
136	if ((ih = ithd->it_ih)) {
137		while (ih->ih_next != NULL)
138			ih = ih->ih_next;
139		ih->ih_next = this_ih;
140	} else
141		ithd->it_ih = this_ih;
142	strcpy(this_ih->ih_name, name);
143	return (this_ih);
144}
145
146
147/*
148 * Schedule a heavyweight software interrupt process.
149 */
150void
151sched_swi(struct intrhand *ih, int flag)
152{
153	struct ithd *it = ih->ih_ithd;	/* and the process that does it */
154	struct proc *p = it->it_proc;
155
156	atomic_add_int(&cnt.v_intr, 1); /* one more global interrupt */
157
158	CTR3(KTR_INTR, "sched_sihand pid %d(%s) need=%d",
159		p->p_pid, p->p_comm, it->it_need);
160
161	/*
162	 * Set it_need so that if the thread is already running but close
163	 * to done, it will do another go-round.  Then get the sched lock
164	 * and see if the thread is on whichkqs yet.  If not, put it on
165	 * there.  In any case, kick everyone so that if the new thread
166	 * is higher priority than their current thread, it gets run now.
167	 */
168	ih->ih_need = 1;
169	if (!(flag & SWI_DELAY)) {
170		it->it_need = 1;
171		mtx_enter(&sched_lock, MTX_SPIN);
172		if (p->p_stat == SWAIT) { /* not on run queue */
173			CTR1(KTR_INTR, "sched_swi: setrunqueue %d", p->p_pid);
174/*			membar_lock(); */
175			p->p_stat = SRUN;
176			setrunqueue(p);
177			aston();
178		}
179		else {
180			CTR3(KTR_INTR, "sched_swi %d: it_need %d, state %d",
181				p->p_pid, it->it_need, p->p_stat );
182		}
183		mtx_exit(&sched_lock, MTX_SPIN);
184		need_resched();
185	}
186}
187
188/*
189 * This is the main code for soft interrupt threads.
190 */
191void
192sithd_loop(void *dummy)
193{
194	struct ithd *it;		/* our thread context */
195	struct intrhand *ih;		/* and our interrupt handler chain */
196
197	struct proc *p = curproc;
198	it = p->p_ithd;			/* point to myself */
199
200	/*
201	 * As long as we have interrupts outstanding, go through the
202	 * list of handlers, giving each one a go at it.
203	 */
204	for (;;) {
205		CTR3(KTR_INTR, "sithd_loop pid %d(%s) need=%d",
206		     p->p_pid, p->p_comm, it->it_need);
207		while (it->it_need) {
208			/*
209			 * Service interrupts.  If another interrupt
210			 * arrives while we are running, they will set
211			 * it_need to denote that we should make
212			 * another pass.
213			 */
214			it->it_need = 0;
215			for (ih = it->it_ih; ih != NULL; ih = ih->ih_next) {
216				if (!ih->ih_need)
217					continue;
218				ih->ih_need = 0;
219				CTR5(KTR_INTR,
220				    "sithd_loop pid %d ih=%p: %p(%p) flg=%x",
221				    p->p_pid, (void *)ih,
222				    (void *)ih->ih_handler, ih->ih_argument,
223				    ih->ih_flags);
224
225				if ((ih->ih_flags & INTR_MPSAFE) == 0)
226					mtx_enter(&Giant, MTX_DEF);
227				ih->ih_handler(ih->ih_argument);
228				if ((ih->ih_flags & INTR_MPSAFE) == 0)
229					mtx_exit(&Giant, MTX_DEF);
230			}
231		}
232
233		/*
234		 * Processed all our interrupts.  Now get the sched
235		 * lock.  This may take a while and it_need may get
236		 * set again, so we have to check it again.
237		 */
238		mtx_assert(&Giant, MA_NOTOWNED);
239		mtx_enter(&sched_lock, MTX_SPIN);
240		if (!it->it_need) {
241			p->p_stat = SWAIT; /* we're idle */
242			CTR1(KTR_INTR, "sithd_loop pid %d: done", p->p_pid);
243			mi_switch();
244			CTR1(KTR_INTR, "sithd_loop pid %d: resumed", p->p_pid);
245		}
246		mtx_exit(&sched_lock, MTX_SPIN);
247	}
248}
249
250SYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL)
251
252/*
253 * Start standard software interrupt threads
254 */
255static void
256start_softintr(dummy)
257	void *dummy;
258{
259	net_ih = sinthand_add("net", NULL, swi_net, NULL, SWI_NET, 0);
260	softclock_ih = sinthand_add("clock", &clk_ithd, softclock, NULL,
261	    SWI_CLOCK, INTR_MPSAFE);
262	vm_ih = sinthand_add("vm", NULL, swi_vm, NULL, SWI_VM, 0);
263}
264
265void
266legacy_setsoftnet()
267{
268	sched_swi(net_ih, SWI_NOSWITCH);
269}
270
271/*
272 * XXX: This should really be in the network code somewhere and installed
273 * via a SI_SUB_SOFINTR, SI_ORDER_MIDDLE sysinit.
274 */
275void	(*netisrs[32]) __P((void));
276u_int	netisr;
277
278int
279register_netisr(num, handler)
280	int num;
281	netisr_t *handler;
282{
283
284	if (num < 0 || num >= (sizeof(netisrs)/sizeof(*netisrs)) ) {
285		printf("register_netisr: bad isr number: %d\n", num);
286		return (EINVAL);
287	}
288	netisrs[num] = handler;
289	return (0);
290}
291
292int
293unregister_netisr(num)
294	int num;
295{
296
297	if (num < 0 || num >= (sizeof(netisrs)/sizeof(*netisrs)) ) {
298		printf("unregister_netisr: bad isr number: %d\n", num);
299		return (EINVAL);
300	}
301	netisrs[num] = NULL;
302	return (0);
303}
304
305static void
306swi_net(void *dummy)
307{
308	u_int bits;
309	int i;
310
311	bits = atomic_readandclear_int(&netisr);
312	while ((i = ffs(bits)) != 0) {
313		i--;
314		netisrs[i]();
315		bits &= ~(1 << i);
316	}
317}
318
319/*
320 * Dummy spl calls.  The only reason for these is to not break
321 * all the code which expects to call them.
322 */
323void spl0 (void) {}
324void splx (intrmask_t x) {}
325intrmask_t  splq(intrmask_t mask) { return 0; }
326intrmask_t  splbio(void) { return 0; }
327intrmask_t  splcam(void) { return 0; }
328intrmask_t  splclock(void) { return 0; }
329intrmask_t  splhigh(void) { return 0; }
330intrmask_t  splimp(void) { return 0; }
331intrmask_t  splnet(void) { return 0; }
332intrmask_t  splsoftcam(void) { return 0; }
333intrmask_t  splsoftcambio(void) { return 0; }
334intrmask_t  splsoftcamnet(void) { return 0; }
335intrmask_t  splsoftclock(void) { return 0; }
336intrmask_t  splsofttty(void) { return 0; }
337intrmask_t  splsoftvm(void) { return 0; }
338intrmask_t  splsofttq(void) { return 0; }
339intrmask_t  splstatclock(void) { return 0; }
340intrmask_t  spltty(void) { return 0; }
341intrmask_t  splvm(void) { return 0; }
342