xref: /freebsd/sys/kern/kern_intr.c (revision 8088699f795d34421f429d5539e8e68cbaf3115c)
1425f9fdaSStefan Eßer /*
2425f9fdaSStefan Eßer  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3425f9fdaSStefan Eßer  * All rights reserved.
4425f9fdaSStefan Eßer  *
5425f9fdaSStefan Eßer  * Redistribution and use in source and binary forms, with or without
6425f9fdaSStefan Eßer  * modification, are permitted provided that the following conditions
7425f9fdaSStefan Eßer  * are met:
8425f9fdaSStefan Eßer  * 1. Redistributions of source code must retain the above copyright
9425f9fdaSStefan Eßer  *    notice unmodified, this list of conditions, and the following
10425f9fdaSStefan Eßer  *    disclaimer.
11425f9fdaSStefan Eßer  * 2. Redistributions in binary form must reproduce the above copyright
12425f9fdaSStefan Eßer  *    notice, this list of conditions and the following disclaimer in the
13425f9fdaSStefan Eßer  *    documentation and/or other materials provided with the distribution.
14425f9fdaSStefan Eßer  *
15425f9fdaSStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16425f9fdaSStefan Eßer  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17425f9fdaSStefan Eßer  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18425f9fdaSStefan Eßer  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19425f9fdaSStefan Eßer  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20425f9fdaSStefan Eßer  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21425f9fdaSStefan Eßer  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22425f9fdaSStefan Eßer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23425f9fdaSStefan Eßer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24425f9fdaSStefan Eßer  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25425f9fdaSStefan Eßer  *
26c3aac50fSPeter Wemm  * $FreeBSD$
27425f9fdaSStefan Eßer  *
28425f9fdaSStefan Eßer  */
29425f9fdaSStefan Eßer 
303900ddb2SDoug Rabson 
311c5bb3eaSPeter Wemm #include <sys/param.h>
329a94c9c5SJohn Baldwin #include <sys/bus.h>
339a94c9c5SJohn Baldwin #include <sys/rtprio.h>
34425f9fdaSStefan Eßer #include <sys/systm.h>
351931cf94SJohn Baldwin #include <sys/ipl.h>
3668352337SDoug Rabson #include <sys/interrupt.h>
371931cf94SJohn Baldwin #include <sys/kernel.h>
381931cf94SJohn Baldwin #include <sys/kthread.h>
391931cf94SJohn Baldwin #include <sys/ktr.h>
401931cf94SJohn Baldwin #include <sys/malloc.h>
4135e0e5b3SJohn Baldwin #include <sys/mutex.h>
421931cf94SJohn Baldwin #include <sys/proc.h>
431931cf94SJohn Baldwin #include <sys/unistd.h>
441931cf94SJohn Baldwin #include <sys/vmmeter.h>
451931cf94SJohn Baldwin #include <machine/atomic.h>
461931cf94SJohn Baldwin #include <machine/cpu.h>
478088699fSJohn Baldwin #include <machine/md_var.h>
48425f9fdaSStefan Eßer 
498088699fSJohn Baldwin #include <net/netisr.h>		/* prototype for legacy_setsoftnet */
5018c5a6c4SBruce Evans 
518088699fSJohn Baldwin struct intrhand *net_ih;
528088699fSJohn Baldwin struct intrhand *vm_ih;
538088699fSJohn Baldwin struct intrhand *softclock_ih;
548088699fSJohn Baldwin struct ithd	*clk_ithd;
558088699fSJohn Baldwin struct ithd	*tty_ithd;
561931cf94SJohn Baldwin 
571931cf94SJohn Baldwin static void start_softintr(void *);
588088699fSJohn Baldwin static void swi_net(void *);
5918c5a6c4SBruce Evans 
609a94c9c5SJohn Baldwin int
619a94c9c5SJohn Baldwin ithread_priority(flags)
629a94c9c5SJohn Baldwin 	int flags;
639a94c9c5SJohn Baldwin {
649a94c9c5SJohn Baldwin 	int pri;
659a94c9c5SJohn Baldwin 
669a94c9c5SJohn Baldwin 	switch (flags) {
679a94c9c5SJohn Baldwin 	case INTR_TYPE_TTY:             /* keyboard or parallel port */
689a94c9c5SJohn Baldwin 		pri = PI_TTYLOW;
699a94c9c5SJohn Baldwin 		break;
709a94c9c5SJohn Baldwin 	case (INTR_TYPE_TTY | INTR_FAST): /* sio */
719a94c9c5SJohn Baldwin 		pri = PI_TTYHIGH;
729a94c9c5SJohn Baldwin 		break;
739a94c9c5SJohn Baldwin 	case INTR_TYPE_BIO:
749a94c9c5SJohn Baldwin 		/*
759a94c9c5SJohn Baldwin 		 * XXX We need to refine this.  BSD/OS distinguishes
769a94c9c5SJohn Baldwin 		 * between tape and disk priorities.
779a94c9c5SJohn Baldwin 		 */
789a94c9c5SJohn Baldwin 		pri = PI_DISK;
799a94c9c5SJohn Baldwin 		break;
809a94c9c5SJohn Baldwin 	case INTR_TYPE_NET:
819a94c9c5SJohn Baldwin 		pri = PI_NET;
829a94c9c5SJohn Baldwin 		break;
839a94c9c5SJohn Baldwin 	case INTR_TYPE_CAM:
849a94c9c5SJohn Baldwin 		pri = PI_DISK;          /* XXX or PI_CAM? */
859a94c9c5SJohn Baldwin 		break;
869a94c9c5SJohn Baldwin 	case INTR_TYPE_MISC:
879a94c9c5SJohn Baldwin 		pri = PI_DULL;          /* don't care */
889a94c9c5SJohn Baldwin 		break;
899a94c9c5SJohn Baldwin 	/* We didn't specify an interrupt level. */
909a94c9c5SJohn Baldwin 	default:
919a94c9c5SJohn Baldwin 		panic("ithread_priority: no interrupt type in flags");
929a94c9c5SJohn Baldwin 	}
939a94c9c5SJohn Baldwin 
949a94c9c5SJohn Baldwin 	return pri;
959a94c9c5SJohn Baldwin }
969a94c9c5SJohn Baldwin 
978088699fSJohn Baldwin void sithd_loop(void *);
988088699fSJohn Baldwin 
998088699fSJohn Baldwin struct intrhand *
1008088699fSJohn Baldwin sinthand_add(const char *name, struct ithd **ithdp, driver_intr_t handler,
1018088699fSJohn Baldwin 	    void *arg, int pri, int flags)
1028088699fSJohn Baldwin {
1038088699fSJohn Baldwin 	struct proc *p;
1048088699fSJohn Baldwin 	struct ithd *ithd;
1058088699fSJohn Baldwin 	struct intrhand *ih;
1068088699fSJohn Baldwin 	struct intrhand *this_ih;
1078088699fSJohn Baldwin 
1088088699fSJohn Baldwin 	ithd = (ithdp != NULL) ? *ithdp : NULL;
1098088699fSJohn Baldwin 
1108088699fSJohn Baldwin 
1118088699fSJohn Baldwin 	if (ithd == NULL) {
1128088699fSJohn Baldwin 		int error;
1138088699fSJohn Baldwin 		ithd = malloc(sizeof (struct ithd), M_DEVBUF, M_WAITOK | M_ZERO);
1148088699fSJohn Baldwin 		error = kthread_create(sithd_loop, NULL, &p,
1158088699fSJohn Baldwin 			RFSTOPPED | RFHIGHPID, "swi%d: %s", pri, name);
1168088699fSJohn Baldwin 		if (error)
1178088699fSJohn Baldwin 			panic("inthand_add: Can't create interrupt thread");
1188088699fSJohn Baldwin 		ithd->it_proc = p;
1198088699fSJohn Baldwin 		p->p_ithd = ithd;
1208088699fSJohn Baldwin 		p->p_rtprio.type = RTP_PRIO_ITHREAD;
1218088699fSJohn Baldwin 		p->p_rtprio.prio = pri + PI_SOFT;	/* soft interrupt */
1228088699fSJohn Baldwin 		p->p_stat = SWAIT;			/* we're idle */
1238088699fSJohn Baldwin 		/* XXX - some hacks are _really_ gross */
1248088699fSJohn Baldwin 		if (pri == SWI_CLOCK)
1258088699fSJohn Baldwin 			p->p_flag |= P_NOLOAD;
1268088699fSJohn Baldwin 		if (ithdp != NULL)
1278088699fSJohn Baldwin 			*ithdp = ithd;
1288088699fSJohn Baldwin 	}
1298088699fSJohn Baldwin 	this_ih = malloc(sizeof (struct intrhand), M_DEVBUF, M_WAITOK | M_ZERO);
1308088699fSJohn Baldwin 	this_ih->ih_handler = handler;
1318088699fSJohn Baldwin 	this_ih->ih_argument = arg;
1328088699fSJohn Baldwin 	this_ih->ih_flags = flags;
1338088699fSJohn Baldwin 	this_ih->ih_ithd = ithd;
1348088699fSJohn Baldwin 	this_ih->ih_name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
1358088699fSJohn Baldwin 	if ((ih = ithd->it_ih)) {
1368088699fSJohn Baldwin 		while (ih->ih_next != NULL)
1378088699fSJohn Baldwin 			ih = ih->ih_next;
1388088699fSJohn Baldwin 		ih->ih_next = this_ih;
1398088699fSJohn Baldwin 	} else
1408088699fSJohn Baldwin 		ithd->it_ih = this_ih;
1418088699fSJohn Baldwin 	strcpy(this_ih->ih_name, name);
1428088699fSJohn Baldwin 	return (this_ih);
1438088699fSJohn Baldwin }
1448088699fSJohn Baldwin 
1458088699fSJohn Baldwin 
1461931cf94SJohn Baldwin /*
1478088699fSJohn Baldwin  * Schedule a heavyweight software interrupt process.
1481931cf94SJohn Baldwin  */
1491931cf94SJohn Baldwin void
1508088699fSJohn Baldwin sched_swi(struct intrhand *ih, int flag)
1511931cf94SJohn Baldwin {
1528088699fSJohn Baldwin 	struct ithd *it = ih->ih_ithd;	/* and the process that does it */
1538088699fSJohn Baldwin 	struct proc *p = it->it_proc;
1548088699fSJohn Baldwin 
1551931cf94SJohn Baldwin 	atomic_add_int(&cnt.v_intr, 1); /* one more global interrupt */
1561931cf94SJohn Baldwin 
1578088699fSJohn Baldwin 	CTR3(KTR_INTR, "sched_sihand pid %d(%s) need=%d",
1588088699fSJohn Baldwin 		p->p_pid, p->p_comm, it->it_need);
1591931cf94SJohn Baldwin 
1601931cf94SJohn Baldwin 	/*
1618088699fSJohn Baldwin 	 * Set it_need so that if the thread is already running but close
1628088699fSJohn Baldwin 	 * to done, it will do another go-round.  Then get the sched lock
1638088699fSJohn Baldwin 	 * and see if the thread is on whichkqs yet.  If not, put it on
1648088699fSJohn Baldwin 	 * there.  In any case, kick everyone so that if the new thread
1658088699fSJohn Baldwin 	 * is higher priority than their current thread, it gets run now.
1661931cf94SJohn Baldwin 	 */
1678088699fSJohn Baldwin 	ih->ih_need = 1;
1688088699fSJohn Baldwin 	if (!(flag & SWI_DELAY)) {
1698088699fSJohn Baldwin 		it->it_need = 1;
1701931cf94SJohn Baldwin 		mtx_enter(&sched_lock, MTX_SPIN);
1718088699fSJohn Baldwin 		if (p->p_stat == SWAIT) { /* not on run queue */
1728088699fSJohn Baldwin 			CTR1(KTR_INTR, "sched_ithd: setrunqueue %d", p->p_pid);
1731931cf94SJohn Baldwin /*			membar_lock(); */
1748088699fSJohn Baldwin 			p->p_stat = SRUN;
1758088699fSJohn Baldwin 			setrunqueue(p);
1761931cf94SJohn Baldwin 			aston();
1771931cf94SJohn Baldwin 		}
1788088699fSJohn Baldwin 		else {
1798088699fSJohn Baldwin 			CTR3(KTR_INTR, "sched_ithd %d: it_need %d, state %d",
1808088699fSJohn Baldwin 				p->p_pid, it->it_need, p->p_stat );
1818088699fSJohn Baldwin 		}
1821931cf94SJohn Baldwin 		mtx_exit(&sched_lock, MTX_SPIN);
1831931cf94SJohn Baldwin 		need_resched();
1848088699fSJohn Baldwin 	}
1858088699fSJohn Baldwin }
1868088699fSJohn Baldwin 
1878088699fSJohn Baldwin /*
1888088699fSJohn Baldwin  * This is the main code for soft interrupt threads.
1898088699fSJohn Baldwin  */
1908088699fSJohn Baldwin void
1918088699fSJohn Baldwin sithd_loop(void *dummy)
1928088699fSJohn Baldwin {
1938088699fSJohn Baldwin 	struct ithd *it;		/* our thread context */
1948088699fSJohn Baldwin 	struct intrhand *ih;		/* and our interrupt handler chain */
1958088699fSJohn Baldwin 
1968088699fSJohn Baldwin 	struct proc *p = curproc;
1978088699fSJohn Baldwin 	it = p->p_ithd;			/* point to myself */
1988088699fSJohn Baldwin 
1998088699fSJohn Baldwin 	/*
2008088699fSJohn Baldwin 	 * As long as we have interrupts outstanding, go through the
2018088699fSJohn Baldwin 	 * list of handlers, giving each one a go at it.
2028088699fSJohn Baldwin 	 */
2038088699fSJohn Baldwin 	for (;;) {
2048088699fSJohn Baldwin 		CTR3(KTR_INTR, "sithd_loop pid %d(%s) need=%d",
2058088699fSJohn Baldwin 		     p->p_pid, p->p_comm, it->it_need);
2068088699fSJohn Baldwin 		while (it->it_need) {
2078088699fSJohn Baldwin 			/*
2088088699fSJohn Baldwin 			 * Service interrupts.  If another interrupt
2098088699fSJohn Baldwin 			 * arrives while we are running, they will set
2108088699fSJohn Baldwin 			 * it_need to denote that we should make
2118088699fSJohn Baldwin 			 * another pass.
2128088699fSJohn Baldwin 			 */
2138088699fSJohn Baldwin 			it->it_need = 0;
2148088699fSJohn Baldwin 			for (ih = it->it_ih; ih != NULL; ih = ih->ih_next) {
2158088699fSJohn Baldwin 				if (!ih->ih_need)
2168088699fSJohn Baldwin 					continue;
2178088699fSJohn Baldwin 				ih->ih_need = 0;
2188088699fSJohn Baldwin 				CTR5(KTR_INTR,
2198088699fSJohn Baldwin 				    "ithd_loop pid %d ih=%p: %p(%p) flg=%x",
2208088699fSJohn Baldwin 				    p->p_pid, (void *)ih,
2218088699fSJohn Baldwin 				    (void *)ih->ih_handler, ih->ih_argument,
2228088699fSJohn Baldwin 				    ih->ih_flags);
2238088699fSJohn Baldwin 
2248088699fSJohn Baldwin 				if ((ih->ih_flags & INTR_MPSAFE) == 0)
2258088699fSJohn Baldwin 					mtx_enter(&Giant, MTX_DEF);
2268088699fSJohn Baldwin 				ih->ih_handler(ih->ih_argument);
2278088699fSJohn Baldwin 				if ((ih->ih_flags & INTR_MPSAFE) == 0)
2288088699fSJohn Baldwin 					mtx_exit(&Giant, MTX_DEF);
2298088699fSJohn Baldwin 			}
2308088699fSJohn Baldwin 		}
2318088699fSJohn Baldwin 
2328088699fSJohn Baldwin 		/*
2338088699fSJohn Baldwin 		 * Processed all our interrupts.  Now get the sched
2348088699fSJohn Baldwin 		 * lock.  This may take a while and it_need may get
2358088699fSJohn Baldwin 		 * set again, so we have to check it again.
2368088699fSJohn Baldwin 		 */
2378088699fSJohn Baldwin 		mtx_enter(&sched_lock, MTX_SPIN);
2388088699fSJohn Baldwin 		if (!it->it_need) {
2398088699fSJohn Baldwin 			p->p_stat = SWAIT; /* we're idle */
2408088699fSJohn Baldwin 			CTR1(KTR_INTR, "sithd_loop pid %d: done", p->p_pid);
2418088699fSJohn Baldwin 			mi_switch();
2428088699fSJohn Baldwin 			CTR1(KTR_INTR, "sithd_loop pid %d: resumed", p->p_pid);
2438088699fSJohn Baldwin 		}
2448088699fSJohn Baldwin 		mtx_exit(&sched_lock, MTX_SPIN);
2458088699fSJohn Baldwin 	}
2461931cf94SJohn Baldwin }
2471931cf94SJohn Baldwin 
2481931cf94SJohn Baldwin SYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL)
2491931cf94SJohn Baldwin 
2501931cf94SJohn Baldwin /*
2518088699fSJohn Baldwin  * Start standard software interrupt threads
2521931cf94SJohn Baldwin  */
2531931cf94SJohn Baldwin static void
2541931cf94SJohn Baldwin start_softintr(dummy)
2551931cf94SJohn Baldwin 	void *dummy;
2561931cf94SJohn Baldwin {
2578088699fSJohn Baldwin 	net_ih = sinthand_add("net", NULL, swi_net, NULL, SWI_NET, 0);
2588088699fSJohn Baldwin 	softclock_ih =
2598088699fSJohn Baldwin 	    sinthand_add("clock", &clk_ithd, softclock, NULL, SWI_CLOCK, 0);
2608088699fSJohn Baldwin 	vm_ih = sinthand_add("vm", NULL, swi_vm, NULL, SWI_VM, 0);
2611931cf94SJohn Baldwin }
2621931cf94SJohn Baldwin 
2631931cf94SJohn Baldwin void
2648088699fSJohn Baldwin legacy_setsoftnet()
2651931cf94SJohn Baldwin {
2668088699fSJohn Baldwin 	sched_swi(net_ih, SWI_NOSWITCH);
2671931cf94SJohn Baldwin }
2681931cf94SJohn Baldwin 
2698088699fSJohn Baldwin /*
2708088699fSJohn Baldwin  * XXX: This should really be in the network code somewhere and installed
2718088699fSJohn Baldwin  * via a SI_SUB_SOFINTR, SI_ORDER_MIDDLE sysinit.
2728088699fSJohn Baldwin  */
2738088699fSJohn Baldwin void	(*netisrs[32]) __P((void));
2748088699fSJohn Baldwin u_int	netisr;
2758088699fSJohn Baldwin 
2768088699fSJohn Baldwin static void
2778088699fSJohn Baldwin swi_net(void *dummy)
2781931cf94SJohn Baldwin {
2798088699fSJohn Baldwin 	u_int bits;
2808088699fSJohn Baldwin 	int i;
2818088699fSJohn Baldwin 
2828088699fSJohn Baldwin 	bits = atomic_readandclear_int(&netisr);
2838088699fSJohn Baldwin 	while ((i = ffs(bits)) != 0) {
2848088699fSJohn Baldwin 		i--;
2858088699fSJohn Baldwin 		netisrs[i]();
2868088699fSJohn Baldwin 		bits &= ~(1 << i);
2878088699fSJohn Baldwin 	}
2881931cf94SJohn Baldwin }
2891931cf94SJohn Baldwin 
2901931cf94SJohn Baldwin /*
2911931cf94SJohn Baldwin  * Dummy spl calls.  The only reason for these is to not break
2921931cf94SJohn Baldwin  * all the code which expects to call them.
2931931cf94SJohn Baldwin  */
2941931cf94SJohn Baldwin void spl0 (void) {}
2951931cf94SJohn Baldwin void splx (intrmask_t x) {}
2961931cf94SJohn Baldwin intrmask_t  splq(intrmask_t mask) { return 0; }
2971931cf94SJohn Baldwin intrmask_t  splbio(void) { return 0; }
2981931cf94SJohn Baldwin intrmask_t  splcam(void) { return 0; }
2991931cf94SJohn Baldwin intrmask_t  splclock(void) { return 0; }
3001931cf94SJohn Baldwin intrmask_t  splhigh(void) { return 0; }
3011931cf94SJohn Baldwin intrmask_t  splimp(void) { return 0; }
3021931cf94SJohn Baldwin intrmask_t  splnet(void) { return 0; }
3031931cf94SJohn Baldwin intrmask_t  splsoftcam(void) { return 0; }
3041931cf94SJohn Baldwin intrmask_t  splsoftcambio(void) { return 0; }
3051931cf94SJohn Baldwin intrmask_t  splsoftcamnet(void) { return 0; }
3061931cf94SJohn Baldwin intrmask_t  splsoftclock(void) { return 0; }
3071931cf94SJohn Baldwin intrmask_t  splsofttty(void) { return 0; }
3081931cf94SJohn Baldwin intrmask_t  splsoftvm(void) { return 0; }
3091931cf94SJohn Baldwin intrmask_t  splsofttq(void) { return 0; }
3101931cf94SJohn Baldwin intrmask_t  splstatclock(void) { return 0; }
3111931cf94SJohn Baldwin intrmask_t  spltty(void) { return 0; }
3121931cf94SJohn Baldwin intrmask_t  splvm(void) { return 0; }
313