xref: /freebsd/sys/kern/kern_intr.c (revision e0f66ef8615145ae7e857974912975bba3a47390)
19454b2d8SWarner Losh /*-
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  */
26425f9fdaSStefan Eßer 
27677b542eSDavid E. O'Brien #include <sys/cdefs.h>
28677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
293900ddb2SDoug Rabson 
308b201c42SJohn Baldwin #include "opt_ddb.h"
318b201c42SJohn Baldwin 
321c5bb3eaSPeter Wemm #include <sys/param.h>
339a94c9c5SJohn Baldwin #include <sys/bus.h>
34c11110eaSAlfred Perlstein #include <sys/conf.h>
359a94c9c5SJohn Baldwin #include <sys/rtprio.h>
36425f9fdaSStefan Eßer #include <sys/systm.h>
3768352337SDoug Rabson #include <sys/interrupt.h>
381931cf94SJohn Baldwin #include <sys/kernel.h>
391931cf94SJohn Baldwin #include <sys/kthread.h>
401931cf94SJohn Baldwin #include <sys/ktr.h>
4105b2c96fSBruce Evans #include <sys/limits.h>
42f34fa851SJohn Baldwin #include <sys/lock.h>
431931cf94SJohn Baldwin #include <sys/malloc.h>
4435e0e5b3SJohn Baldwin #include <sys/mutex.h>
451931cf94SJohn Baldwin #include <sys/proc.h>
463e5da754SJohn Baldwin #include <sys/random.h>
47b4151f71SJohn Baldwin #include <sys/resourcevar.h>
4863710c4dSJohn Baldwin #include <sys/sched.h>
49d279178dSThomas Moestl #include <sys/sysctl.h>
501931cf94SJohn Baldwin #include <sys/unistd.h>
511931cf94SJohn Baldwin #include <sys/vmmeter.h>
521931cf94SJohn Baldwin #include <machine/atomic.h>
531931cf94SJohn Baldwin #include <machine/cpu.h>
548088699fSJohn Baldwin #include <machine/md_var.h>
55b4151f71SJohn Baldwin #include <machine/stdarg.h>
568b201c42SJohn Baldwin #ifdef DDB
578b201c42SJohn Baldwin #include <ddb/ddb.h>
588b201c42SJohn Baldwin #include <ddb/db_sym.h>
598b201c42SJohn Baldwin #endif
60425f9fdaSStefan Eßer 
61e0f66ef8SJohn Baldwin /*
62e0f66ef8SJohn Baldwin  * Describe an interrupt thread.  There is one of these per interrupt event.
63e0f66ef8SJohn Baldwin  */
64e0f66ef8SJohn Baldwin struct intr_thread {
65e0f66ef8SJohn Baldwin 	struct intr_event *it_event;
66e0f66ef8SJohn Baldwin 	struct thread *it_thread;	/* Kernel thread. */
67e0f66ef8SJohn Baldwin 	int	it_flags;		/* (j) IT_* flags. */
68e0f66ef8SJohn Baldwin 	int	it_need;		/* Needs service. */
693e5da754SJohn Baldwin };
703e5da754SJohn Baldwin 
71e0f66ef8SJohn Baldwin /* Interrupt thread flags kept in it_flags */
72e0f66ef8SJohn Baldwin #define	IT_DEAD		0x000001	/* Thread is waiting to exit. */
73e0f66ef8SJohn Baldwin 
74e0f66ef8SJohn Baldwin struct	intr_entropy {
75e0f66ef8SJohn Baldwin 	struct	thread *td;
76e0f66ef8SJohn Baldwin 	uintptr_t event;
77e0f66ef8SJohn Baldwin };
78e0f66ef8SJohn Baldwin 
79e0f66ef8SJohn Baldwin struct	intr_event *clk_intr_event;
80e0f66ef8SJohn Baldwin struct	intr_event *tty_intr_event;
817b1fe905SBruce Evans void	*softclock_ih;
827b1fe905SBruce Evans void	*vm_ih;
831931cf94SJohn Baldwin 
84b4151f71SJohn Baldwin static MALLOC_DEFINE(M_ITHREAD, "ithread", "Interrupt Threads");
85b4151f71SJohn Baldwin 
867870c3c6SJohn Baldwin static int intr_storm_threshold = 500;
877870c3c6SJohn Baldwin TUNABLE_INT("hw.intr_storm_threshold", &intr_storm_threshold);
887870c3c6SJohn Baldwin SYSCTL_INT(_hw, OID_AUTO, intr_storm_threshold, CTLFLAG_RW,
897870c3c6SJohn Baldwin     &intr_storm_threshold, 0,
907b1fe905SBruce Evans     "Number of consecutive interrupts before storm protection is enabled");
91e0f66ef8SJohn Baldwin static TAILQ_HEAD(, intr_event) event_list =
92e0f66ef8SJohn Baldwin     TAILQ_HEAD_INITIALIZER(event_list);
937b1fe905SBruce Evans 
94e0f66ef8SJohn Baldwin static void	intr_event_update(struct intr_event *ie);
95e0f66ef8SJohn Baldwin static struct intr_thread *ithread_create(const char *name);
96e0f66ef8SJohn Baldwin #ifdef notyet
97e0f66ef8SJohn Baldwin static void	ithread_destroy(struct intr_thread *ithread);
98e0f66ef8SJohn Baldwin #endif
99e0f66ef8SJohn Baldwin static void	ithread_execute_handlers(struct proc *p, struct intr_event *ie);
1007b1fe905SBruce Evans static void	ithread_loop(void *);
101e0f66ef8SJohn Baldwin static void	ithread_update(struct intr_thread *ithd);
1027b1fe905SBruce Evans static void	start_softintr(void *);
1037870c3c6SJohn Baldwin 
104b4151f71SJohn Baldwin u_char
105e0f66ef8SJohn Baldwin intr_priority(enum intr_type flags)
1069a94c9c5SJohn Baldwin {
107b4151f71SJohn Baldwin 	u_char pri;
1089a94c9c5SJohn Baldwin 
109b4151f71SJohn Baldwin 	flags &= (INTR_TYPE_TTY | INTR_TYPE_BIO | INTR_TYPE_NET |
1105a280d9cSPeter Wemm 	    INTR_TYPE_CAM | INTR_TYPE_MISC | INTR_TYPE_CLK | INTR_TYPE_AV);
1119a94c9c5SJohn Baldwin 	switch (flags) {
112b4151f71SJohn Baldwin 	case INTR_TYPE_TTY:
1139a94c9c5SJohn Baldwin 		pri = PI_TTYLOW;
1149a94c9c5SJohn Baldwin 		break;
1159a94c9c5SJohn Baldwin 	case INTR_TYPE_BIO:
1169a94c9c5SJohn Baldwin 		/*
1179a94c9c5SJohn Baldwin 		 * XXX We need to refine this.  BSD/OS distinguishes
1189a94c9c5SJohn Baldwin 		 * between tape and disk priorities.
1199a94c9c5SJohn Baldwin 		 */
1209a94c9c5SJohn Baldwin 		pri = PI_DISK;
1219a94c9c5SJohn Baldwin 		break;
1229a94c9c5SJohn Baldwin 	case INTR_TYPE_NET:
1239a94c9c5SJohn Baldwin 		pri = PI_NET;
1249a94c9c5SJohn Baldwin 		break;
1259a94c9c5SJohn Baldwin 	case INTR_TYPE_CAM:
1269a94c9c5SJohn Baldwin 		pri = PI_DISK;          /* XXX or PI_CAM? */
1279a94c9c5SJohn Baldwin 		break;
1285a280d9cSPeter Wemm 	case INTR_TYPE_AV:		/* Audio/video */
1295a280d9cSPeter Wemm 		pri = PI_AV;
1305a280d9cSPeter Wemm 		break;
131b4151f71SJohn Baldwin 	case INTR_TYPE_CLK:
132b4151f71SJohn Baldwin 		pri = PI_REALTIME;
133b4151f71SJohn Baldwin 		break;
1349a94c9c5SJohn Baldwin 	case INTR_TYPE_MISC:
1359a94c9c5SJohn Baldwin 		pri = PI_DULL;          /* don't care */
1369a94c9c5SJohn Baldwin 		break;
1379a94c9c5SJohn Baldwin 	default:
138b4151f71SJohn Baldwin 		/* We didn't specify an interrupt level. */
139e0f66ef8SJohn Baldwin 		panic("intr_priority: no interrupt type in flags");
1409a94c9c5SJohn Baldwin 	}
1419a94c9c5SJohn Baldwin 
1429a94c9c5SJohn Baldwin 	return pri;
1439a94c9c5SJohn Baldwin }
1449a94c9c5SJohn Baldwin 
145b4151f71SJohn Baldwin /*
146e0f66ef8SJohn Baldwin  * Update an ithread based on the associated intr_event.
147b4151f71SJohn Baldwin  */
148b4151f71SJohn Baldwin static void
149e0f66ef8SJohn Baldwin ithread_update(struct intr_thread *ithd)
150b4151f71SJohn Baldwin {
151e0f66ef8SJohn Baldwin 	struct intr_event *ie;
152b40ce416SJulian Elischer 	struct thread *td;
153e0f66ef8SJohn Baldwin 	u_char pri;
1548088699fSJohn Baldwin 
155e0f66ef8SJohn Baldwin 	ie = ithd->it_event;
156e0f66ef8SJohn Baldwin 	td = ithd->it_thread;
157b4151f71SJohn Baldwin 
158e0f66ef8SJohn Baldwin 	/* Determine the overall priority of this event. */
159e0f66ef8SJohn Baldwin 	if (TAILQ_EMPTY(&ie->ie_handlers))
160e0f66ef8SJohn Baldwin 		pri = PRI_MAX_ITHD;
161e0f66ef8SJohn Baldwin 	else
162e0f66ef8SJohn Baldwin 		pri = TAILQ_FIRST(&ie->ie_handlers)->ih_pri;
163e80fb434SRobert Drehmel 
164e0f66ef8SJohn Baldwin 	/* Update name and priority. */
165e0f66ef8SJohn Baldwin 	strlcpy(td->td_proc->p_comm, ie->ie_fullname,
166e0f66ef8SJohn Baldwin 	    sizeof(td->td_proc->p_comm));
167b106d2f5SJohn Baldwin 	mtx_lock_spin(&sched_lock);
168e0f66ef8SJohn Baldwin 	sched_prio(td, pri);
169b106d2f5SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
170b4151f71SJohn Baldwin }
171e0f66ef8SJohn Baldwin 
172e0f66ef8SJohn Baldwin /*
173e0f66ef8SJohn Baldwin  * Regenerate the full name of an interrupt event and update its priority.
174e0f66ef8SJohn Baldwin  */
175e0f66ef8SJohn Baldwin static void
176e0f66ef8SJohn Baldwin intr_event_update(struct intr_event *ie)
177e0f66ef8SJohn Baldwin {
178e0f66ef8SJohn Baldwin 	struct intr_handler *ih;
179e0f66ef8SJohn Baldwin 	char *last;
180e0f66ef8SJohn Baldwin 	int missed, space;
181e0f66ef8SJohn Baldwin 
182e0f66ef8SJohn Baldwin 	/* Start off with no entropy and just the name of the event. */
183e0f66ef8SJohn Baldwin 	mtx_assert(&ie->ie_lock, MA_OWNED);
184e0f66ef8SJohn Baldwin 	strlcpy(ie->ie_fullname, ie->ie_name, sizeof(ie->ie_fullname));
185e0f66ef8SJohn Baldwin 	ie->ie_flags &= ~IE_ENTROPY;
1860811d60aSJohn Baldwin 	missed = 0;
187e0f66ef8SJohn Baldwin 	space = 1;
188e0f66ef8SJohn Baldwin 
189e0f66ef8SJohn Baldwin 	/* Run through all the handlers updating values. */
190e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
191e0f66ef8SJohn Baldwin 		if (strlen(ie->ie_fullname) + strlen(ih->ih_name) + 1 <
192e0f66ef8SJohn Baldwin 		    sizeof(ie->ie_fullname)) {
193e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, " ");
194e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, ih->ih_name);
195e0f66ef8SJohn Baldwin 			space = 0;
1960811d60aSJohn Baldwin 		} else
1970811d60aSJohn Baldwin 			missed++;
1980811d60aSJohn Baldwin 		if (ih->ih_flags & IH_ENTROPY)
199e0f66ef8SJohn Baldwin 			ie->ie_flags |= IE_ENTROPY;
2000811d60aSJohn Baldwin 	}
201e0f66ef8SJohn Baldwin 
202e0f66ef8SJohn Baldwin 	/*
203e0f66ef8SJohn Baldwin 	 * If the handler names were too long, add +'s to indicate missing
204e0f66ef8SJohn Baldwin 	 * names. If we run out of room and still have +'s to add, change
205e0f66ef8SJohn Baldwin 	 * the last character from a + to a *.
206e0f66ef8SJohn Baldwin 	 */
207e0f66ef8SJohn Baldwin 	last = &ie->ie_fullname[sizeof(ie->ie_fullname) - 2];
2080811d60aSJohn Baldwin 	while (missed-- > 0) {
209e0f66ef8SJohn Baldwin 		if (strlen(ie->ie_fullname) + 1 == sizeof(ie->ie_fullname)) {
210e0f66ef8SJohn Baldwin 			if (*last == '+') {
211e0f66ef8SJohn Baldwin 				*last = '*';
212e0f66ef8SJohn Baldwin 				break;
213b4151f71SJohn Baldwin 			} else
214e0f66ef8SJohn Baldwin 				*last = '+';
215e0f66ef8SJohn Baldwin 		} else if (space) {
216e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, " +");
217e0f66ef8SJohn Baldwin 			space = 0;
218e0f66ef8SJohn Baldwin 		} else
219e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, "+");
220b4151f71SJohn Baldwin 	}
221e0f66ef8SJohn Baldwin 
222e0f66ef8SJohn Baldwin 	/*
223e0f66ef8SJohn Baldwin 	 * If this event has an ithread, update it's priority and
224e0f66ef8SJohn Baldwin 	 * name.
225e0f66ef8SJohn Baldwin 	 */
226e0f66ef8SJohn Baldwin 	if (ie->ie_thread != NULL)
227e0f66ef8SJohn Baldwin 		ithread_update(ie->ie_thread);
228e0f66ef8SJohn Baldwin 	CTR2(KTR_INTR, "%s: updated %s", __func__, ie->ie_fullname);
229b4151f71SJohn Baldwin }
230b4151f71SJohn Baldwin 
231b4151f71SJohn Baldwin int
232e0f66ef8SJohn Baldwin intr_event_create(struct intr_event **event, void *source, int flags,
233e0f66ef8SJohn Baldwin     void (*enable)(void *), const char *fmt, ...)
234b4151f71SJohn Baldwin {
235e0f66ef8SJohn Baldwin 	struct intr_event *ie;
236b4151f71SJohn Baldwin 	va_list ap;
237b4151f71SJohn Baldwin 
238e0f66ef8SJohn Baldwin 	/* The only valid flag during creation is IE_SOFT. */
239e0f66ef8SJohn Baldwin 	if ((flags & ~IE_SOFT) != 0)
2403e5da754SJohn Baldwin 		return (EINVAL);
241e0f66ef8SJohn Baldwin 	ie = malloc(sizeof(struct intr_event), M_ITHREAD, M_WAITOK | M_ZERO);
242e0f66ef8SJohn Baldwin 	ie->ie_source = source;
243e0f66ef8SJohn Baldwin 	ie->ie_enable = enable;
244e0f66ef8SJohn Baldwin 	ie->ie_flags = flags;
245e0f66ef8SJohn Baldwin 	TAILQ_INIT(&ie->ie_handlers);
246e0f66ef8SJohn Baldwin 	mtx_init(&ie->ie_lock, "intr event", NULL, MTX_DEF);
247b4151f71SJohn Baldwin 
248b4151f71SJohn Baldwin 	va_start(ap, fmt);
249e0f66ef8SJohn Baldwin 	vsnprintf(ie->ie_name, sizeof(ie->ie_name), fmt, ap);
250b4151f71SJohn Baldwin 	va_end(ap);
251e0f66ef8SJohn Baldwin 	strlcpy(ie->ie_fullname, ie->ie_name, sizeof(ie->ie_fullname));
252e0f66ef8SJohn Baldwin 	mtx_pool_lock(mtxpool_sleep, &event_list);
253e0f66ef8SJohn Baldwin 	TAILQ_INSERT_TAIL(&event_list, ie, ie_list);
254e0f66ef8SJohn Baldwin 	mtx_pool_unlock(mtxpool_sleep, &event_list);
255e0f66ef8SJohn Baldwin 	if (event != NULL)
256e0f66ef8SJohn Baldwin 		*event = ie;
257e0f66ef8SJohn Baldwin 	CTR2(KTR_INTR, "%s: created %s", __func__, ie->ie_name);
258b4151f71SJohn Baldwin 	return (0);
259b4151f71SJohn Baldwin }
260b4151f71SJohn Baldwin 
261b4151f71SJohn Baldwin int
262e0f66ef8SJohn Baldwin intr_event_destroy(struct intr_event *ie)
263b4151f71SJohn Baldwin {
264b4151f71SJohn Baldwin 
265e0f66ef8SJohn Baldwin 	mtx_lock(&ie->ie_lock);
266e0f66ef8SJohn Baldwin 	if (!TAILQ_EMPTY(&ie->ie_handlers)) {
267e0f66ef8SJohn Baldwin 		mtx_unlock(&ie->ie_lock);
268e0f66ef8SJohn Baldwin 		return (EBUSY);
2694d29cb2dSJohn Baldwin 	}
270e0f66ef8SJohn Baldwin 	mtx_pool_lock(mtxpool_sleep, &event_list);
271e0f66ef8SJohn Baldwin 	TAILQ_REMOVE(&event_list, ie, ie_list);
272e0f66ef8SJohn Baldwin 	mtx_pool_unlock(mtxpool_sleep, &event_list);
273e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
274e0f66ef8SJohn Baldwin 	mtx_destroy(&ie->ie_lock);
275e0f66ef8SJohn Baldwin 	free(ie, M_ITHREAD);
276e0f66ef8SJohn Baldwin 	return (0);
277e0f66ef8SJohn Baldwin }
278e0f66ef8SJohn Baldwin 
279e0f66ef8SJohn Baldwin static struct intr_thread *
280e0f66ef8SJohn Baldwin ithread_create(const char *name)
281e0f66ef8SJohn Baldwin {
282e0f66ef8SJohn Baldwin 	struct intr_thread *ithd;
283e0f66ef8SJohn Baldwin 	struct thread *td;
284e0f66ef8SJohn Baldwin 	struct proc *p;
285e0f66ef8SJohn Baldwin 	int error;
286e0f66ef8SJohn Baldwin 
287e0f66ef8SJohn Baldwin 	ithd = malloc(sizeof(struct intr_thread), M_ITHREAD, M_WAITOK | M_ZERO);
288e0f66ef8SJohn Baldwin 
289e0f66ef8SJohn Baldwin 	error = kthread_create(ithread_loop, ithd, &p, RFSTOPPED | RFHIGHPID,
290e0f66ef8SJohn Baldwin 	    0, "%s", name);
291e0f66ef8SJohn Baldwin 	if (error)
292e0f66ef8SJohn Baldwin 		panic("kthread_create() failed with %d", error);
293e0f66ef8SJohn Baldwin 	td = FIRST_THREAD_IN_PROC(p);	/* XXXKSE */
2944d29cb2dSJohn Baldwin 	mtx_lock_spin(&sched_lock);
295e0f66ef8SJohn Baldwin 	td->td_ksegrp->kg_pri_class = PRI_ITHD;
296e0f66ef8SJohn Baldwin 	TD_SET_IWAIT(td);
297e0f66ef8SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
298e0f66ef8SJohn Baldwin 	td->td_pflags |= TDP_ITHREAD;
299e0f66ef8SJohn Baldwin 	ithd->it_thread = td;
300e0f66ef8SJohn Baldwin 	CTR2(KTR_INTR, "%s: created %s", __func__, name);
301e0f66ef8SJohn Baldwin 	return (ithd);
302e0f66ef8SJohn Baldwin }
303e0f66ef8SJohn Baldwin 
304e0f66ef8SJohn Baldwin #ifdef notyet
305e0f66ef8SJohn Baldwin static void
306e0f66ef8SJohn Baldwin ithread_destroy(struct intr_thread *ithread)
307e0f66ef8SJohn Baldwin {
308e0f66ef8SJohn Baldwin 	struct thread *td;
309e0f66ef8SJohn Baldwin 
310e0f66ef8SJohn Baldwin 	td = ithread->it_thread;
311e0f66ef8SJohn Baldwin 	mtx_lock_spin(&sched_lock);
312e0f66ef8SJohn Baldwin 	ithread->it_flags |= IT_DEAD;
31371fad9fdSJulian Elischer 	if (TD_AWAITING_INTR(td)) {
31471fad9fdSJulian Elischer 		TD_CLR_IWAIT(td);
3152630e4c9SJulian Elischer 		setrunqueue(td, SRQ_INTR);
316b4151f71SJohn Baldwin 	}
317b4151f71SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
31891f91617SDavid E. O'Brien 	CTR2(KTR_INTR, "%s: killing %s", __func__, ithread->it_name);
319b4151f71SJohn Baldwin }
320e0f66ef8SJohn Baldwin #endif
321b4151f71SJohn Baldwin 
322b4151f71SJohn Baldwin int
323e0f66ef8SJohn Baldwin intr_event_add_handler(struct intr_event *ie, const char *name,
324b4151f71SJohn Baldwin     driver_intr_t handler, void *arg, u_char pri, enum intr_type flags,
325b4151f71SJohn Baldwin     void **cookiep)
326b4151f71SJohn Baldwin {
327e0f66ef8SJohn Baldwin 	struct intr_handler *ih, *temp_ih;
328e0f66ef8SJohn Baldwin 	struct intr_thread *it;
329b4151f71SJohn Baldwin 
330e0f66ef8SJohn Baldwin 	if (ie == NULL || name == NULL || handler == NULL)
331b4151f71SJohn Baldwin 		return (EINVAL);
332b4151f71SJohn Baldwin 
333e0f66ef8SJohn Baldwin 	/* Allocate and populate an interrupt handler structure. */
334e0f66ef8SJohn Baldwin 	ih = malloc(sizeof(struct intr_handler), M_ITHREAD, M_WAITOK | M_ZERO);
335b4151f71SJohn Baldwin 	ih->ih_handler = handler;
336b4151f71SJohn Baldwin 	ih->ih_argument = arg;
337b4151f71SJohn Baldwin 	ih->ih_name = name;
338e0f66ef8SJohn Baldwin 	ih->ih_event = ie;
339b4151f71SJohn Baldwin 	ih->ih_pri = pri;
340b4151f71SJohn Baldwin 	if (flags & INTR_FAST)
3418bc08464SJohn Baldwin 		ih->ih_flags = IH_FAST;
342b4151f71SJohn Baldwin 	else if (flags & INTR_EXCL)
343b4151f71SJohn Baldwin 		ih->ih_flags = IH_EXCLUSIVE;
344b4151f71SJohn Baldwin 	if (flags & INTR_MPSAFE)
345b4151f71SJohn Baldwin 		ih->ih_flags |= IH_MPSAFE;
346b4151f71SJohn Baldwin 	if (flags & INTR_ENTROPY)
347b4151f71SJohn Baldwin 		ih->ih_flags |= IH_ENTROPY;
348b4151f71SJohn Baldwin 
349e0f66ef8SJohn Baldwin 	/* We can only have one exclusive handler in a event. */
350e0f66ef8SJohn Baldwin 	mtx_lock(&ie->ie_lock);
351e0f66ef8SJohn Baldwin 	if (!TAILQ_EMPTY(&ie->ie_handlers)) {
352e0f66ef8SJohn Baldwin 		if ((flags & INTR_EXCL) ||
353e0f66ef8SJohn Baldwin 		    (TAILQ_FIRST(&ie->ie_handlers)->ih_flags & IH_EXCLUSIVE)) {
354e0f66ef8SJohn Baldwin 			mtx_unlock(&ie->ie_lock);
355b4151f71SJohn Baldwin 			free(ih, M_ITHREAD);
356b4151f71SJohn Baldwin 			return (EINVAL);
357b4151f71SJohn Baldwin 		}
358e0f66ef8SJohn Baldwin 	}
359e0f66ef8SJohn Baldwin 
360e0f66ef8SJohn Baldwin 	/* Add the new handler to the event in priority order. */
361e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(temp_ih, &ie->ie_handlers, ih_next) {
362e0f66ef8SJohn Baldwin 		if (temp_ih->ih_pri > ih->ih_pri)
363e0f66ef8SJohn Baldwin 			break;
364e0f66ef8SJohn Baldwin 	}
365e0f66ef8SJohn Baldwin 	if (temp_ih == NULL)
366e0f66ef8SJohn Baldwin 		TAILQ_INSERT_TAIL(&ie->ie_handlers, ih, ih_next);
367e0f66ef8SJohn Baldwin 	else
368e0f66ef8SJohn Baldwin 		TAILQ_INSERT_BEFORE(temp_ih, ih, ih_next);
369e0f66ef8SJohn Baldwin 	intr_event_update(ie);
370e0f66ef8SJohn Baldwin 
371e0f66ef8SJohn Baldwin 	/* Create a thread if we need one. */
372e0f66ef8SJohn Baldwin 	while (ie->ie_thread == NULL && !(flags & INTR_FAST)) {
373e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_ADDING_THREAD)
374e0f66ef8SJohn Baldwin 			msleep(ie, &ie->ie_lock, curthread->td_priority,
375e0f66ef8SJohn Baldwin 			    "ithread", 0);
376e0f66ef8SJohn Baldwin 		else {
377e0f66ef8SJohn Baldwin 			ie->ie_flags |= IE_ADDING_THREAD;
378e0f66ef8SJohn Baldwin 			mtx_unlock(&ie->ie_lock);
379e0f66ef8SJohn Baldwin 			it = ithread_create("intr: newborn");
380e0f66ef8SJohn Baldwin 			mtx_lock(&ie->ie_lock);
381e0f66ef8SJohn Baldwin 			ie->ie_flags &= ~IE_ADDING_THREAD;
382e0f66ef8SJohn Baldwin 			ie->ie_thread = it;
383e0f66ef8SJohn Baldwin 			it->it_event = ie;
384e0f66ef8SJohn Baldwin 			ithread_update(it);
385e0f66ef8SJohn Baldwin 			wakeup(ie);
386e0f66ef8SJohn Baldwin 		}
387e0f66ef8SJohn Baldwin 	}
388e0f66ef8SJohn Baldwin 	CTR3(KTR_INTR, "%s: added %s to %s", __func__, ih->ih_name,
389e0f66ef8SJohn Baldwin 	    ie->ie_name);
390e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
391e0f66ef8SJohn Baldwin 
392e0f66ef8SJohn Baldwin 	if (cookiep != NULL)
393e0f66ef8SJohn Baldwin 		*cookiep = ih;
394e0f66ef8SJohn Baldwin 	return (0);
395e0f66ef8SJohn Baldwin }
396b4151f71SJohn Baldwin 
397b4151f71SJohn Baldwin int
398e0f66ef8SJohn Baldwin intr_event_remove_handler(void *cookie)
399b4151f71SJohn Baldwin {
400e0f66ef8SJohn Baldwin 	struct intr_handler *handler = (struct intr_handler *)cookie;
401e0f66ef8SJohn Baldwin 	struct intr_event *ie;
402b4151f71SJohn Baldwin #ifdef INVARIANTS
403e0f66ef8SJohn Baldwin 	struct intr_handler *ih;
404e0f66ef8SJohn Baldwin #endif
405e0f66ef8SJohn Baldwin #ifdef notyet
406e0f66ef8SJohn Baldwin 	int dead;
407b4151f71SJohn Baldwin #endif
408b4151f71SJohn Baldwin 
4093e5da754SJohn Baldwin 	if (handler == NULL)
410b4151f71SJohn Baldwin 		return (EINVAL);
411e0f66ef8SJohn Baldwin 	ie = handler->ih_event;
412e0f66ef8SJohn Baldwin 	KASSERT(ie != NULL,
413e0f66ef8SJohn Baldwin 	    ("interrupt handler \"%s\" has a NULL interrupt event",
4143e5da754SJohn Baldwin 		handler->ih_name));
415e0f66ef8SJohn Baldwin 	mtx_lock(&ie->ie_lock);
41691f91617SDavid E. O'Brien 	CTR3(KTR_INTR, "%s: removing %s from %s", __func__, handler->ih_name,
417e0f66ef8SJohn Baldwin 	    ie->ie_name);
418b4151f71SJohn Baldwin #ifdef INVARIANTS
419e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next)
4203e5da754SJohn Baldwin 		if (ih == handler)
4213e5da754SJohn Baldwin 			goto ok;
422e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
423e0f66ef8SJohn Baldwin 	panic("interrupt handler \"%s\" not found in interrupt event \"%s\"",
424e0f66ef8SJohn Baldwin 	    ih->ih_name, ie->ie_name);
4253e5da754SJohn Baldwin ok:
426b4151f71SJohn Baldwin #endif
427de271f01SJohn Baldwin 	/*
428e0f66ef8SJohn Baldwin 	 * If there is no ithread, then just remove the handler and return.
429e0f66ef8SJohn Baldwin 	 * XXX: Note that an INTR_FAST handler might be running on another
430e0f66ef8SJohn Baldwin 	 * CPU!
431e0f66ef8SJohn Baldwin 	 */
432e0f66ef8SJohn Baldwin 	if (ie->ie_thread == NULL) {
433e0f66ef8SJohn Baldwin 		TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next);
434e0f66ef8SJohn Baldwin 		mtx_unlock(&ie->ie_lock);
435e0f66ef8SJohn Baldwin 		free(handler, M_ITHREAD);
436e0f66ef8SJohn Baldwin 		return (0);
437e0f66ef8SJohn Baldwin 	}
438e0f66ef8SJohn Baldwin 
439e0f66ef8SJohn Baldwin 	/*
440de271f01SJohn Baldwin 	 * If the interrupt thread is already running, then just mark this
441de271f01SJohn Baldwin 	 * handler as being dead and let the ithread do the actual removal.
442288e351bSDon Lewis 	 *
443288e351bSDon Lewis 	 * During a cold boot while cold is set, msleep() does not sleep,
444288e351bSDon Lewis 	 * so we have to remove the handler here rather than letting the
445288e351bSDon Lewis 	 * thread do it.
446de271f01SJohn Baldwin 	 */
447de271f01SJohn Baldwin 	mtx_lock_spin(&sched_lock);
448e0f66ef8SJohn Baldwin 	if (!TD_AWAITING_INTR(ie->ie_thread->it_thread) && !cold) {
449de271f01SJohn Baldwin 		handler->ih_flags |= IH_DEAD;
450de271f01SJohn Baldwin 
451de271f01SJohn Baldwin 		/*
452de271f01SJohn Baldwin 		 * Ensure that the thread will process the handler list
453de271f01SJohn Baldwin 		 * again and remove this handler if it has already passed
454de271f01SJohn Baldwin 		 * it on the list.
455de271f01SJohn Baldwin 		 */
456e0f66ef8SJohn Baldwin 		ie->ie_thread->it_need = 1;
4574d29cb2dSJohn Baldwin 	} else
458e0f66ef8SJohn Baldwin 		TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next);
459de271f01SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
460e0f66ef8SJohn Baldwin 	while (handler->ih_flags & IH_DEAD)
461e0f66ef8SJohn Baldwin 		msleep(handler, &ie->ie_lock, curthread->td_priority, "iev_rmh",
462e0f66ef8SJohn Baldwin 		    0);
463e0f66ef8SJohn Baldwin 	intr_event_update(ie);
464e0f66ef8SJohn Baldwin #ifdef notyet
465e0f66ef8SJohn Baldwin 	/*
466e0f66ef8SJohn Baldwin 	 * XXX: This could be bad in the case of ppbus(8).  Also, I think
467e0f66ef8SJohn Baldwin 	 * this could lead to races of stale data when servicing an
468e0f66ef8SJohn Baldwin 	 * interrupt.
469e0f66ef8SJohn Baldwin 	 */
470e0f66ef8SJohn Baldwin 	dead = 1;
471e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
472e0f66ef8SJohn Baldwin 		if (!(ih->ih_flags & IH_FAST)) {
473e0f66ef8SJohn Baldwin 			dead = 0;
474e0f66ef8SJohn Baldwin 			break;
475e0f66ef8SJohn Baldwin 		}
476e0f66ef8SJohn Baldwin 	}
477e0f66ef8SJohn Baldwin 	if (dead) {
478e0f66ef8SJohn Baldwin 		ithread_destroy(ie->ie_thread);
479e0f66ef8SJohn Baldwin 		ie->ie_thread = NULL;
480e0f66ef8SJohn Baldwin 	}
481e0f66ef8SJohn Baldwin #endif
482e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
483b4151f71SJohn Baldwin 	free(handler, M_ITHREAD);
484b4151f71SJohn Baldwin 	return (0);
485b4151f71SJohn Baldwin }
486b4151f71SJohn Baldwin 
487b4151f71SJohn Baldwin int
488e0f66ef8SJohn Baldwin intr_event_schedule_thread(struct intr_event *ie)
4893e5da754SJohn Baldwin {
490e0f66ef8SJohn Baldwin 	struct intr_entropy entropy;
491e0f66ef8SJohn Baldwin 	struct intr_thread *it;
492b40ce416SJulian Elischer 	struct thread *td;
49304774f23SJulian Elischer 	struct thread *ctd;
4943e5da754SJohn Baldwin 	struct proc *p;
4953e5da754SJohn Baldwin 
4963e5da754SJohn Baldwin 	/*
4973e5da754SJohn Baldwin 	 * If no ithread or no handlers, then we have a stray interrupt.
4983e5da754SJohn Baldwin 	 */
499e0f66ef8SJohn Baldwin 	if (ie == NULL || TAILQ_EMPTY(&ie->ie_handlers) ||
500e0f66ef8SJohn Baldwin 	    ie->ie_thread == NULL)
5013e5da754SJohn Baldwin 		return (EINVAL);
5023e5da754SJohn Baldwin 
50304774f23SJulian Elischer 	ctd = curthread;
504e0f66ef8SJohn Baldwin 	it = ie->ie_thread;
505e0f66ef8SJohn Baldwin 	td = it->it_thread;
5066f40c417SRobert Watson 	p = td->td_proc;
507e0f66ef8SJohn Baldwin 
5083e5da754SJohn Baldwin 	/*
5093e5da754SJohn Baldwin 	 * If any of the handlers for this ithread claim to be good
5103e5da754SJohn Baldwin 	 * sources of entropy, then gather some.
5113e5da754SJohn Baldwin 	 */
512e0f66ef8SJohn Baldwin 	if (harvest.interrupt && ie->ie_flags & IE_ENTROPY) {
5136f40c417SRobert Watson 		CTR3(KTR_INTR, "%s: pid %d (%s) gathering entropy", __func__,
5146f40c417SRobert Watson 		    p->p_pid, p->p_comm);
515e0f66ef8SJohn Baldwin 		entropy.event = (uintptr_t)ie;
516e0f66ef8SJohn Baldwin 		entropy.td = ctd;
5173e5da754SJohn Baldwin 		random_harvest(&entropy, sizeof(entropy), 2, 0,
5183e5da754SJohn Baldwin 		    RANDOM_INTERRUPT);
5193e5da754SJohn Baldwin 	}
5203e5da754SJohn Baldwin 
521e0f66ef8SJohn Baldwin 	KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name));
5223e5da754SJohn Baldwin 
5233e5da754SJohn Baldwin 	/*
5243e5da754SJohn Baldwin 	 * Set it_need to tell the thread to keep running if it is already
5253e5da754SJohn Baldwin 	 * running.  Then, grab sched_lock and see if we actually need to
5260c0b25aeSJohn Baldwin 	 * put this thread on the runqueue.
5273e5da754SJohn Baldwin 	 */
528e0f66ef8SJohn Baldwin 	it->it_need = 1;
5293e5da754SJohn Baldwin 	mtx_lock_spin(&sched_lock);
53071fad9fdSJulian Elischer 	if (TD_AWAITING_INTR(td)) {
531e0f66ef8SJohn Baldwin 		CTR3(KTR_INTR, "%s: schedule pid %d (%s)", __func__, p->p_pid,
532e0f66ef8SJohn Baldwin 		    p->p_comm);
53371fad9fdSJulian Elischer 		TD_CLR_IWAIT(td);
5342630e4c9SJulian Elischer 		setrunqueue(td, SRQ_INTR);
5353e5da754SJohn Baldwin 	} else {
536e0f66ef8SJohn Baldwin 		CTR5(KTR_INTR, "%s: pid %d (%s): it_need %d, state %d",
537e0f66ef8SJohn Baldwin 		    __func__, p->p_pid, p->p_comm, it->it_need, td->td_state);
5383e5da754SJohn Baldwin 	}
5393e5da754SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
5403e5da754SJohn Baldwin 
5413e5da754SJohn Baldwin 	return (0);
5423e5da754SJohn Baldwin }
5433e5da754SJohn Baldwin 
5443e5da754SJohn Baldwin int
545e0f66ef8SJohn Baldwin swi_add(struct intr_event **eventp, const char *name, driver_intr_t handler,
546b4151f71SJohn Baldwin 	    void *arg, int pri, enum intr_type flags, void **cookiep)
5478088699fSJohn Baldwin {
548e0f66ef8SJohn Baldwin 	struct intr_event *ie;
549b4151f71SJohn Baldwin 	int error;
5508088699fSJohn Baldwin 
5513e5da754SJohn Baldwin 	if (flags & (INTR_FAST | INTR_ENTROPY))
5523e5da754SJohn Baldwin 		return (EINVAL);
5533e5da754SJohn Baldwin 
554e0f66ef8SJohn Baldwin 	ie = (eventp != NULL) ? *eventp : NULL;
5558088699fSJohn Baldwin 
556e0f66ef8SJohn Baldwin 	if (ie != NULL) {
557e0f66ef8SJohn Baldwin 		if (!(ie->ie_flags & IE_SOFT))
5583e5da754SJohn Baldwin 			return (EINVAL);
5593e5da754SJohn Baldwin 	} else {
560e0f66ef8SJohn Baldwin 		error = intr_event_create(&ie, NULL, IE_SOFT, NULL,
561b4151f71SJohn Baldwin 		    "swi%d:", pri);
5628088699fSJohn Baldwin 		if (error)
563b4151f71SJohn Baldwin 			return (error);
564e0f66ef8SJohn Baldwin 		if (eventp != NULL)
565e0f66ef8SJohn Baldwin 			*eventp = ie;
5668088699fSJohn Baldwin 	}
567e0f66ef8SJohn Baldwin 	return (intr_event_add_handler(ie, name, handler, arg,
568d5a08a60SJake Burkholder 		    (pri * RQ_PPQ) + PI_SOFT, flags, cookiep));
569ed062c8dSJulian Elischer 		    /* XXKSE.. think of a better way to get separate queues */
5708088699fSJohn Baldwin }
5718088699fSJohn Baldwin 
5721931cf94SJohn Baldwin /*
573e0f66ef8SJohn Baldwin  * Schedule a software interrupt thread.
5741931cf94SJohn Baldwin  */
5751931cf94SJohn Baldwin void
576b4151f71SJohn Baldwin swi_sched(void *cookie, int flags)
5771931cf94SJohn Baldwin {
578e0f66ef8SJohn Baldwin 	struct intr_handler *ih = (struct intr_handler *)cookie;
579e0f66ef8SJohn Baldwin 	struct intr_event *ie = ih->ih_event;
5803e5da754SJohn Baldwin 	int error;
5818088699fSJohn Baldwin 
582aa9aa68dSJohn Baldwin 	PCPU_LAZY_INC(cnt.v_intr);
5831931cf94SJohn Baldwin 
584e0f66ef8SJohn Baldwin 	CTR3(KTR_INTR, "swi_sched: %s %s need=%d", ie->ie_name, ih->ih_name,
585e0f66ef8SJohn Baldwin 	    ih->ih_need);
5861931cf94SJohn Baldwin 
5871931cf94SJohn Baldwin 	/*
5883e5da754SJohn Baldwin 	 * Set ih_need for this handler so that if the ithread is already
5893e5da754SJohn Baldwin 	 * running it will execute this handler on the next pass.  Otherwise,
5903e5da754SJohn Baldwin 	 * it will execute it the next time it runs.
5911931cf94SJohn Baldwin 	 */
592b4151f71SJohn Baldwin 	atomic_store_rel_int(&ih->ih_need, 1);
593b4151f71SJohn Baldwin 	if (!(flags & SWI_DELAY)) {
594e0f66ef8SJohn Baldwin 		error = intr_event_schedule_thread(ie);
5953e5da754SJohn Baldwin 		KASSERT(error == 0, ("stray software interrupt"));
5968088699fSJohn Baldwin 	}
5978088699fSJohn Baldwin }
5988088699fSJohn Baldwin 
599e0f66ef8SJohn Baldwin static void
600e0f66ef8SJohn Baldwin ithread_execute_handlers(struct proc *p, struct intr_event *ie)
601e0f66ef8SJohn Baldwin {
602e0f66ef8SJohn Baldwin 	struct intr_handler *ih, *ihn;
603e0f66ef8SJohn Baldwin 
604e0f66ef8SJohn Baldwin 	/* Interrupt handlers should not sleep. */
605e0f66ef8SJohn Baldwin 	if (!(ie->ie_flags & IE_SOFT))
606e0f66ef8SJohn Baldwin 		THREAD_NO_SLEEPING();
607e0f66ef8SJohn Baldwin 	TAILQ_FOREACH_SAFE(ih, &ie->ie_handlers, ih_next, ihn) {
608e0f66ef8SJohn Baldwin 
609e0f66ef8SJohn Baldwin 		/*
610e0f66ef8SJohn Baldwin 		 * If this handler is marked for death, remove it from
611e0f66ef8SJohn Baldwin 		 * the list of handlers and wake up the sleeper.
612e0f66ef8SJohn Baldwin 		 */
613e0f66ef8SJohn Baldwin 		if (ih->ih_flags & IH_DEAD) {
614e0f66ef8SJohn Baldwin 			mtx_lock(&ie->ie_lock);
615e0f66ef8SJohn Baldwin 			TAILQ_REMOVE(&ie->ie_handlers, ih, ih_next);
616e0f66ef8SJohn Baldwin 			ih->ih_flags &= ~IH_DEAD;
617e0f66ef8SJohn Baldwin 			wakeup(ih);
618e0f66ef8SJohn Baldwin 			mtx_unlock(&ie->ie_lock);
619e0f66ef8SJohn Baldwin 			continue;
620e0f66ef8SJohn Baldwin 		}
621e0f66ef8SJohn Baldwin 
622e0f66ef8SJohn Baldwin 		/*
623e0f66ef8SJohn Baldwin 		 * For software interrupt threads, we only execute
624e0f66ef8SJohn Baldwin 		 * handlers that have their need flag set.  Hardware
625e0f66ef8SJohn Baldwin 		 * interrupt threads always invoke all of their handlers.
626e0f66ef8SJohn Baldwin 		 */
627e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_SOFT) {
628e0f66ef8SJohn Baldwin 			if (!ih->ih_need)
629e0f66ef8SJohn Baldwin 				continue;
630e0f66ef8SJohn Baldwin 			else
631e0f66ef8SJohn Baldwin 				atomic_store_rel_int(&ih->ih_need, 0);
632e0f66ef8SJohn Baldwin 		}
633e0f66ef8SJohn Baldwin 
634e0f66ef8SJohn Baldwin 		/* Fast handlers are handled in primary interrupt context. */
635e0f66ef8SJohn Baldwin 		if (ih->ih_flags & IH_FAST)
636e0f66ef8SJohn Baldwin 			continue;
637e0f66ef8SJohn Baldwin 
638e0f66ef8SJohn Baldwin 		/* Execute this handler. */
639e0f66ef8SJohn Baldwin 		CTR6(KTR_INTR, "%s: pid %d exec %p(%p) for %s flg=%x",
640e0f66ef8SJohn Baldwin 		    __func__, p->p_pid, (void *)ih->ih_handler, ih->ih_argument,
641e0f66ef8SJohn Baldwin 		    ih->ih_name, ih->ih_flags);
642e0f66ef8SJohn Baldwin 
643e0f66ef8SJohn Baldwin 		if (!(ih->ih_flags & IH_MPSAFE))
644e0f66ef8SJohn Baldwin 			mtx_lock(&Giant);
645e0f66ef8SJohn Baldwin 		ih->ih_handler(ih->ih_argument);
646e0f66ef8SJohn Baldwin 		if (!(ih->ih_flags & IH_MPSAFE))
647e0f66ef8SJohn Baldwin 			mtx_unlock(&Giant);
648e0f66ef8SJohn Baldwin 	}
649e0f66ef8SJohn Baldwin 	if (!(ie->ie_flags & IE_SOFT))
650e0f66ef8SJohn Baldwin 		THREAD_SLEEPING_OK();
651e0f66ef8SJohn Baldwin 
652e0f66ef8SJohn Baldwin 	/*
653e0f66ef8SJohn Baldwin 	 * Interrupt storm handling:
654e0f66ef8SJohn Baldwin 	 *
655e0f66ef8SJohn Baldwin 	 * If this interrupt source is currently storming, then throttle
656e0f66ef8SJohn Baldwin 	 * it to only fire the handler once  per clock tick.
657e0f66ef8SJohn Baldwin 	 *
658e0f66ef8SJohn Baldwin 	 * If this interrupt source is not currently storming, but the
659e0f66ef8SJohn Baldwin 	 * number of back to back interrupts exceeds the storm threshold,
660e0f66ef8SJohn Baldwin 	 * then enter storming mode.
661e0f66ef8SJohn Baldwin 	 */
662e0f66ef8SJohn Baldwin 	if (intr_storm_threshold != 0 && ie->ie_count >= intr_storm_threshold) {
663e0f66ef8SJohn Baldwin 		if (ie->ie_warned == 0) {
664e0f66ef8SJohn Baldwin 			printf(
665e0f66ef8SJohn Baldwin 	"Interrupt storm detected on \"%s\"; throttling interrupt source\n",
666e0f66ef8SJohn Baldwin 			    ie->ie_name);
667e0f66ef8SJohn Baldwin 			ie->ie_warned = 1;
668e0f66ef8SJohn Baldwin 		}
669e0f66ef8SJohn Baldwin 		tsleep(&ie->ie_count, curthread->td_priority, "istorm", 1);
670e0f66ef8SJohn Baldwin 	} else
671e0f66ef8SJohn Baldwin 		ie->ie_count++;
672e0f66ef8SJohn Baldwin 
673e0f66ef8SJohn Baldwin 	/*
674e0f66ef8SJohn Baldwin 	 * Now that all the handlers have had a chance to run, reenable
675e0f66ef8SJohn Baldwin 	 * the interrupt source.
676e0f66ef8SJohn Baldwin 	 */
677e0f66ef8SJohn Baldwin 	if (ie->ie_enable != NULL)
678e0f66ef8SJohn Baldwin 		ie->ie_enable(ie->ie_source);
679e0f66ef8SJohn Baldwin }
680e0f66ef8SJohn Baldwin 
6818088699fSJohn Baldwin /*
682b4151f71SJohn Baldwin  * This is the main code for interrupt threads.
6838088699fSJohn Baldwin  */
68437c84183SPoul-Henning Kamp static void
685b4151f71SJohn Baldwin ithread_loop(void *arg)
6868088699fSJohn Baldwin {
687e0f66ef8SJohn Baldwin 	struct intr_thread *ithd;
688e0f66ef8SJohn Baldwin 	struct intr_event *ie;
689b40ce416SJulian Elischer 	struct thread *td;
690b4151f71SJohn Baldwin 	struct proc *p;
6918088699fSJohn Baldwin 
692b40ce416SJulian Elischer 	td = curthread;
693b40ce416SJulian Elischer 	p = td->td_proc;
694e0f66ef8SJohn Baldwin 	ithd = (struct intr_thread *)arg;
695e0f66ef8SJohn Baldwin 	KASSERT(ithd->it_thread == td,
69691f91617SDavid E. O'Brien 	    ("%s: ithread and proc linkage out of sync", __func__));
697e0f66ef8SJohn Baldwin 	ie = ithd->it_event;
698e0f66ef8SJohn Baldwin 	ie->ie_count = 0;
6998088699fSJohn Baldwin 
7008088699fSJohn Baldwin 	/*
7018088699fSJohn Baldwin 	 * As long as we have interrupts outstanding, go through the
7028088699fSJohn Baldwin 	 * list of handlers, giving each one a go at it.
7038088699fSJohn Baldwin 	 */
7048088699fSJohn Baldwin 	for (;;) {
705b4151f71SJohn Baldwin 		/*
706b4151f71SJohn Baldwin 		 * If we are an orphaned thread, then just die.
707b4151f71SJohn Baldwin 		 */
708b4151f71SJohn Baldwin 		if (ithd->it_flags & IT_DEAD) {
709e0f66ef8SJohn Baldwin 			CTR3(KTR_INTR, "%s: pid %d (%s) exiting", __func__,
710b4151f71SJohn Baldwin 			    p->p_pid, p->p_comm);
711b4151f71SJohn Baldwin 			free(ithd, M_ITHREAD);
712b4151f71SJohn Baldwin 			kthread_exit(0);
713b4151f71SJohn Baldwin 		}
714b4151f71SJohn Baldwin 
715e0f66ef8SJohn Baldwin 		/*
716e0f66ef8SJohn Baldwin 		 * Service interrupts.  If another interrupt arrives while
717e0f66ef8SJohn Baldwin 		 * we are running, it will set it_need to note that we
718e0f66ef8SJohn Baldwin 		 * should make another pass.
719e0f66ef8SJohn Baldwin 		 */
720b4151f71SJohn Baldwin 		while (ithd->it_need) {
7218088699fSJohn Baldwin 			/*
722e0f66ef8SJohn Baldwin 			 * This might need a full read and write barrier
723e0f66ef8SJohn Baldwin 			 * to make sure that this write posts before any
724e0f66ef8SJohn Baldwin 			 * of the memory or device accesses in the
725e0f66ef8SJohn Baldwin 			 * handlers.
7268088699fSJohn Baldwin 			 */
727b4151f71SJohn Baldwin 			atomic_store_rel_int(&ithd->it_need, 0);
728e0f66ef8SJohn Baldwin 			ithread_execute_handlers(p, ie);
7298088699fSJohn Baldwin 		}
7307870c3c6SJohn Baldwin 		WITNESS_WARN(WARN_PANIC, NULL, "suspending ithread");
7317870c3c6SJohn Baldwin 		mtx_assert(&Giant, MA_NOTOWNED);
7328088699fSJohn Baldwin 
7338088699fSJohn Baldwin 		/*
7348088699fSJohn Baldwin 		 * Processed all our interrupts.  Now get the sched
7358088699fSJohn Baldwin 		 * lock.  This may take a while and it_need may get
7368088699fSJohn Baldwin 		 * set again, so we have to check it again.
7378088699fSJohn Baldwin 		 */
7389ed346baSBosko Milekic 		mtx_lock_spin(&sched_lock);
739e0f66ef8SJohn Baldwin 		if (!ithd->it_need && !(ithd->it_flags & IT_DEAD)) {
7407870c3c6SJohn Baldwin 			TD_SET_IWAIT(td);
741e0f66ef8SJohn Baldwin 			ie->ie_count = 0;
742bf0acc27SJohn Baldwin 			mi_switch(SW_VOL, NULL);
7438088699fSJohn Baldwin 		}
7449ed346baSBosko Milekic 		mtx_unlock_spin(&sched_lock);
7458088699fSJohn Baldwin 	}
7461931cf94SJohn Baldwin }
7471931cf94SJohn Baldwin 
7488b201c42SJohn Baldwin #ifdef DDB
7498b201c42SJohn Baldwin /*
7508b201c42SJohn Baldwin  * Dump details about an interrupt handler
7518b201c42SJohn Baldwin  */
7528b201c42SJohn Baldwin static void
753e0f66ef8SJohn Baldwin db_dump_intrhand(struct intr_handler *ih)
7548b201c42SJohn Baldwin {
7558b201c42SJohn Baldwin 	int comma;
7568b201c42SJohn Baldwin 
7578b201c42SJohn Baldwin 	db_printf("\t%-10s ", ih->ih_name);
7588b201c42SJohn Baldwin 	switch (ih->ih_pri) {
7598b201c42SJohn Baldwin 	case PI_REALTIME:
7608b201c42SJohn Baldwin 		db_printf("CLK ");
7618b201c42SJohn Baldwin 		break;
7628b201c42SJohn Baldwin 	case PI_AV:
7638b201c42SJohn Baldwin 		db_printf("AV  ");
7648b201c42SJohn Baldwin 		break;
7658b201c42SJohn Baldwin 	case PI_TTYHIGH:
7668b201c42SJohn Baldwin 	case PI_TTYLOW:
7678b201c42SJohn Baldwin 		db_printf("TTY ");
7688b201c42SJohn Baldwin 		break;
7698b201c42SJohn Baldwin 	case PI_TAPE:
7708b201c42SJohn Baldwin 		db_printf("TAPE");
7718b201c42SJohn Baldwin 		break;
7728b201c42SJohn Baldwin 	case PI_NET:
7738b201c42SJohn Baldwin 		db_printf("NET ");
7748b201c42SJohn Baldwin 		break;
7758b201c42SJohn Baldwin 	case PI_DISK:
7768b201c42SJohn Baldwin 	case PI_DISKLOW:
7778b201c42SJohn Baldwin 		db_printf("DISK");
7788b201c42SJohn Baldwin 		break;
7798b201c42SJohn Baldwin 	case PI_DULL:
7808b201c42SJohn Baldwin 		db_printf("DULL");
7818b201c42SJohn Baldwin 		break;
7828b201c42SJohn Baldwin 	default:
7838b201c42SJohn Baldwin 		if (ih->ih_pri >= PI_SOFT)
7848b201c42SJohn Baldwin 			db_printf("SWI ");
7858b201c42SJohn Baldwin 		else
7868b201c42SJohn Baldwin 			db_printf("%4u", ih->ih_pri);
7878b201c42SJohn Baldwin 		break;
7888b201c42SJohn Baldwin 	}
7898b201c42SJohn Baldwin 	db_printf(" ");
7908b201c42SJohn Baldwin 	db_printsym((uintptr_t)ih->ih_handler, DB_STGY_PROC);
7918b201c42SJohn Baldwin 	db_printf("(%p)", ih->ih_argument);
7928b201c42SJohn Baldwin 	if (ih->ih_need ||
7938b201c42SJohn Baldwin 	    (ih->ih_flags & (IH_FAST | IH_EXCLUSIVE | IH_ENTROPY | IH_DEAD |
7948b201c42SJohn Baldwin 	    IH_MPSAFE)) != 0) {
7958b201c42SJohn Baldwin 		db_printf(" {");
7968b201c42SJohn Baldwin 		comma = 0;
7978b201c42SJohn Baldwin 		if (ih->ih_flags & IH_FAST) {
7988b201c42SJohn Baldwin 			db_printf("FAST");
7998b201c42SJohn Baldwin 			comma = 1;
8008b201c42SJohn Baldwin 		}
8018b201c42SJohn Baldwin 		if (ih->ih_flags & IH_EXCLUSIVE) {
8028b201c42SJohn Baldwin 			if (comma)
8038b201c42SJohn Baldwin 				db_printf(", ");
8048b201c42SJohn Baldwin 			db_printf("EXCL");
8058b201c42SJohn Baldwin 			comma = 1;
8068b201c42SJohn Baldwin 		}
8078b201c42SJohn Baldwin 		if (ih->ih_flags & IH_ENTROPY) {
8088b201c42SJohn Baldwin 			if (comma)
8098b201c42SJohn Baldwin 				db_printf(", ");
8108b201c42SJohn Baldwin 			db_printf("ENTROPY");
8118b201c42SJohn Baldwin 			comma = 1;
8128b201c42SJohn Baldwin 		}
8138b201c42SJohn Baldwin 		if (ih->ih_flags & IH_DEAD) {
8148b201c42SJohn Baldwin 			if (comma)
8158b201c42SJohn Baldwin 				db_printf(", ");
8168b201c42SJohn Baldwin 			db_printf("DEAD");
8178b201c42SJohn Baldwin 			comma = 1;
8188b201c42SJohn Baldwin 		}
8198b201c42SJohn Baldwin 		if (ih->ih_flags & IH_MPSAFE) {
8208b201c42SJohn Baldwin 			if (comma)
8218b201c42SJohn Baldwin 				db_printf(", ");
8228b201c42SJohn Baldwin 			db_printf("MPSAFE");
8238b201c42SJohn Baldwin 			comma = 1;
8248b201c42SJohn Baldwin 		}
8258b201c42SJohn Baldwin 		if (ih->ih_need) {
8268b201c42SJohn Baldwin 			if (comma)
8278b201c42SJohn Baldwin 				db_printf(", ");
8288b201c42SJohn Baldwin 			db_printf("NEED");
8298b201c42SJohn Baldwin 		}
8308b201c42SJohn Baldwin 		db_printf("}");
8318b201c42SJohn Baldwin 	}
8328b201c42SJohn Baldwin 	db_printf("\n");
8338b201c42SJohn Baldwin }
8348b201c42SJohn Baldwin 
8358b201c42SJohn Baldwin /*
836e0f66ef8SJohn Baldwin  * Dump details about a event.
8378b201c42SJohn Baldwin  */
8388b201c42SJohn Baldwin void
839e0f66ef8SJohn Baldwin db_dump_intr_event(struct intr_event *ie, int handlers)
8408b201c42SJohn Baldwin {
841e0f66ef8SJohn Baldwin 	struct intr_handler *ih;
842e0f66ef8SJohn Baldwin 	struct intr_thread *it;
8438b201c42SJohn Baldwin 	int comma;
8448b201c42SJohn Baldwin 
845e0f66ef8SJohn Baldwin 	db_printf("%s ", ie->ie_fullname);
846e0f66ef8SJohn Baldwin 	it = ie->ie_thread;
847e0f66ef8SJohn Baldwin 	if (it != NULL)
848e0f66ef8SJohn Baldwin 		db_printf("(pid %d)", it->it_thread->td_proc->p_pid);
849e0f66ef8SJohn Baldwin 	else
850e0f66ef8SJohn Baldwin 		db_printf("(no thread)");
851e0f66ef8SJohn Baldwin 	if ((ie->ie_flags & (IE_SOFT | IE_ENTROPY | IE_ADDING_THREAD)) != 0 ||
852e0f66ef8SJohn Baldwin 	    (it != NULL && it->it_need)) {
8538b201c42SJohn Baldwin 		db_printf(" {");
8548b201c42SJohn Baldwin 		comma = 0;
855e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_SOFT) {
8568b201c42SJohn Baldwin 			db_printf("SOFT");
8578b201c42SJohn Baldwin 			comma = 1;
8588b201c42SJohn Baldwin 		}
859e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_ENTROPY) {
8608b201c42SJohn Baldwin 			if (comma)
8618b201c42SJohn Baldwin 				db_printf(", ");
8628b201c42SJohn Baldwin 			db_printf("ENTROPY");
8638b201c42SJohn Baldwin 			comma = 1;
8648b201c42SJohn Baldwin 		}
865e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_ADDING_THREAD) {
8668b201c42SJohn Baldwin 			if (comma)
8678b201c42SJohn Baldwin 				db_printf(", ");
868e0f66ef8SJohn Baldwin 			db_printf("ADDING_THREAD");
8698b201c42SJohn Baldwin 			comma = 1;
8708b201c42SJohn Baldwin 		}
871e0f66ef8SJohn Baldwin 		if (it != NULL && it->it_need) {
8728b201c42SJohn Baldwin 			if (comma)
8738b201c42SJohn Baldwin 				db_printf(", ");
8748b201c42SJohn Baldwin 			db_printf("NEED");
8758b201c42SJohn Baldwin 		}
8768b201c42SJohn Baldwin 		db_printf("}");
8778b201c42SJohn Baldwin 	}
8788b201c42SJohn Baldwin 	db_printf("\n");
8798b201c42SJohn Baldwin 
8808b201c42SJohn Baldwin 	if (handlers)
881e0f66ef8SJohn Baldwin 		TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next)
8828b201c42SJohn Baldwin 		    db_dump_intrhand(ih);
8838b201c42SJohn Baldwin }
884e0f66ef8SJohn Baldwin 
885e0f66ef8SJohn Baldwin /*
886e0f66ef8SJohn Baldwin  * Dump data about interrupt handlers
887e0f66ef8SJohn Baldwin  */
888e0f66ef8SJohn Baldwin DB_SHOW_COMMAND(intr, db_show_intr)
889e0f66ef8SJohn Baldwin {
890e0f66ef8SJohn Baldwin 	struct intr_event *ie;
891e0f66ef8SJohn Baldwin 	int quit, all, verbose;
892e0f66ef8SJohn Baldwin 
893e0f66ef8SJohn Baldwin 	quit = 0;
894e0f66ef8SJohn Baldwin 	verbose = index(modif, 'v') != NULL;
895e0f66ef8SJohn Baldwin 	all = index(modif, 'a') != NULL;
896e0f66ef8SJohn Baldwin 	db_setup_paging(db_simple_pager, &quit, db_lines_per_page);
897e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ie, &event_list, ie_list) {
898e0f66ef8SJohn Baldwin 		if (!all && TAILQ_EMPTY(&ie->ie_handlers))
899e0f66ef8SJohn Baldwin 			continue;
900e0f66ef8SJohn Baldwin 		db_dump_intr_event(ie, verbose);
901e0f66ef8SJohn Baldwin 	}
902e0f66ef8SJohn Baldwin }
9038b201c42SJohn Baldwin #endif /* DDB */
9048b201c42SJohn Baldwin 
905b4151f71SJohn Baldwin /*
9068088699fSJohn Baldwin  * Start standard software interrupt threads
9071931cf94SJohn Baldwin  */
9081931cf94SJohn Baldwin static void
909b4151f71SJohn Baldwin start_softintr(void *dummy)
9101931cf94SJohn Baldwin {
9118804bf6bSJohn Baldwin 	struct proc *p;
912b4151f71SJohn Baldwin 
913e0f66ef8SJohn Baldwin 	if (swi_add(&clk_intr_event, "clock", softclock, NULL, SWI_CLOCK,
914b4151f71SJohn Baldwin 		INTR_MPSAFE, &softclock_ih) ||
91579501b66SScott Long 	    swi_add(NULL, "vm", swi_vm, NULL, SWI_VM, INTR_MPSAFE, &vm_ih))
916b4151f71SJohn Baldwin 		panic("died while creating standard software ithreads");
9173e5da754SJohn Baldwin 
918e0f66ef8SJohn Baldwin 	p = clk_intr_event->ie_thread->it_thread->td_proc;
9198804bf6bSJohn Baldwin 	PROC_LOCK(p);
9208804bf6bSJohn Baldwin 	p->p_flag |= P_NOLOAD;
9218804bf6bSJohn Baldwin 	PROC_UNLOCK(p);
9221931cf94SJohn Baldwin }
923b4151f71SJohn Baldwin SYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL)
9241931cf94SJohn Baldwin 
925d279178dSThomas Moestl /*
926d279178dSThomas Moestl  * Sysctls used by systat and others: hw.intrnames and hw.intrcnt.
927d279178dSThomas Moestl  * The data for this machine dependent, and the declarations are in machine
928d279178dSThomas Moestl  * dependent code.  The layout of intrnames and intrcnt however is machine
929d279178dSThomas Moestl  * independent.
930d279178dSThomas Moestl  *
931d279178dSThomas Moestl  * We do not know the length of intrcnt and intrnames at compile time, so
932d279178dSThomas Moestl  * calculate things at run time.
933d279178dSThomas Moestl  */
934d279178dSThomas Moestl static int
935d279178dSThomas Moestl sysctl_intrnames(SYSCTL_HANDLER_ARGS)
936d279178dSThomas Moestl {
937d279178dSThomas Moestl 	return (sysctl_handle_opaque(oidp, intrnames, eintrnames - intrnames,
938d279178dSThomas Moestl 	   req));
939d279178dSThomas Moestl }
940d279178dSThomas Moestl 
941d279178dSThomas Moestl SYSCTL_PROC(_hw, OID_AUTO, intrnames, CTLTYPE_OPAQUE | CTLFLAG_RD,
942d279178dSThomas Moestl     NULL, 0, sysctl_intrnames, "", "Interrupt Names");
943d279178dSThomas Moestl 
944d279178dSThomas Moestl static int
945d279178dSThomas Moestl sysctl_intrcnt(SYSCTL_HANDLER_ARGS)
946d279178dSThomas Moestl {
947d279178dSThomas Moestl 	return (sysctl_handle_opaque(oidp, intrcnt,
948d279178dSThomas Moestl 	    (char *)eintrcnt - (char *)intrcnt, req));
949d279178dSThomas Moestl }
950d279178dSThomas Moestl 
951d279178dSThomas Moestl SYSCTL_PROC(_hw, OID_AUTO, intrcnt, CTLTYPE_OPAQUE | CTLFLAG_RD,
952d279178dSThomas Moestl     NULL, 0, sysctl_intrcnt, "", "Interrupt Counts");
9538b201c42SJohn Baldwin 
9548b201c42SJohn Baldwin #ifdef DDB
9558b201c42SJohn Baldwin /*
9568b201c42SJohn Baldwin  * DDB command to dump the interrupt statistics.
9578b201c42SJohn Baldwin  */
9588b201c42SJohn Baldwin DB_SHOW_COMMAND(intrcnt, db_show_intrcnt)
9598b201c42SJohn Baldwin {
9608b201c42SJohn Baldwin 	u_long *i;
9618b201c42SJohn Baldwin 	char *cp;
9628b201c42SJohn Baldwin 	int quit;
9638b201c42SJohn Baldwin 
9648b201c42SJohn Baldwin 	cp = intrnames;
965d39d4a6eSJohn Baldwin 	db_setup_paging(db_simple_pager, &quit, db_lines_per_page);
9668b201c42SJohn Baldwin 	for (i = intrcnt, quit = 0; i != eintrcnt && !quit; i++) {
9678b201c42SJohn Baldwin 		if (*cp == '\0')
9688b201c42SJohn Baldwin 			break;
9698b201c42SJohn Baldwin 		if (*i != 0)
9708b201c42SJohn Baldwin 			db_printf("%s\t%lu\n", cp, *i);
9718b201c42SJohn Baldwin 		cp += strlen(cp) + 1;
9728b201c42SJohn Baldwin 	}
9738b201c42SJohn Baldwin }
9748b201c42SJohn Baldwin #endif
975