kern_intr.c revision 72839
11541Srgrimes/*
21541Srgrimes * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
31541Srgrimes * All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice unmodified, this list of conditions, and the following
101541Srgrimes *    disclaimer.
111541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121541Srgrimes *    notice, this list of conditions and the following disclaimer in the
131541Srgrimes *    documentation and/or other materials provided with the distribution.
141541Srgrimes *
151541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
161541Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
171541Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
181541Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
191541Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
201541Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
211541Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
221541Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
231541Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
241541Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
251541Srgrimes *
261541Srgrimes * $FreeBSD: head/sys/kern/kern_intr.c 72839 2001-02-22 02:18:32Z jhb $
271541Srgrimes *
281541Srgrimes */
291541Srgrimes
301541Srgrimes
311541Srgrimes#include <sys/param.h>
321541Srgrimes#include <sys/bus.h>
331541Srgrimes#include <sys/rtprio.h>
341541Srgrimes#include <sys/systm.h>
351541Srgrimes#include <sys/ipl.h>
361541Srgrimes#include <sys/interrupt.h>
371541Srgrimes#include <sys/kernel.h>
381541Srgrimes#include <sys/kthread.h>
3915985Sdg#include <sys/ktr.h>
401541Srgrimes#include <sys/malloc.h>
411541Srgrimes#include <sys/mutex.h>
421541Srgrimes#include <sys/proc.h>
431541Srgrimes#include <sys/random.h>
441541Srgrimes#include <sys/resourcevar.h>
451541Srgrimes#include <sys/unistd.h>
461541Srgrimes#include <sys/vmmeter.h>
471541Srgrimes#include <machine/atomic.h>
481541Srgrimes#include <machine/cpu.h>
4912221Sbde#include <machine/md_var.h>
501541Srgrimes#include <machine/stdarg.h>
511541Srgrimes
521541Srgrimes#include <net/netisr.h>		/* prototype for legacy_setsoftnet */
531541Srgrimes
541541Srgrimesstruct	int_entropy {
551541Srgrimes	struct	proc *proc;
5612221Sbde	int	vector;
5711332Sswallace};
581541Srgrimes
591541Srgrimesvoid	*net_ih;
6012221Sbdevoid	*vm_ih;
611541Srgrimesvoid	*softclock_ih;
621541Srgrimesstruct	ithd *clk_ithd;
631549Srgrimesstruct	ithd *tty_ithd;
641541Srgrimes
651541Srgrimesstatic struct	mtx ithread_list_lock;
6611332Sswallace
671541Srgrimesstatic MALLOC_DEFINE(M_ITHREAD, "ithread", "Interrupt Threads");
681541Srgrimes
691541Srgrimesstatic void	ithread_update(struct ithd *);
701541Srgrimesstatic void	ithread_loop(void *);
711541Srgrimesstatic void	ithread_init(void *);
721541Srgrimesstatic void	start_softintr(void *);
731541Srgrimesstatic void	swi_net(void *);
741541Srgrimes
751541Srgrimesu_char
761541Srgrimesithread_priority(enum intr_type flags)
7712221Sbde{
7811332Sswallace	u_char pri;
7911332Sswallace
8011332Sswallace	flags &= (INTR_TYPE_TTY | INTR_TYPE_BIO | INTR_TYPE_NET |
8112221Sbde	    INTR_TYPE_CAM | INTR_TYPE_MISC | INTR_TYPE_CLK);
821541Srgrimes	switch (flags) {
831549Srgrimes	case INTR_TYPE_TTY:
841541Srgrimes		pri = PI_TTYLOW;
851541Srgrimes		break;
8611332Sswallace	case INTR_TYPE_BIO:
871541Srgrimes		/*
881541Srgrimes		 * XXX We need to refine this.  BSD/OS distinguishes
891541Srgrimes		 * between tape and disk priorities.
901541Srgrimes		 */
911541Srgrimes		pri = PI_DISK;
921541Srgrimes		break;
931541Srgrimes	case INTR_TYPE_NET:
941541Srgrimes		pri = PI_NET;
9512221Sbde		break;
9611332Sswallace	case INTR_TYPE_CAM:
9711332Sswallace		pri = PI_DISK;          /* XXX or PI_CAM? */
9811332Sswallace		break;
9912221Sbde	case INTR_TYPE_CLK:
10011332Sswallace		pri = PI_REALTIME;
1011549Srgrimes		break;
1021541Srgrimes	case INTR_TYPE_MISC:
1031541Srgrimes		pri = PI_DULL;          /* don't care */
10411332Sswallace		break;
1051541Srgrimes	default:
1061541Srgrimes		/* We didn't specify an interrupt level. */
1071541Srgrimes		panic("ithread_priority: no interrupt type in flags");
1081541Srgrimes	}
1091541Srgrimes
1101541Srgrimes	return pri;
1111541Srgrimes}
11212221Sbde
11311332Sswallace/*
11411332Sswallace * Regenerate the name (p_comm) and priority for a threaded interrupt thread.
11511332Sswallace */
11612221Sbdestatic void
11711332Sswallaceithread_update(struct ithd *ithd)
1181541Srgrimes{
1191549Srgrimes	struct intrhand *ih;
1201541Srgrimes	struct proc *p;
1211541Srgrimes	int entropy;
12211332Sswallace
1231541Srgrimes	p = ithd->it_proc;
1241541Srgrimes	if (p == NULL)
1251541Srgrimes		return;
1261541Srgrimes
1271541Srgrimes	strncpy(p->p_comm, ithd->it_name, sizeof(ithd->it_name));
1281541Srgrimes	ih = TAILQ_FIRST(&ithd->it_handlers);
1291541Srgrimes	if (ih == NULL) {
1301541Srgrimes		p->p_pri.pri_level = PRI_MAX_ITHD;
1311541Srgrimes		ithd->it_flags &= ~IT_ENTROPY;
1321541Srgrimes		return;
13312221Sbde	}
13411332Sswallace
13511332Sswallace	entropy = 0;
13611332Sswallace	p->p_pri.pri_level = ih->ih_pri;
13712221Sbde	TAILQ_FOREACH(ih, &ithd->it_handlers, ih_next) {
13811332Sswallace		if (strlen(p->p_comm) + strlen(ih->ih_name) + 1 <
1391541Srgrimes		    sizeof(p->p_comm)) {
1401549Srgrimes			strcat(p->p_comm, " ");
1411541Srgrimes			strcat(p->p_comm, ih->ih_name);
1421541Srgrimes		} else if (strlen(p->p_comm) + 1 == sizeof(p->p_comm)) {
14311332Sswallace			if (p->p_comm[sizeof(p->p_comm) - 2] == '+')
1441541Srgrimes				p->p_comm[sizeof(p->p_comm) - 2] = '*';
1451541Srgrimes			else
1461541Srgrimes				p->p_comm[sizeof(p->p_comm) - 2] = '+';
1471541Srgrimes		} else
1481541Srgrimes			strcat(p->p_comm, "+");
1491541Srgrimes		if (ih->ih_flags & IH_ENTROPY)
1501541Srgrimes			entropy++;
15112221Sbde	}
15211332Sswallace
15311332Sswallace	if (entropy)
15411332Sswallace		ithd->it_flags |= IT_ENTROPY;
15512221Sbde	else
15611332Sswallace		ithd->it_flags &= ~IT_ENTROPY;
1571541Srgrimes
1581549Srgrimes	CTR1(KTR_INTR, __func__ ": updated %s\n", p->p_comm);
1591541Srgrimes}
1601541Srgrimes
16111332Sswallaceint
1621541Srgrimesithread_create(struct ithd **ithread, int vector, int flags,
1631541Srgrimes    void (*disable)(int), void (*enable)(int), const char *fmt, ...)
1641541Srgrimes{
1651541Srgrimes	struct ithd *ithd;
1661541Srgrimes	struct proc *p;
1671541Srgrimes	int error;
1681541Srgrimes	va_list ap;
1691541Srgrimes
1701541Srgrimes	/* The only valid flag during creation is IT_SOFT. */
1711541Srgrimes	if ((flags & ~IT_SOFT) != 0)
1721541Srgrimes		return (EINVAL);
1731541Srgrimes
1741541Srgrimes	ithd = malloc(sizeof(struct ithd), M_ITHREAD, M_WAITOK | M_ZERO);
1751541Srgrimes	ithd->it_vector = vector;
1761541Srgrimes	ithd->it_disable = disable;
17712221Sbde	ithd->it_enable = enable;
17811332Sswallace	ithd->it_flags = flags;
17911332Sswallace	TAILQ_INIT(&ithd->it_handlers);
18011332Sswallace
18112221Sbde	va_start(ap, fmt);
18211332Sswallace	vsnprintf(ithd->it_name, sizeof(ithd->it_name), fmt, ap);
1831541Srgrimes	va_end(ap);
1841549Srgrimes
1851541Srgrimes	error = kthread_create(ithread_loop, ithd, &p, RFSTOPPED | RFHIGHPID,
1861541Srgrimes	    ithd->it_name);
18711332Sswallace	if (error) {
1881541Srgrimes		free(ithd, M_ITHREAD);
1891541Srgrimes		return (error);
1901541Srgrimes	}
1911541Srgrimes	p->p_pri.pri_class = PRI_ITHD;
1921541Srgrimes	p->p_pri.pri_level = PRI_MAX_ITHD;
1931541Srgrimes	p->p_stat = SWAIT;
1941541Srgrimes	ithd->it_proc = p;
19512221Sbde	p->p_ithd = ithd;
1961541Srgrimes	if (ithread != NULL)
1971541Srgrimes		*ithread = ithd;
1981541Srgrimes
1991541Srgrimes	CTR1(KTR_INTR, __func__ ": created %s", ithd->it_name);
20012221Sbde	return (0);
2011549Srgrimes}
2021541Srgrimes
2031541Srgrimesint
2041541Srgrimesithread_destroy(struct ithd *ithread)
2051541Srgrimes{
2061541Srgrimes
2071541Srgrimes	if (ithread == NULL || !TAILQ_EMPTY(&ithread->it_handlers))
2081541Srgrimes		return (EINVAL);
2091541Srgrimes
2101541Srgrimes	mtx_lock_spin(&sched_lock);
2111541Srgrimes	ithread->it_flags |= IT_DEAD;
2121541Srgrimes	if (ithread->it_proc->p_stat == SWAIT) {
2131541Srgrimes		ithread->it_proc->p_stat = SRUN;
2141541Srgrimes		setrunqueue(ithread->it_proc);
2151541Srgrimes	}
2161541Srgrimes	mtx_unlock_spin(&sched_lock);
2171541Srgrimes	CTR1(KTR_INTR, __func__ ": killing %s", ithread->it_name);
2183098Sphk	return (0);
2193098Sphk}
2201541Srgrimes
2211541Srgrimesint
2221541Srgrimesithread_add_handler(struct ithd* ithread, const char *name,
2231541Srgrimes    driver_intr_t handler, void *arg, u_char pri, enum intr_type flags,
2241541Srgrimes    void **cookiep)
22512221Sbde{
22612207Sbde	struct intrhand *ih, *temp_ih;
22711332Sswallace
22811332Sswallace	if (ithread == NULL || name == NULL || handler == NULL)
22912221Sbde		return (EINVAL);
23011332Sswallace	if ((flags & INTR_FAST) !=0)
2311541Srgrimes		flags |= INTR_EXCL;
2321549Srgrimes
2331541Srgrimes	ih = malloc(sizeof(struct intrhand), M_ITHREAD, M_WAITOK | M_ZERO);
2341541Srgrimes	ih->ih_handler = handler;
23512207Sbde	ih->ih_argument = arg;
2361541Srgrimes	ih->ih_name = name;
2371541Srgrimes	ih->ih_ithread = ithread;
2381541Srgrimes	ih->ih_pri = pri;
2391541Srgrimes	if (flags & INTR_FAST)
2401541Srgrimes		ih->ih_flags = IH_FAST | IH_EXCLUSIVE;
2411541Srgrimes	else if (flags & INTR_EXCL)
2421541Srgrimes		ih->ih_flags = IH_EXCLUSIVE;
2431541Srgrimes	if (flags & INTR_MPSAFE)
2441541Srgrimes		ih->ih_flags |= IH_MPSAFE;
2451541Srgrimes	if (flags & INTR_ENTROPY)
2461541Srgrimes		ih->ih_flags |= IH_ENTROPY;
2471541Srgrimes
2481541Srgrimes	mtx_lock_spin(&ithread_list_lock);
2491541Srgrimes	if ((flags & INTR_EXCL) !=0 && !TAILQ_EMPTY(&ithread->it_handlers))
2501541Srgrimes		goto fail;
2511541Srgrimes	if (!TAILQ_EMPTY(&ithread->it_handlers) &&
2521541Srgrimes	    (TAILQ_FIRST(&ithread->it_handlers)->ih_flags & IH_EXCLUSIVE) != 0)
2531541Srgrimes		goto fail;
2541541Srgrimes
2551541Srgrimes	TAILQ_FOREACH(temp_ih, &ithread->it_handlers, ih_next)
2561541Srgrimes	    if (temp_ih->ih_pri > ih->ih_pri)
2571541Srgrimes		    break;
2581541Srgrimes	if (temp_ih == NULL)
2591541Srgrimes		TAILQ_INSERT_TAIL(&ithread->it_handlers, ih, ih_next);
2601541Srgrimes	else
26112221Sbde		TAILQ_INSERT_BEFORE(temp_ih, ih, ih_next);
2621541Srgrimes	ithread_update(ithread);
2631541Srgrimes	mtx_unlock_spin(&ithread_list_lock);
2641541Srgrimes
2651541Srgrimes	if (cookiep != NULL)
26612221Sbde		*cookiep = ih;
2671541Srgrimes	CTR2(KTR_INTR, __func__ ": added %s to %s", ih->ih_name,
2681549Srgrimes	    ithread->it_name);
2691541Srgrimes	return (0);
2701541Srgrimes
2711541Srgrimesfail:
2721541Srgrimes	mtx_unlock_spin(&ithread_list_lock);
2731541Srgrimes	free(ih, M_ITHREAD);
2741541Srgrimes	return (EINVAL);
2751541Srgrimes}
2761541Srgrimes
2771541Srgrimesint
2781541Srgrimesithread_remove_handler(void *cookie)
2791541Srgrimes{
28015985Sdg	struct intrhand *handler = (struct intrhand *)cookie;
2811541Srgrimes	struct ithd *ithread;
2821541Srgrimes#ifdef INVARIANTS
2831541Srgrimes	struct intrhand *ih;
2841541Srgrimes#endif
2851541Srgrimes
2861541Srgrimes	if (handler == NULL)
2871541Srgrimes		return (EINVAL);
2881541Srgrimes	ithread = handler->ih_ithread;
2891541Srgrimes	KASSERT(ithread != NULL,
2901541Srgrimes	    ("interrupt handler \"%s\" has a NULL interrupt thread",
2911541Srgrimes		handler->ih_name));
2921541Srgrimes	CTR2(KTR_INTR, __func__ ": removing %s from %s", handler->ih_name,
2931541Srgrimes	    ithread->it_name);
2941541Srgrimes	mtx_lock_spin(&ithread_list_lock);
2951541Srgrimes#ifdef INVARIANTS
2961541Srgrimes	TAILQ_FOREACH(ih, &ithread->it_handlers, ih_next)
29712221Sbde		if (ih == handler)
2981541Srgrimes			goto ok;
2991541Srgrimes	mtx_unlock_spin(&ithread_list_lock);
3001541Srgrimes	panic("interrupt handler \"%s\" not found in interrupt thread \"%s\"",
30112221Sbde	    ih->ih_name, ithread->it_name);
3021541Srgrimesok:
3031549Srgrimes#endif
3041541Srgrimes	/*
3051541Srgrimes	 * If the interrupt thread is already running, then just mark this
3061541Srgrimes	 * handler as being dead and let the ithread do the actual removal.
3071541Srgrimes	 */
3081541Srgrimes	mtx_lock_spin(&sched_lock);
3091541Srgrimes	if (ithread->it_proc->p_stat != SWAIT) {
3101541Srgrimes		handler->ih_flags |= IH_DEAD;
3111541Srgrimes
3121541Srgrimes		/*
3131541Srgrimes		 * Ensure that the thread will process the handler list
3148162Sache		 * again and remove this handler if it has already passed
3158162Sache		 * it on the list.
3161541Srgrimes		 */
3171541Srgrimes		ithread->it_need = 1;
3181541Srgrimes	} else {
3191541Srgrimes		TAILQ_REMOVE(&ithread->it_handlers, handler, ih_next);
3201541Srgrimes		ithread_update(ithread);
3211541Srgrimes	}
3228162Sache	mtx_unlock_spin(&sched_lock);
3238141Sache	mtx_unlock_spin(&ithread_list_lock);
3248141Sache
3258141Sache	if ((handler->ih_flags & IH_DEAD) == 0)
3261541Srgrimes		free(handler, M_ITHREAD);
3278162Sache	return (0);
3288141Sache}
3298141Sache
3308141Sacheint
3318162Sacheithread_schedule(struct ithd *ithread, int do_switch)
3321541Srgrimes{
3331541Srgrimes	struct int_entropy entropy;
3341541Srgrimes	struct proc *p;
3351541Srgrimes	intrmask_t saveintr;
33612221Sbde
3371541Srgrimes	/*
3381541Srgrimes	 * If no ithread or no handlers, then we have a stray interrupt.
3391541Srgrimes	 */
34012221Sbde	if ((ithread == NULL) || TAILQ_EMPTY(&ithread->it_handlers))
3411541Srgrimes		return (EINVAL);
3421549Srgrimes
3431541Srgrimes	/*
3441541Srgrimes	 * If any of the handlers for this ithread claim to be good
3451541Srgrimes	 * sources of entropy, then gather some.
3461541Srgrimes	 */
3471541Srgrimes	if (harvest.interrupt && ithread->it_flags & IT_ENTROPY) {
3481541Srgrimes		entropy.vector = ithread->it_vector;
3491541Srgrimes		entropy.proc = CURPROC;
3501541Srgrimes		random_harvest(&entropy, sizeof(entropy), 2, 0,
3511541Srgrimes		    RANDOM_INTERRUPT);
3521541Srgrimes	}
3531541Srgrimes
3541541Srgrimes	p = ithread->it_proc;
3551541Srgrimes	CTR3(KTR_INTR, __func__ ": pid %d: (%s) need = %d", p->p_pid, p->p_comm,
3561541Srgrimes	    ithread->it_need);
3571541Srgrimes
3581541Srgrimes	/*
3591541Srgrimes	 * Set it_need to tell the thread to keep running if it is already
3601541Srgrimes	 * running.  Then, grab sched_lock and see if we actually need to
3611541Srgrimes	 * put this thread on the runqueue.  If so and the do_switch flag is
3621541Srgrimes	 * true, then switch to the ithread immediately.  Otherwise, use
3631541Srgrimes	 * need_resched() to guarantee that this ithread will run before any
3641541Srgrimes	 * userland processes.
3651541Srgrimes	 */
36612221Sbde	ithread->it_need = 1;
3671541Srgrimes	mtx_lock_spin(&sched_lock);
3681541Srgrimes	if (p->p_stat == SWAIT) {
3691541Srgrimes		CTR1(KTR_INTR, __func__ ": setrunqueue %d", p->p_pid);
37012221Sbde		p->p_stat = SRUN;
3711541Srgrimes		setrunqueue(p);
3721549Srgrimes		if (do_switch) {
3731541Srgrimes			saveintr = sched_lock.mtx_saveintr;
3741541Srgrimes			mtx_intr_enable(&sched_lock);
3751541Srgrimes			if (curproc != PCPU_GET(idleproc))
3761541Srgrimes				setrunqueue(curproc);
3771541Srgrimes			curproc->p_stats->p_ru.ru_nvcsw++;
3781541Srgrimes			mi_switch();
3791541Srgrimes			sched_lock.mtx_saveintr = saveintr;
3801541Srgrimes		} else
3811541Srgrimes			need_resched();
3821541Srgrimes	} else {
3838162Sache		CTR3(KTR_INTR, __func__ ": pid %d: it_need %d, state %d",
3848162Sache		    p->p_pid, ithread->it_need, p->p_stat);
3851541Srgrimes	}
3861541Srgrimes	mtx_unlock_spin(&sched_lock);
3871541Srgrimes
3888162Sache	return (0);
3898141Sache}
3908141Sache
3918141Sacheint
3921541Srgrimesswi_add(struct ithd **ithdp, const char *name, driver_intr_t handler,
3931541Srgrimes	    void *arg, int pri, enum intr_type flags, void **cookiep)
3941541Srgrimes{
3951541Srgrimes	struct ithd *ithd;
39612221Sbde	int error;
3971541Srgrimes
3981541Srgrimes	if (flags & (INTR_FAST | INTR_ENTROPY))
3991541Srgrimes		return (EINVAL);
40012221Sbde
4011541Srgrimes	ithd = (ithdp != NULL) ? *ithdp : NULL;
4021549Srgrimes
4031541Srgrimes	if (ithd != NULL) {
4041541Srgrimes		if ((ithd->it_flags & IT_SOFT) == 0)
4051541Srgrimes			return(EINVAL);
4061541Srgrimes	} else {
4071541Srgrimes		error = ithread_create(&ithd, pri, IT_SOFT, NULL, NULL,
4081541Srgrimes		    "swi%d:", pri);
4091541Srgrimes		if (error)
4101541Srgrimes			return (error);
4111541Srgrimes
4121541Srgrimes		if (ithdp != NULL)
4131541Srgrimes			*ithdp = ithd;
4141541Srgrimes	}
4151541Srgrimes	return (ithread_add_handler(ithd, name, handler, arg,
4161541Srgrimes		    (pri * RQ_PPQ) + PI_SOFT, flags, cookiep));
4171541Srgrimes}
4181541Srgrimes
4191541Srgrimes
4201541Srgrimes/*
4211541Srgrimes * Schedule a heavyweight software interrupt process.
42212221Sbde */
4231541Srgrimesvoid
4241541Srgrimesswi_sched(void *cookie, int flags)
4251541Srgrimes{
4261541Srgrimes	struct intrhand *ih = (struct intrhand *)cookie;
42712221Sbde	struct ithd *it = ih->ih_ithread;
4281541Srgrimes	int error;
4291549Srgrimes
4301541Srgrimes	atomic_add_int(&cnt.v_intr, 1); /* one more global interrupt */
4311541Srgrimes
4321541Srgrimes	CTR3(KTR_INTR, "swi_sched pid %d(%s) need=%d",
4331541Srgrimes		it->it_proc->p_pid, it->it_proc->p_comm, it->it_need);
4341541Srgrimes
4351541Srgrimes	/*
4361541Srgrimes	 * Set ih_need for this handler so that if the ithread is already
4371541Srgrimes	 * running it will execute this handler on the next pass.  Otherwise,
4381541Srgrimes	 * it will execute it the next time it runs.
4393098Sphk	 */
4401541Srgrimes	atomic_store_rel_int(&ih->ih_need, 1);
44112063Sdg	if (!(flags & SWI_DELAY)) {
44212063Sdg		error = ithread_schedule(it, !cold && flags & SWI_SWITCH);
4431541Srgrimes		KASSERT(error == 0, ("stray software interrupt"));
4441541Srgrimes	}
4453098Sphk}
4463098Sphk
4471541Srgrimes/*
4481541Srgrimes * This is the main code for interrupt threads.
4491541Srgrimes */
4501541Srgrimesvoid
4511541Srgrimesithread_loop(void *arg)
4521541Srgrimes{
45312221Sbde	struct ithd *ithd;		/* our thread context */
4541541Srgrimes	struct intrhand *ih;		/* and our interrupt handler chain */
4559238Sache	struct proc *p;
4569238Sache
4571541Srgrimes	p = curproc;
45812221Sbde	ithd = (struct ithd *)arg;	/* point to myself */
4591541Srgrimes	KASSERT(ithd->it_proc == p && p->p_ithd == ithd,
4601549Srgrimes	    (__func__ ": ithread and proc linkage out of sync"));
4618019Sache
4621541Srgrimes	/*
4631541Srgrimes	 * As long as we have interrupts outstanding, go through the
4641541Srgrimes	 * list of handlers, giving each one a go at it.
4651541Srgrimes	 */
4661541Srgrimes	for (;;) {
4679238Sache		/*
4688135Sache		 * If we are an orphaned thread, then just die.
4691541Srgrimes		 */
4709238Sache		if (ithd->it_flags & IT_DEAD) {
4719238Sache			CTR2(KTR_INTR, __func__ ": pid %d: (%s) exiting",
4729238Sache			    p->p_pid, p->p_comm);
4739238Sache			p->p_ithd = NULL;
4748135Sache			mtx_lock(&Giant);
4758135Sache			free(ithd, M_ITHREAD);
4769238Sache			kthread_exit(0);
4779238Sache		}
4789238Sache
4799238Sache		CTR3(KTR_INTR, __func__ ": pid %d: (%s) need=%d",
4809238Sache		     p->p_pid, p->p_comm, ithd->it_need);
4819238Sache		while (ithd->it_need) {
4829238Sache			/*
4839238Sache			 * Service interrupts.  If another interrupt
4848135Sache			 * arrives while we are running, they will set
4859238Sache			 * it_need to denote that we should make
4868111Sache			 * another pass.
4879238Sache			 */
4888135Sache			atomic_store_rel_int(&ithd->it_need, 0);
4891541Srgrimesrestart:
4901541Srgrimes			TAILQ_FOREACH(ih, &ithd->it_handlers, ih_next) {
49112221Sbde				if (ithd->it_flags & IT_SOFT && !ih->ih_need)
4921541Srgrimes					continue;
4939238Sache				atomic_store_rel_int(&ih->ih_need, 0);
4949238Sache				CTR5(KTR_INTR,
4951541Srgrimes				    __func__ ": pid %d ih=%p: %p(%p) flg=%x",
49612221Sbde				    p->p_pid, (void *)ih,
4971541Srgrimes				    (void *)ih->ih_handler, ih->ih_argument,
4981549Srgrimes				    ih->ih_flags);
4998019Sache
5001541Srgrimes				if ((ih->ih_flags & IH_MPSAFE) == 0)
5011541Srgrimes					mtx_lock(&Giant);
5021541Srgrimes				if ((ih->ih_flags & IH_DEAD) != 0) {
5031541Srgrimes					mtx_lock_spin(&ithread_list_lock);
5041541Srgrimes					TAILQ_REMOVE(&ithd->it_handlers, ih,
5059238Sache					    ih_next);
5068135Sache					ithread_update(ithd);
5071541Srgrimes					mtx_unlock_spin(&ithread_list_lock);
5089238Sache					if (!mtx_owned(&Giant))
5099238Sache						mtx_lock(&Giant);
5109238Sache					free(ih, M_ITHREAD);
5119238Sache					mtx_unlock(&Giant);
5128135Sache					goto restart;
5138135Sache				}
5149238Sache				ih->ih_handler(ih->ih_argument);
5159238Sache				if ((ih->ih_flags & IH_MPSAFE) == 0)
5169238Sache					mtx_unlock(&Giant);
5179238Sache			}
5189238Sache		}
5199238Sache
5209238Sache		/*
5218111Sache		 * Processed all our interrupts.  Now get the sched
5229238Sache		 * lock.  This may take a while and it_need may get
5238135Sache		 * set again, so we have to check it again.
5241541Srgrimes		 */
5251541Srgrimes		mtx_assert(&Giant, MA_NOTOWNED);
5261541Srgrimes		mtx_lock_spin(&sched_lock);
5271541Srgrimes		if (!ithd->it_need) {
5281541Srgrimes			/*
5291549Srgrimes			 * Should we call this earlier in the loop above?
5301541Srgrimes			 */
5311541Srgrimes			if (ithd->it_enable != NULL)
5321541Srgrimes				ithd->it_enable(ithd->it_vector);
5331541Srgrimes			p->p_stat = SWAIT; /* we're idle */
5341541Srgrimes			CTR1(KTR_INTR, __func__ ": pid %d: done", p->p_pid);
5351541Srgrimes			mi_switch();
5361541Srgrimes			CTR1(KTR_INTR, __func__ ": pid %d: resumed", p->p_pid);
5371541Srgrimes		}
5381541Srgrimes		mtx_unlock_spin(&sched_lock);
5391541Srgrimes	}
5401541Srgrimes}
5411541Srgrimes
5421541Srgrimes/*
5431541Srgrimes * Initialize mutex used to protect ithread handler lists.
5441541Srgrimes */
5451541Srgrimesstatic void
5461541Srgrimesithread_init(void *dummy)
5471541Srgrimes{
5481541Srgrimes
5491541Srgrimes	mtx_init(&ithread_list_lock, "ithread list lock", MTX_SPIN);
5501549Srgrimes}
5511541SrgrimesSYSINIT(ithread_init, SI_SUB_INTR, SI_ORDER_FIRST, ithread_init, NULL);
5521541Srgrimes
5538011Sbde/*
5541541Srgrimes * Start standard software interrupt threads
5551541Srgrimes */
5561541Srgrimesstatic void
5571541Srgrimesstart_softintr(void *dummy)
5581541Srgrimes{
5591541Srgrimes
5601541Srgrimes	if (swi_add(NULL, "net", swi_net, NULL, SWI_NET, 0, &net_ih) ||
5611541Srgrimes	    swi_add(&clk_ithd, "clock", softclock, NULL, SWI_CLOCK,
5621541Srgrimes		INTR_MPSAFE, &softclock_ih) ||
5631541Srgrimes	    swi_add(NULL, "vm", swi_vm, NULL, SWI_VM, 0, &vm_ih))
5641541Srgrimes		panic("died while creating standard software ithreads");
5651541Srgrimes
5661541Srgrimes	PROC_LOCK(clk_ithd->it_proc);
5671541Srgrimes	clk_ithd->it_proc->p_flag |= P_NOLOAD;
5681541Srgrimes	PROC_UNLOCK(clk_ithd->it_proc);
5691541Srgrimes}
5701541SrgrimesSYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL)
5711541Srgrimes
5721541Srgrimesvoid
5731541Srgrimeslegacy_setsoftnet(void)
5741541Srgrimes{
5751541Srgrimes	swi_sched(net_ih, SWI_NOSWITCH);
5761541Srgrimes}
5771541Srgrimes
5781541Srgrimes/*
5791541Srgrimes * XXX: This should really be in the network code somewhere and installed
5801541Srgrimes * via a SI_SUB_SOFINTR, SI_ORDER_MIDDLE sysinit.
5811549Srgrimes */
5821541Srgrimesvoid	(*netisrs[32]) __P((void));
5831541Srgrimesu_int	netisr;
5841541Srgrimes
5851541Srgrimesint
5861541Srgrimesregister_netisr(num, handler)
5871541Srgrimes	int num;
5881541Srgrimes	netisr_t *handler;
5891541Srgrimes{
5901541Srgrimes
5911541Srgrimes	if (num < 0 || num >= (sizeof(netisrs)/sizeof(*netisrs)) ) {
5921541Srgrimes		printf("register_netisr: bad isr number: %d\n", num);
5931541Srgrimes		return (EINVAL);
5941541Srgrimes	}
5951541Srgrimes	netisrs[num] = handler;
5961541Srgrimes	return (0);
5971541Srgrimes}
5981541Srgrimes
5991541Srgrimesint
6001541Srgrimesunregister_netisr(num)
6011541Srgrimes	int num;
6021541Srgrimes{
6031541Srgrimes
6041541Srgrimes	if (num < 0 || num >= (sizeof(netisrs)/sizeof(*netisrs)) ) {
6051541Srgrimes		printf("unregister_netisr: bad isr number: %d\n", num);
6061541Srgrimes		return (EINVAL);
6071541Srgrimes	}
6081541Srgrimes	netisrs[num] = NULL;
6091541Srgrimes	return (0);
6101541Srgrimes}
6111541Srgrimes
6121541Srgrimesstatic void
6131541Srgrimesswi_net(void *dummy)
6141541Srgrimes{
6151541Srgrimes	u_int bits;
6161541Srgrimes	int i;
6171541Srgrimes
6181541Srgrimes	bits = atomic_readandclear_int(&netisr);
6191541Srgrimes	while ((i = ffs(bits)) != 0) {
6201541Srgrimes		i--;
6211541Srgrimes		if (netisrs[i] != NULL)
6221541Srgrimes			netisrs[i]();
6231541Srgrimes		else
6241541Srgrimes			printf("swi_net: unregistered isr number: %d.\n", i);
6251541Srgrimes		bits &= ~(1 << i);
6261541Srgrimes	}
6271541Srgrimes}
6281541Srgrimes