xref: /freebsd/sys/kern/kern_intr.c (revision 19e9205a23a412d9e48707fc012db23116ccf282)
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 static void	ithread_destroy(struct intr_thread *ithread);
97e0f66ef8SJohn Baldwin static void	ithread_execute_handlers(struct proc *p, struct intr_event *ie);
987b1fe905SBruce Evans static void	ithread_loop(void *);
99e0f66ef8SJohn Baldwin static void	ithread_update(struct intr_thread *ithd);
1007b1fe905SBruce Evans static void	start_softintr(void *);
1017870c3c6SJohn Baldwin 
102b4151f71SJohn Baldwin u_char
103e0f66ef8SJohn Baldwin intr_priority(enum intr_type flags)
1049a94c9c5SJohn Baldwin {
105b4151f71SJohn Baldwin 	u_char pri;
1069a94c9c5SJohn Baldwin 
107b4151f71SJohn Baldwin 	flags &= (INTR_TYPE_TTY | INTR_TYPE_BIO | INTR_TYPE_NET |
1085a280d9cSPeter Wemm 	    INTR_TYPE_CAM | INTR_TYPE_MISC | INTR_TYPE_CLK | INTR_TYPE_AV);
1099a94c9c5SJohn Baldwin 	switch (flags) {
110b4151f71SJohn Baldwin 	case INTR_TYPE_TTY:
1119a94c9c5SJohn Baldwin 		pri = PI_TTYLOW;
1129a94c9c5SJohn Baldwin 		break;
1139a94c9c5SJohn Baldwin 	case INTR_TYPE_BIO:
1149a94c9c5SJohn Baldwin 		/*
1159a94c9c5SJohn Baldwin 		 * XXX We need to refine this.  BSD/OS distinguishes
1169a94c9c5SJohn Baldwin 		 * between tape and disk priorities.
1179a94c9c5SJohn Baldwin 		 */
1189a94c9c5SJohn Baldwin 		pri = PI_DISK;
1199a94c9c5SJohn Baldwin 		break;
1209a94c9c5SJohn Baldwin 	case INTR_TYPE_NET:
1219a94c9c5SJohn Baldwin 		pri = PI_NET;
1229a94c9c5SJohn Baldwin 		break;
1239a94c9c5SJohn Baldwin 	case INTR_TYPE_CAM:
1249a94c9c5SJohn Baldwin 		pri = PI_DISK;          /* XXX or PI_CAM? */
1259a94c9c5SJohn Baldwin 		break;
1265a280d9cSPeter Wemm 	case INTR_TYPE_AV:		/* Audio/video */
1275a280d9cSPeter Wemm 		pri = PI_AV;
1285a280d9cSPeter Wemm 		break;
129b4151f71SJohn Baldwin 	case INTR_TYPE_CLK:
130b4151f71SJohn Baldwin 		pri = PI_REALTIME;
131b4151f71SJohn Baldwin 		break;
1329a94c9c5SJohn Baldwin 	case INTR_TYPE_MISC:
1339a94c9c5SJohn Baldwin 		pri = PI_DULL;          /* don't care */
1349a94c9c5SJohn Baldwin 		break;
1359a94c9c5SJohn Baldwin 	default:
136b4151f71SJohn Baldwin 		/* We didn't specify an interrupt level. */
137e0f66ef8SJohn Baldwin 		panic("intr_priority: no interrupt type in flags");
1389a94c9c5SJohn Baldwin 	}
1399a94c9c5SJohn Baldwin 
1409a94c9c5SJohn Baldwin 	return pri;
1419a94c9c5SJohn Baldwin }
1429a94c9c5SJohn Baldwin 
143b4151f71SJohn Baldwin /*
144e0f66ef8SJohn Baldwin  * Update an ithread based on the associated intr_event.
145b4151f71SJohn Baldwin  */
146b4151f71SJohn Baldwin static void
147e0f66ef8SJohn Baldwin ithread_update(struct intr_thread *ithd)
148b4151f71SJohn Baldwin {
149e0f66ef8SJohn Baldwin 	struct intr_event *ie;
150b40ce416SJulian Elischer 	struct thread *td;
151e0f66ef8SJohn Baldwin 	u_char pri;
1528088699fSJohn Baldwin 
153e0f66ef8SJohn Baldwin 	ie = ithd->it_event;
154e0f66ef8SJohn Baldwin 	td = ithd->it_thread;
155b4151f71SJohn Baldwin 
156e0f66ef8SJohn Baldwin 	/* Determine the overall priority of this event. */
157e0f66ef8SJohn Baldwin 	if (TAILQ_EMPTY(&ie->ie_handlers))
158e0f66ef8SJohn Baldwin 		pri = PRI_MAX_ITHD;
159e0f66ef8SJohn Baldwin 	else
160e0f66ef8SJohn Baldwin 		pri = TAILQ_FIRST(&ie->ie_handlers)->ih_pri;
161e80fb434SRobert Drehmel 
162e0f66ef8SJohn Baldwin 	/* Update name and priority. */
163e0f66ef8SJohn Baldwin 	strlcpy(td->td_proc->p_comm, ie->ie_fullname,
164e0f66ef8SJohn Baldwin 	    sizeof(td->td_proc->p_comm));
165b106d2f5SJohn Baldwin 	mtx_lock_spin(&sched_lock);
166e0f66ef8SJohn Baldwin 	sched_prio(td, pri);
167b106d2f5SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
168b4151f71SJohn Baldwin }
169e0f66ef8SJohn Baldwin 
170e0f66ef8SJohn Baldwin /*
171e0f66ef8SJohn Baldwin  * Regenerate the full name of an interrupt event and update its priority.
172e0f66ef8SJohn Baldwin  */
173e0f66ef8SJohn Baldwin static void
174e0f66ef8SJohn Baldwin intr_event_update(struct intr_event *ie)
175e0f66ef8SJohn Baldwin {
176e0f66ef8SJohn Baldwin 	struct intr_handler *ih;
177e0f66ef8SJohn Baldwin 	char *last;
178e0f66ef8SJohn Baldwin 	int missed, space;
179e0f66ef8SJohn Baldwin 
180e0f66ef8SJohn Baldwin 	/* Start off with no entropy and just the name of the event. */
181e0f66ef8SJohn Baldwin 	mtx_assert(&ie->ie_lock, MA_OWNED);
182e0f66ef8SJohn Baldwin 	strlcpy(ie->ie_fullname, ie->ie_name, sizeof(ie->ie_fullname));
183e0f66ef8SJohn Baldwin 	ie->ie_flags &= ~IE_ENTROPY;
1840811d60aSJohn Baldwin 	missed = 0;
185e0f66ef8SJohn Baldwin 	space = 1;
186e0f66ef8SJohn Baldwin 
187e0f66ef8SJohn Baldwin 	/* Run through all the handlers updating values. */
188e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
189e0f66ef8SJohn Baldwin 		if (strlen(ie->ie_fullname) + strlen(ih->ih_name) + 1 <
190e0f66ef8SJohn Baldwin 		    sizeof(ie->ie_fullname)) {
191e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, " ");
192e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, ih->ih_name);
193e0f66ef8SJohn Baldwin 			space = 0;
1940811d60aSJohn Baldwin 		} else
1950811d60aSJohn Baldwin 			missed++;
1960811d60aSJohn Baldwin 		if (ih->ih_flags & IH_ENTROPY)
197e0f66ef8SJohn Baldwin 			ie->ie_flags |= IE_ENTROPY;
1980811d60aSJohn Baldwin 	}
199e0f66ef8SJohn Baldwin 
200e0f66ef8SJohn Baldwin 	/*
201e0f66ef8SJohn Baldwin 	 * If the handler names were too long, add +'s to indicate missing
202e0f66ef8SJohn Baldwin 	 * names. If we run out of room and still have +'s to add, change
203e0f66ef8SJohn Baldwin 	 * the last character from a + to a *.
204e0f66ef8SJohn Baldwin 	 */
205e0f66ef8SJohn Baldwin 	last = &ie->ie_fullname[sizeof(ie->ie_fullname) - 2];
2060811d60aSJohn Baldwin 	while (missed-- > 0) {
207e0f66ef8SJohn Baldwin 		if (strlen(ie->ie_fullname) + 1 == sizeof(ie->ie_fullname)) {
208e0f66ef8SJohn Baldwin 			if (*last == '+') {
209e0f66ef8SJohn Baldwin 				*last = '*';
210e0f66ef8SJohn Baldwin 				break;
211b4151f71SJohn Baldwin 			} else
212e0f66ef8SJohn Baldwin 				*last = '+';
213e0f66ef8SJohn Baldwin 		} else if (space) {
214e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, " +");
215e0f66ef8SJohn Baldwin 			space = 0;
216e0f66ef8SJohn Baldwin 		} else
217e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, "+");
218b4151f71SJohn Baldwin 	}
219e0f66ef8SJohn Baldwin 
220e0f66ef8SJohn Baldwin 	/*
221e0f66ef8SJohn Baldwin 	 * If this event has an ithread, update it's priority and
222e0f66ef8SJohn Baldwin 	 * name.
223e0f66ef8SJohn Baldwin 	 */
224e0f66ef8SJohn Baldwin 	if (ie->ie_thread != NULL)
225e0f66ef8SJohn Baldwin 		ithread_update(ie->ie_thread);
226e0f66ef8SJohn Baldwin 	CTR2(KTR_INTR, "%s: updated %s", __func__, ie->ie_fullname);
227b4151f71SJohn Baldwin }
228b4151f71SJohn Baldwin 
229b4151f71SJohn Baldwin int
230e0f66ef8SJohn Baldwin intr_event_create(struct intr_event **event, void *source, int flags,
231e0f66ef8SJohn Baldwin     void (*enable)(void *), const char *fmt, ...)
232b4151f71SJohn Baldwin {
233e0f66ef8SJohn Baldwin 	struct intr_event *ie;
234b4151f71SJohn Baldwin 	va_list ap;
235b4151f71SJohn Baldwin 
236e0f66ef8SJohn Baldwin 	/* The only valid flag during creation is IE_SOFT. */
237e0f66ef8SJohn Baldwin 	if ((flags & ~IE_SOFT) != 0)
2383e5da754SJohn Baldwin 		return (EINVAL);
239e0f66ef8SJohn Baldwin 	ie = malloc(sizeof(struct intr_event), M_ITHREAD, M_WAITOK | M_ZERO);
240e0f66ef8SJohn Baldwin 	ie->ie_source = source;
241e0f66ef8SJohn Baldwin 	ie->ie_enable = enable;
242e0f66ef8SJohn Baldwin 	ie->ie_flags = flags;
243e0f66ef8SJohn Baldwin 	TAILQ_INIT(&ie->ie_handlers);
244e0f66ef8SJohn Baldwin 	mtx_init(&ie->ie_lock, "intr event", NULL, MTX_DEF);
245b4151f71SJohn Baldwin 
246b4151f71SJohn Baldwin 	va_start(ap, fmt);
247e0f66ef8SJohn Baldwin 	vsnprintf(ie->ie_name, sizeof(ie->ie_name), fmt, ap);
248b4151f71SJohn Baldwin 	va_end(ap);
249e0f66ef8SJohn Baldwin 	strlcpy(ie->ie_fullname, ie->ie_name, sizeof(ie->ie_fullname));
250e0f66ef8SJohn Baldwin 	mtx_pool_lock(mtxpool_sleep, &event_list);
251e0f66ef8SJohn Baldwin 	TAILQ_INSERT_TAIL(&event_list, ie, ie_list);
252e0f66ef8SJohn Baldwin 	mtx_pool_unlock(mtxpool_sleep, &event_list);
253e0f66ef8SJohn Baldwin 	if (event != NULL)
254e0f66ef8SJohn Baldwin 		*event = ie;
255e0f66ef8SJohn Baldwin 	CTR2(KTR_INTR, "%s: created %s", __func__, ie->ie_name);
256b4151f71SJohn Baldwin 	return (0);
257b4151f71SJohn Baldwin }
258b4151f71SJohn Baldwin 
259b4151f71SJohn Baldwin int
260e0f66ef8SJohn Baldwin intr_event_destroy(struct intr_event *ie)
261b4151f71SJohn Baldwin {
262b4151f71SJohn Baldwin 
263e0f66ef8SJohn Baldwin 	mtx_lock(&ie->ie_lock);
264e0f66ef8SJohn Baldwin 	if (!TAILQ_EMPTY(&ie->ie_handlers)) {
265e0f66ef8SJohn Baldwin 		mtx_unlock(&ie->ie_lock);
266e0f66ef8SJohn Baldwin 		return (EBUSY);
2674d29cb2dSJohn Baldwin 	}
268e0f66ef8SJohn Baldwin 	mtx_pool_lock(mtxpool_sleep, &event_list);
269e0f66ef8SJohn Baldwin 	TAILQ_REMOVE(&event_list, ie, ie_list);
270e0f66ef8SJohn Baldwin 	mtx_pool_unlock(mtxpool_sleep, &event_list);
2719477358dSJohn Baldwin #ifndef notyet
2729477358dSJohn Baldwin 	if (ie->ie_thread != NULL) {
2739477358dSJohn Baldwin 		ithread_destroy(ie->ie_thread);
2749477358dSJohn Baldwin 		ie->ie_thread = NULL;
2759477358dSJohn Baldwin 	}
2769477358dSJohn Baldwin #endif
277e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
278e0f66ef8SJohn Baldwin 	mtx_destroy(&ie->ie_lock);
279e0f66ef8SJohn Baldwin 	free(ie, M_ITHREAD);
280e0f66ef8SJohn Baldwin 	return (0);
281e0f66ef8SJohn Baldwin }
282e0f66ef8SJohn Baldwin 
283e0f66ef8SJohn Baldwin static struct intr_thread *
284e0f66ef8SJohn Baldwin ithread_create(const char *name)
285e0f66ef8SJohn Baldwin {
286e0f66ef8SJohn Baldwin 	struct intr_thread *ithd;
287e0f66ef8SJohn Baldwin 	struct thread *td;
288e0f66ef8SJohn Baldwin 	struct proc *p;
289e0f66ef8SJohn Baldwin 	int error;
290e0f66ef8SJohn Baldwin 
291e0f66ef8SJohn Baldwin 	ithd = malloc(sizeof(struct intr_thread), M_ITHREAD, M_WAITOK | M_ZERO);
292e0f66ef8SJohn Baldwin 
293e0f66ef8SJohn Baldwin 	error = kthread_create(ithread_loop, ithd, &p, RFSTOPPED | RFHIGHPID,
294e0f66ef8SJohn Baldwin 	    0, "%s", name);
295e0f66ef8SJohn Baldwin 	if (error)
296e0f66ef8SJohn Baldwin 		panic("kthread_create() failed with %d", error);
297e0f66ef8SJohn Baldwin 	td = FIRST_THREAD_IN_PROC(p);	/* XXXKSE */
2984d29cb2dSJohn Baldwin 	mtx_lock_spin(&sched_lock);
299e0f66ef8SJohn Baldwin 	td->td_ksegrp->kg_pri_class = PRI_ITHD;
300e0f66ef8SJohn Baldwin 	TD_SET_IWAIT(td);
301e0f66ef8SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
302e0f66ef8SJohn Baldwin 	td->td_pflags |= TDP_ITHREAD;
303e0f66ef8SJohn Baldwin 	ithd->it_thread = td;
304e0f66ef8SJohn Baldwin 	CTR2(KTR_INTR, "%s: created %s", __func__, name);
305e0f66ef8SJohn Baldwin 	return (ithd);
306e0f66ef8SJohn Baldwin }
307e0f66ef8SJohn Baldwin 
308e0f66ef8SJohn Baldwin static void
309e0f66ef8SJohn Baldwin ithread_destroy(struct intr_thread *ithread)
310e0f66ef8SJohn Baldwin {
311e0f66ef8SJohn Baldwin 	struct thread *td;
312e0f66ef8SJohn Baldwin 
313bb141be1SScott Long 	CTR2(KTR_INTR, "%s: killing %s", __func__, ithread->it_event->ie_name);
314e0f66ef8SJohn Baldwin 	td = ithread->it_thread;
315e0f66ef8SJohn Baldwin 	mtx_lock_spin(&sched_lock);
316e0f66ef8SJohn Baldwin 	ithread->it_flags |= IT_DEAD;
31771fad9fdSJulian Elischer 	if (TD_AWAITING_INTR(td)) {
31871fad9fdSJulian Elischer 		TD_CLR_IWAIT(td);
3192630e4c9SJulian Elischer 		setrunqueue(td, SRQ_INTR);
320b4151f71SJohn Baldwin 	}
321b4151f71SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
322b4151f71SJohn Baldwin }
323b4151f71SJohn Baldwin 
324b4151f71SJohn Baldwin int
325e0f66ef8SJohn Baldwin intr_event_add_handler(struct intr_event *ie, const char *name,
326b4151f71SJohn Baldwin     driver_intr_t handler, void *arg, u_char pri, enum intr_type flags,
327b4151f71SJohn Baldwin     void **cookiep)
328b4151f71SJohn Baldwin {
329e0f66ef8SJohn Baldwin 	struct intr_handler *ih, *temp_ih;
330e0f66ef8SJohn Baldwin 	struct intr_thread *it;
331b4151f71SJohn Baldwin 
332e0f66ef8SJohn Baldwin 	if (ie == NULL || name == NULL || handler == NULL)
333b4151f71SJohn Baldwin 		return (EINVAL);
334b4151f71SJohn Baldwin 
335e0f66ef8SJohn Baldwin 	/* Allocate and populate an interrupt handler structure. */
336e0f66ef8SJohn Baldwin 	ih = malloc(sizeof(struct intr_handler), M_ITHREAD, M_WAITOK | M_ZERO);
337b4151f71SJohn Baldwin 	ih->ih_handler = handler;
338b4151f71SJohn Baldwin 	ih->ih_argument = arg;
339b4151f71SJohn Baldwin 	ih->ih_name = name;
340e0f66ef8SJohn Baldwin 	ih->ih_event = ie;
341b4151f71SJohn Baldwin 	ih->ih_pri = pri;
342b4151f71SJohn Baldwin 	if (flags & INTR_FAST)
3438bc08464SJohn Baldwin 		ih->ih_flags = IH_FAST;
344b4151f71SJohn Baldwin 	else if (flags & INTR_EXCL)
345b4151f71SJohn Baldwin 		ih->ih_flags = IH_EXCLUSIVE;
346b4151f71SJohn Baldwin 	if (flags & INTR_MPSAFE)
347b4151f71SJohn Baldwin 		ih->ih_flags |= IH_MPSAFE;
348b4151f71SJohn Baldwin 	if (flags & INTR_ENTROPY)
349b4151f71SJohn Baldwin 		ih->ih_flags |= IH_ENTROPY;
350b4151f71SJohn Baldwin 
351e0f66ef8SJohn Baldwin 	/* We can only have one exclusive handler in a event. */
352e0f66ef8SJohn Baldwin 	mtx_lock(&ie->ie_lock);
353e0f66ef8SJohn Baldwin 	if (!TAILQ_EMPTY(&ie->ie_handlers)) {
354e0f66ef8SJohn Baldwin 		if ((flags & INTR_EXCL) ||
355e0f66ef8SJohn Baldwin 		    (TAILQ_FIRST(&ie->ie_handlers)->ih_flags & IH_EXCLUSIVE)) {
356e0f66ef8SJohn Baldwin 			mtx_unlock(&ie->ie_lock);
357b4151f71SJohn Baldwin 			free(ih, M_ITHREAD);
358b4151f71SJohn Baldwin 			return (EINVAL);
359b4151f71SJohn Baldwin 		}
360e0f66ef8SJohn Baldwin 	}
361e0f66ef8SJohn Baldwin 
362e0f66ef8SJohn Baldwin 	/* Add the new handler to the event in priority order. */
363e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(temp_ih, &ie->ie_handlers, ih_next) {
364e0f66ef8SJohn Baldwin 		if (temp_ih->ih_pri > ih->ih_pri)
365e0f66ef8SJohn Baldwin 			break;
366e0f66ef8SJohn Baldwin 	}
367e0f66ef8SJohn Baldwin 	if (temp_ih == NULL)
368e0f66ef8SJohn Baldwin 		TAILQ_INSERT_TAIL(&ie->ie_handlers, ih, ih_next);
369e0f66ef8SJohn Baldwin 	else
370e0f66ef8SJohn Baldwin 		TAILQ_INSERT_BEFORE(temp_ih, ih, ih_next);
371e0f66ef8SJohn Baldwin 	intr_event_update(ie);
372e0f66ef8SJohn Baldwin 
373e0f66ef8SJohn Baldwin 	/* Create a thread if we need one. */
374e0f66ef8SJohn Baldwin 	while (ie->ie_thread == NULL && !(flags & INTR_FAST)) {
375e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_ADDING_THREAD)
3760f180a7cSJohn Baldwin 			msleep(ie, &ie->ie_lock, 0, "ithread", 0);
377e0f66ef8SJohn Baldwin 		else {
378e0f66ef8SJohn Baldwin 			ie->ie_flags |= IE_ADDING_THREAD;
379e0f66ef8SJohn Baldwin 			mtx_unlock(&ie->ie_lock);
380e0f66ef8SJohn Baldwin 			it = ithread_create("intr: newborn");
381e0f66ef8SJohn Baldwin 			mtx_lock(&ie->ie_lock);
382e0f66ef8SJohn Baldwin 			ie->ie_flags &= ~IE_ADDING_THREAD;
383e0f66ef8SJohn Baldwin 			ie->ie_thread = it;
384e0f66ef8SJohn Baldwin 			it->it_event = ie;
385e0f66ef8SJohn Baldwin 			ithread_update(it);
386e0f66ef8SJohn Baldwin 			wakeup(ie);
387e0f66ef8SJohn Baldwin 		}
388e0f66ef8SJohn Baldwin 	}
389e0f66ef8SJohn Baldwin 	CTR3(KTR_INTR, "%s: added %s to %s", __func__, ih->ih_name,
390e0f66ef8SJohn Baldwin 	    ie->ie_name);
391e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
392e0f66ef8SJohn Baldwin 
393e0f66ef8SJohn Baldwin 	if (cookiep != NULL)
394e0f66ef8SJohn Baldwin 		*cookiep = ih;
395e0f66ef8SJohn Baldwin 	return (0);
396e0f66ef8SJohn Baldwin }
397b4151f71SJohn Baldwin 
398b4151f71SJohn Baldwin int
399e0f66ef8SJohn Baldwin intr_event_remove_handler(void *cookie)
400b4151f71SJohn Baldwin {
401e0f66ef8SJohn Baldwin 	struct intr_handler *handler = (struct intr_handler *)cookie;
402e0f66ef8SJohn Baldwin 	struct intr_event *ie;
403b4151f71SJohn Baldwin #ifdef INVARIANTS
404e0f66ef8SJohn Baldwin 	struct intr_handler *ih;
405e0f66ef8SJohn Baldwin #endif
406e0f66ef8SJohn Baldwin #ifdef notyet
407e0f66ef8SJohn Baldwin 	int dead;
408b4151f71SJohn Baldwin #endif
409b4151f71SJohn Baldwin 
4103e5da754SJohn Baldwin 	if (handler == NULL)
411b4151f71SJohn Baldwin 		return (EINVAL);
412e0f66ef8SJohn Baldwin 	ie = handler->ih_event;
413e0f66ef8SJohn Baldwin 	KASSERT(ie != NULL,
414e0f66ef8SJohn Baldwin 	    ("interrupt handler \"%s\" has a NULL interrupt event",
4153e5da754SJohn Baldwin 		handler->ih_name));
416e0f66ef8SJohn Baldwin 	mtx_lock(&ie->ie_lock);
41791f91617SDavid E. O'Brien 	CTR3(KTR_INTR, "%s: removing %s from %s", __func__, handler->ih_name,
418e0f66ef8SJohn Baldwin 	    ie->ie_name);
419b4151f71SJohn Baldwin #ifdef INVARIANTS
420e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next)
4213e5da754SJohn Baldwin 		if (ih == handler)
4223e5da754SJohn Baldwin 			goto ok;
423e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
424e0f66ef8SJohn Baldwin 	panic("interrupt handler \"%s\" not found in interrupt event \"%s\"",
425e0f66ef8SJohn Baldwin 	    ih->ih_name, ie->ie_name);
4263e5da754SJohn Baldwin ok:
427b4151f71SJohn Baldwin #endif
428de271f01SJohn Baldwin 	/*
429e0f66ef8SJohn Baldwin 	 * If there is no ithread, then just remove the handler and return.
430e0f66ef8SJohn Baldwin 	 * XXX: Note that an INTR_FAST handler might be running on another
431e0f66ef8SJohn Baldwin 	 * CPU!
432e0f66ef8SJohn Baldwin 	 */
433e0f66ef8SJohn Baldwin 	if (ie->ie_thread == NULL) {
434e0f66ef8SJohn Baldwin 		TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next);
435e0f66ef8SJohn Baldwin 		mtx_unlock(&ie->ie_lock);
436e0f66ef8SJohn Baldwin 		free(handler, M_ITHREAD);
437e0f66ef8SJohn Baldwin 		return (0);
438e0f66ef8SJohn Baldwin 	}
439e0f66ef8SJohn Baldwin 
440e0f66ef8SJohn Baldwin 	/*
441de271f01SJohn Baldwin 	 * If the interrupt thread is already running, then just mark this
442de271f01SJohn Baldwin 	 * handler as being dead and let the ithread do the actual removal.
443288e351bSDon Lewis 	 *
444288e351bSDon Lewis 	 * During a cold boot while cold is set, msleep() does not sleep,
445288e351bSDon Lewis 	 * so we have to remove the handler here rather than letting the
446288e351bSDon Lewis 	 * thread do it.
447de271f01SJohn Baldwin 	 */
448de271f01SJohn Baldwin 	mtx_lock_spin(&sched_lock);
449e0f66ef8SJohn Baldwin 	if (!TD_AWAITING_INTR(ie->ie_thread->it_thread) && !cold) {
450de271f01SJohn Baldwin 		handler->ih_flags |= IH_DEAD;
451de271f01SJohn Baldwin 
452de271f01SJohn Baldwin 		/*
453de271f01SJohn Baldwin 		 * Ensure that the thread will process the handler list
454de271f01SJohn Baldwin 		 * again and remove this handler if it has already passed
455de271f01SJohn Baldwin 		 * it on the list.
456de271f01SJohn Baldwin 		 */
457e0f66ef8SJohn Baldwin 		ie->ie_thread->it_need = 1;
4584d29cb2dSJohn Baldwin 	} else
459e0f66ef8SJohn Baldwin 		TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next);
460de271f01SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
461e0f66ef8SJohn Baldwin 	while (handler->ih_flags & IH_DEAD)
4620f180a7cSJohn Baldwin 		msleep(handler, &ie->ie_lock, 0, "iev_rmh", 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 
544fe486a37SJohn Baldwin /*
545fe486a37SJohn Baldwin  * Add a software interrupt handler to a specified event.  If a given event
546fe486a37SJohn Baldwin  * is not specified, then a new event is created.
547fe486a37SJohn Baldwin  */
5483e5da754SJohn Baldwin int
549e0f66ef8SJohn Baldwin swi_add(struct intr_event **eventp, const char *name, driver_intr_t handler,
550b4151f71SJohn Baldwin 	    void *arg, int pri, enum intr_type flags, void **cookiep)
5518088699fSJohn Baldwin {
552e0f66ef8SJohn Baldwin 	struct intr_event *ie;
553b4151f71SJohn Baldwin 	int error;
5548088699fSJohn Baldwin 
5553e5da754SJohn Baldwin 	if (flags & (INTR_FAST | INTR_ENTROPY))
5563e5da754SJohn Baldwin 		return (EINVAL);
5573e5da754SJohn Baldwin 
558e0f66ef8SJohn Baldwin 	ie = (eventp != NULL) ? *eventp : NULL;
5598088699fSJohn Baldwin 
560e0f66ef8SJohn Baldwin 	if (ie != NULL) {
561e0f66ef8SJohn Baldwin 		if (!(ie->ie_flags & IE_SOFT))
5623e5da754SJohn Baldwin 			return (EINVAL);
5633e5da754SJohn Baldwin 	} else {
564e0f66ef8SJohn Baldwin 		error = intr_event_create(&ie, NULL, IE_SOFT, NULL,
565b4151f71SJohn Baldwin 		    "swi%d:", pri);
5668088699fSJohn Baldwin 		if (error)
567b4151f71SJohn Baldwin 			return (error);
568e0f66ef8SJohn Baldwin 		if (eventp != NULL)
569e0f66ef8SJohn Baldwin 			*eventp = ie;
5708088699fSJohn Baldwin 	}
571e0f66ef8SJohn Baldwin 	return (intr_event_add_handler(ie, name, handler, arg,
572d5a08a60SJake Burkholder 		    (pri * RQ_PPQ) + PI_SOFT, flags, cookiep));
573ed062c8dSJulian Elischer 		    /* XXKSE.. think of a better way to get separate queues */
5748088699fSJohn Baldwin }
5758088699fSJohn Baldwin 
5761931cf94SJohn Baldwin /*
577e0f66ef8SJohn Baldwin  * Schedule a software interrupt thread.
5781931cf94SJohn Baldwin  */
5791931cf94SJohn Baldwin void
580b4151f71SJohn Baldwin swi_sched(void *cookie, int flags)
5811931cf94SJohn Baldwin {
582e0f66ef8SJohn Baldwin 	struct intr_handler *ih = (struct intr_handler *)cookie;
583e0f66ef8SJohn Baldwin 	struct intr_event *ie = ih->ih_event;
5843e5da754SJohn Baldwin 	int error;
5858088699fSJohn Baldwin 
586aa9aa68dSJohn Baldwin 	PCPU_LAZY_INC(cnt.v_intr);
5871931cf94SJohn Baldwin 
588e0f66ef8SJohn Baldwin 	CTR3(KTR_INTR, "swi_sched: %s %s need=%d", ie->ie_name, ih->ih_name,
589e0f66ef8SJohn Baldwin 	    ih->ih_need);
5901931cf94SJohn Baldwin 
5911931cf94SJohn Baldwin 	/*
5923e5da754SJohn Baldwin 	 * Set ih_need for this handler so that if the ithread is already
5933e5da754SJohn Baldwin 	 * running it will execute this handler on the next pass.  Otherwise,
5943e5da754SJohn Baldwin 	 * it will execute it the next time it runs.
5951931cf94SJohn Baldwin 	 */
596b4151f71SJohn Baldwin 	atomic_store_rel_int(&ih->ih_need, 1);
597b4151f71SJohn Baldwin 	if (!(flags & SWI_DELAY)) {
598e0f66ef8SJohn Baldwin 		error = intr_event_schedule_thread(ie);
5993e5da754SJohn Baldwin 		KASSERT(error == 0, ("stray software interrupt"));
6008088699fSJohn Baldwin 	}
6018088699fSJohn Baldwin }
6028088699fSJohn Baldwin 
603fe486a37SJohn Baldwin /*
604fe486a37SJohn Baldwin  * Remove a software interrupt handler.  Currently this code does not
605fe486a37SJohn Baldwin  * remove the associated interrupt event if it becomes empty.  Calling code
606fe486a37SJohn Baldwin  * may do so manually via intr_event_destroy(), but that's not really
607fe486a37SJohn Baldwin  * an optimal interface.
608fe486a37SJohn Baldwin  */
609fe486a37SJohn Baldwin int
610fe486a37SJohn Baldwin swi_remove(void *cookie)
611fe486a37SJohn Baldwin {
612fe486a37SJohn Baldwin 
613fe486a37SJohn Baldwin 	return (intr_event_remove_handler(cookie));
614fe486a37SJohn Baldwin }
615fe486a37SJohn Baldwin 
616e0f66ef8SJohn Baldwin static void
617e0f66ef8SJohn Baldwin ithread_execute_handlers(struct proc *p, struct intr_event *ie)
618e0f66ef8SJohn Baldwin {
619e0f66ef8SJohn Baldwin 	struct intr_handler *ih, *ihn;
620e0f66ef8SJohn Baldwin 
621e0f66ef8SJohn Baldwin 	/* Interrupt handlers should not sleep. */
622e0f66ef8SJohn Baldwin 	if (!(ie->ie_flags & IE_SOFT))
623e0f66ef8SJohn Baldwin 		THREAD_NO_SLEEPING();
624e0f66ef8SJohn Baldwin 	TAILQ_FOREACH_SAFE(ih, &ie->ie_handlers, ih_next, ihn) {
625e0f66ef8SJohn Baldwin 
626e0f66ef8SJohn Baldwin 		/*
627e0f66ef8SJohn Baldwin 		 * If this handler is marked for death, remove it from
628e0f66ef8SJohn Baldwin 		 * the list of handlers and wake up the sleeper.
629e0f66ef8SJohn Baldwin 		 */
630e0f66ef8SJohn Baldwin 		if (ih->ih_flags & IH_DEAD) {
631e0f66ef8SJohn Baldwin 			mtx_lock(&ie->ie_lock);
632e0f66ef8SJohn Baldwin 			TAILQ_REMOVE(&ie->ie_handlers, ih, ih_next);
633e0f66ef8SJohn Baldwin 			ih->ih_flags &= ~IH_DEAD;
634e0f66ef8SJohn Baldwin 			wakeup(ih);
635e0f66ef8SJohn Baldwin 			mtx_unlock(&ie->ie_lock);
636e0f66ef8SJohn Baldwin 			continue;
637e0f66ef8SJohn Baldwin 		}
638e0f66ef8SJohn Baldwin 
639e0f66ef8SJohn Baldwin 		/*
640e0f66ef8SJohn Baldwin 		 * For software interrupt threads, we only execute
641e0f66ef8SJohn Baldwin 		 * handlers that have their need flag set.  Hardware
642e0f66ef8SJohn Baldwin 		 * interrupt threads always invoke all of their handlers.
643e0f66ef8SJohn Baldwin 		 */
644e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_SOFT) {
645e0f66ef8SJohn Baldwin 			if (!ih->ih_need)
646e0f66ef8SJohn Baldwin 				continue;
647e0f66ef8SJohn Baldwin 			else
648e0f66ef8SJohn Baldwin 				atomic_store_rel_int(&ih->ih_need, 0);
649e0f66ef8SJohn Baldwin 		}
650e0f66ef8SJohn Baldwin 
651e0f66ef8SJohn Baldwin 		/* Fast handlers are handled in primary interrupt context. */
652e0f66ef8SJohn Baldwin 		if (ih->ih_flags & IH_FAST)
653e0f66ef8SJohn Baldwin 			continue;
654e0f66ef8SJohn Baldwin 
655e0f66ef8SJohn Baldwin 		/* Execute this handler. */
656e0f66ef8SJohn Baldwin 		CTR6(KTR_INTR, "%s: pid %d exec %p(%p) for %s flg=%x",
657e0f66ef8SJohn Baldwin 		    __func__, p->p_pid, (void *)ih->ih_handler, ih->ih_argument,
658e0f66ef8SJohn Baldwin 		    ih->ih_name, ih->ih_flags);
659e0f66ef8SJohn Baldwin 
660e0f66ef8SJohn Baldwin 		if (!(ih->ih_flags & IH_MPSAFE))
661e0f66ef8SJohn Baldwin 			mtx_lock(&Giant);
662e0f66ef8SJohn Baldwin 		ih->ih_handler(ih->ih_argument);
663e0f66ef8SJohn Baldwin 		if (!(ih->ih_flags & IH_MPSAFE))
664e0f66ef8SJohn Baldwin 			mtx_unlock(&Giant);
665e0f66ef8SJohn Baldwin 	}
666e0f66ef8SJohn Baldwin 	if (!(ie->ie_flags & IE_SOFT))
667e0f66ef8SJohn Baldwin 		THREAD_SLEEPING_OK();
668e0f66ef8SJohn Baldwin 
669e0f66ef8SJohn Baldwin 	/*
670e0f66ef8SJohn Baldwin 	 * Interrupt storm handling:
671e0f66ef8SJohn Baldwin 	 *
672e0f66ef8SJohn Baldwin 	 * If this interrupt source is currently storming, then throttle
673e0f66ef8SJohn Baldwin 	 * it to only fire the handler once  per clock tick.
674e0f66ef8SJohn Baldwin 	 *
675e0f66ef8SJohn Baldwin 	 * If this interrupt source is not currently storming, but the
676e0f66ef8SJohn Baldwin 	 * number of back to back interrupts exceeds the storm threshold,
677e0f66ef8SJohn Baldwin 	 * then enter storming mode.
678e0f66ef8SJohn Baldwin 	 */
679e0f66ef8SJohn Baldwin 	if (intr_storm_threshold != 0 && ie->ie_count >= intr_storm_threshold) {
680e0f66ef8SJohn Baldwin 		if (ie->ie_warned == 0) {
681e0f66ef8SJohn Baldwin 			printf(
682e0f66ef8SJohn Baldwin 	"Interrupt storm detected on \"%s\"; throttling interrupt source\n",
683e0f66ef8SJohn Baldwin 			    ie->ie_name);
684e0f66ef8SJohn Baldwin 			ie->ie_warned = 1;
685e0f66ef8SJohn Baldwin 		}
6860f180a7cSJohn Baldwin 		tsleep(&ie->ie_count, 0, "istorm", 1);
687e0f66ef8SJohn Baldwin 	} else
688e0f66ef8SJohn Baldwin 		ie->ie_count++;
689e0f66ef8SJohn Baldwin 
690e0f66ef8SJohn Baldwin 	/*
691e0f66ef8SJohn Baldwin 	 * Now that all the handlers have had a chance to run, reenable
692e0f66ef8SJohn Baldwin 	 * the interrupt source.
693e0f66ef8SJohn Baldwin 	 */
694e0f66ef8SJohn Baldwin 	if (ie->ie_enable != NULL)
695e0f66ef8SJohn Baldwin 		ie->ie_enable(ie->ie_source);
696e0f66ef8SJohn Baldwin }
697e0f66ef8SJohn Baldwin 
6988088699fSJohn Baldwin /*
699b4151f71SJohn Baldwin  * This is the main code for interrupt threads.
7008088699fSJohn Baldwin  */
70137c84183SPoul-Henning Kamp static void
702b4151f71SJohn Baldwin ithread_loop(void *arg)
7038088699fSJohn Baldwin {
704e0f66ef8SJohn Baldwin 	struct intr_thread *ithd;
705e0f66ef8SJohn Baldwin 	struct intr_event *ie;
706b40ce416SJulian Elischer 	struct thread *td;
707b4151f71SJohn Baldwin 	struct proc *p;
7088088699fSJohn Baldwin 
709b40ce416SJulian Elischer 	td = curthread;
710b40ce416SJulian Elischer 	p = td->td_proc;
711e0f66ef8SJohn Baldwin 	ithd = (struct intr_thread *)arg;
712e0f66ef8SJohn Baldwin 	KASSERT(ithd->it_thread == td,
71391f91617SDavid E. O'Brien 	    ("%s: ithread and proc linkage out of sync", __func__));
714e0f66ef8SJohn Baldwin 	ie = ithd->it_event;
715e0f66ef8SJohn Baldwin 	ie->ie_count = 0;
7168088699fSJohn Baldwin 
7178088699fSJohn Baldwin 	/*
7188088699fSJohn Baldwin 	 * As long as we have interrupts outstanding, go through the
7198088699fSJohn Baldwin 	 * list of handlers, giving each one a go at it.
7208088699fSJohn Baldwin 	 */
7218088699fSJohn Baldwin 	for (;;) {
722b4151f71SJohn Baldwin 		/*
723b4151f71SJohn Baldwin 		 * If we are an orphaned thread, then just die.
724b4151f71SJohn Baldwin 		 */
725b4151f71SJohn Baldwin 		if (ithd->it_flags & IT_DEAD) {
726e0f66ef8SJohn Baldwin 			CTR3(KTR_INTR, "%s: pid %d (%s) exiting", __func__,
727b4151f71SJohn Baldwin 			    p->p_pid, p->p_comm);
728b4151f71SJohn Baldwin 			free(ithd, M_ITHREAD);
729b4151f71SJohn Baldwin 			kthread_exit(0);
730b4151f71SJohn Baldwin 		}
731b4151f71SJohn Baldwin 
732e0f66ef8SJohn Baldwin 		/*
733e0f66ef8SJohn Baldwin 		 * Service interrupts.  If another interrupt arrives while
734e0f66ef8SJohn Baldwin 		 * we are running, it will set it_need to note that we
735e0f66ef8SJohn Baldwin 		 * should make another pass.
736e0f66ef8SJohn Baldwin 		 */
737b4151f71SJohn Baldwin 		while (ithd->it_need) {
7388088699fSJohn Baldwin 			/*
739e0f66ef8SJohn Baldwin 			 * This might need a full read and write barrier
740e0f66ef8SJohn Baldwin 			 * to make sure that this write posts before any
741e0f66ef8SJohn Baldwin 			 * of the memory or device accesses in the
742e0f66ef8SJohn Baldwin 			 * handlers.
7438088699fSJohn Baldwin 			 */
744b4151f71SJohn Baldwin 			atomic_store_rel_int(&ithd->it_need, 0);
745e0f66ef8SJohn Baldwin 			ithread_execute_handlers(p, ie);
7468088699fSJohn Baldwin 		}
7477870c3c6SJohn Baldwin 		WITNESS_WARN(WARN_PANIC, NULL, "suspending ithread");
7487870c3c6SJohn Baldwin 		mtx_assert(&Giant, MA_NOTOWNED);
7498088699fSJohn Baldwin 
7508088699fSJohn Baldwin 		/*
7518088699fSJohn Baldwin 		 * Processed all our interrupts.  Now get the sched
7528088699fSJohn Baldwin 		 * lock.  This may take a while and it_need may get
7538088699fSJohn Baldwin 		 * set again, so we have to check it again.
7548088699fSJohn Baldwin 		 */
7559ed346baSBosko Milekic 		mtx_lock_spin(&sched_lock);
756e0f66ef8SJohn Baldwin 		if (!ithd->it_need && !(ithd->it_flags & IT_DEAD)) {
7577870c3c6SJohn Baldwin 			TD_SET_IWAIT(td);
758e0f66ef8SJohn Baldwin 			ie->ie_count = 0;
759bf0acc27SJohn Baldwin 			mi_switch(SW_VOL, NULL);
7608088699fSJohn Baldwin 		}
7619ed346baSBosko Milekic 		mtx_unlock_spin(&sched_lock);
7628088699fSJohn Baldwin 	}
7631931cf94SJohn Baldwin }
7641931cf94SJohn Baldwin 
7658b201c42SJohn Baldwin #ifdef DDB
7668b201c42SJohn Baldwin /*
7678b201c42SJohn Baldwin  * Dump details about an interrupt handler
7688b201c42SJohn Baldwin  */
7698b201c42SJohn Baldwin static void
770e0f66ef8SJohn Baldwin db_dump_intrhand(struct intr_handler *ih)
7718b201c42SJohn Baldwin {
7728b201c42SJohn Baldwin 	int comma;
7738b201c42SJohn Baldwin 
7748b201c42SJohn Baldwin 	db_printf("\t%-10s ", ih->ih_name);
7758b201c42SJohn Baldwin 	switch (ih->ih_pri) {
7768b201c42SJohn Baldwin 	case PI_REALTIME:
7778b201c42SJohn Baldwin 		db_printf("CLK ");
7788b201c42SJohn Baldwin 		break;
7798b201c42SJohn Baldwin 	case PI_AV:
7808b201c42SJohn Baldwin 		db_printf("AV  ");
7818b201c42SJohn Baldwin 		break;
7828b201c42SJohn Baldwin 	case PI_TTYHIGH:
7838b201c42SJohn Baldwin 	case PI_TTYLOW:
7848b201c42SJohn Baldwin 		db_printf("TTY ");
7858b201c42SJohn Baldwin 		break;
7868b201c42SJohn Baldwin 	case PI_TAPE:
7878b201c42SJohn Baldwin 		db_printf("TAPE");
7888b201c42SJohn Baldwin 		break;
7898b201c42SJohn Baldwin 	case PI_NET:
7908b201c42SJohn Baldwin 		db_printf("NET ");
7918b201c42SJohn Baldwin 		break;
7928b201c42SJohn Baldwin 	case PI_DISK:
7938b201c42SJohn Baldwin 	case PI_DISKLOW:
7948b201c42SJohn Baldwin 		db_printf("DISK");
7958b201c42SJohn Baldwin 		break;
7968b201c42SJohn Baldwin 	case PI_DULL:
7978b201c42SJohn Baldwin 		db_printf("DULL");
7988b201c42SJohn Baldwin 		break;
7998b201c42SJohn Baldwin 	default:
8008b201c42SJohn Baldwin 		if (ih->ih_pri >= PI_SOFT)
8018b201c42SJohn Baldwin 			db_printf("SWI ");
8028b201c42SJohn Baldwin 		else
8038b201c42SJohn Baldwin 			db_printf("%4u", ih->ih_pri);
8048b201c42SJohn Baldwin 		break;
8058b201c42SJohn Baldwin 	}
8068b201c42SJohn Baldwin 	db_printf(" ");
8078b201c42SJohn Baldwin 	db_printsym((uintptr_t)ih->ih_handler, DB_STGY_PROC);
8088b201c42SJohn Baldwin 	db_printf("(%p)", ih->ih_argument);
8098b201c42SJohn Baldwin 	if (ih->ih_need ||
8108b201c42SJohn Baldwin 	    (ih->ih_flags & (IH_FAST | IH_EXCLUSIVE | IH_ENTROPY | IH_DEAD |
8118b201c42SJohn Baldwin 	    IH_MPSAFE)) != 0) {
8128b201c42SJohn Baldwin 		db_printf(" {");
8138b201c42SJohn Baldwin 		comma = 0;
8148b201c42SJohn Baldwin 		if (ih->ih_flags & IH_FAST) {
8158b201c42SJohn Baldwin 			db_printf("FAST");
8168b201c42SJohn Baldwin 			comma = 1;
8178b201c42SJohn Baldwin 		}
8188b201c42SJohn Baldwin 		if (ih->ih_flags & IH_EXCLUSIVE) {
8198b201c42SJohn Baldwin 			if (comma)
8208b201c42SJohn Baldwin 				db_printf(", ");
8218b201c42SJohn Baldwin 			db_printf("EXCL");
8228b201c42SJohn Baldwin 			comma = 1;
8238b201c42SJohn Baldwin 		}
8248b201c42SJohn Baldwin 		if (ih->ih_flags & IH_ENTROPY) {
8258b201c42SJohn Baldwin 			if (comma)
8268b201c42SJohn Baldwin 				db_printf(", ");
8278b201c42SJohn Baldwin 			db_printf("ENTROPY");
8288b201c42SJohn Baldwin 			comma = 1;
8298b201c42SJohn Baldwin 		}
8308b201c42SJohn Baldwin 		if (ih->ih_flags & IH_DEAD) {
8318b201c42SJohn Baldwin 			if (comma)
8328b201c42SJohn Baldwin 				db_printf(", ");
8338b201c42SJohn Baldwin 			db_printf("DEAD");
8348b201c42SJohn Baldwin 			comma = 1;
8358b201c42SJohn Baldwin 		}
8368b201c42SJohn Baldwin 		if (ih->ih_flags & IH_MPSAFE) {
8378b201c42SJohn Baldwin 			if (comma)
8388b201c42SJohn Baldwin 				db_printf(", ");
8398b201c42SJohn Baldwin 			db_printf("MPSAFE");
8408b201c42SJohn Baldwin 			comma = 1;
8418b201c42SJohn Baldwin 		}
8428b201c42SJohn Baldwin 		if (ih->ih_need) {
8438b201c42SJohn Baldwin 			if (comma)
8448b201c42SJohn Baldwin 				db_printf(", ");
8458b201c42SJohn Baldwin 			db_printf("NEED");
8468b201c42SJohn Baldwin 		}
8478b201c42SJohn Baldwin 		db_printf("}");
8488b201c42SJohn Baldwin 	}
8498b201c42SJohn Baldwin 	db_printf("\n");
8508b201c42SJohn Baldwin }
8518b201c42SJohn Baldwin 
8528b201c42SJohn Baldwin /*
853e0f66ef8SJohn Baldwin  * Dump details about a event.
8548b201c42SJohn Baldwin  */
8558b201c42SJohn Baldwin void
856e0f66ef8SJohn Baldwin db_dump_intr_event(struct intr_event *ie, int handlers)
8578b201c42SJohn Baldwin {
858e0f66ef8SJohn Baldwin 	struct intr_handler *ih;
859e0f66ef8SJohn Baldwin 	struct intr_thread *it;
8608b201c42SJohn Baldwin 	int comma;
8618b201c42SJohn Baldwin 
862e0f66ef8SJohn Baldwin 	db_printf("%s ", ie->ie_fullname);
863e0f66ef8SJohn Baldwin 	it = ie->ie_thread;
864e0f66ef8SJohn Baldwin 	if (it != NULL)
865e0f66ef8SJohn Baldwin 		db_printf("(pid %d)", it->it_thread->td_proc->p_pid);
866e0f66ef8SJohn Baldwin 	else
867e0f66ef8SJohn Baldwin 		db_printf("(no thread)");
868e0f66ef8SJohn Baldwin 	if ((ie->ie_flags & (IE_SOFT | IE_ENTROPY | IE_ADDING_THREAD)) != 0 ||
869e0f66ef8SJohn Baldwin 	    (it != NULL && it->it_need)) {
8708b201c42SJohn Baldwin 		db_printf(" {");
8718b201c42SJohn Baldwin 		comma = 0;
872e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_SOFT) {
8738b201c42SJohn Baldwin 			db_printf("SOFT");
8748b201c42SJohn Baldwin 			comma = 1;
8758b201c42SJohn Baldwin 		}
876e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_ENTROPY) {
8778b201c42SJohn Baldwin 			if (comma)
8788b201c42SJohn Baldwin 				db_printf(", ");
8798b201c42SJohn Baldwin 			db_printf("ENTROPY");
8808b201c42SJohn Baldwin 			comma = 1;
8818b201c42SJohn Baldwin 		}
882e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_ADDING_THREAD) {
8838b201c42SJohn Baldwin 			if (comma)
8848b201c42SJohn Baldwin 				db_printf(", ");
885e0f66ef8SJohn Baldwin 			db_printf("ADDING_THREAD");
8868b201c42SJohn Baldwin 			comma = 1;
8878b201c42SJohn Baldwin 		}
888e0f66ef8SJohn Baldwin 		if (it != NULL && it->it_need) {
8898b201c42SJohn Baldwin 			if (comma)
8908b201c42SJohn Baldwin 				db_printf(", ");
8918b201c42SJohn Baldwin 			db_printf("NEED");
8928b201c42SJohn Baldwin 		}
8938b201c42SJohn Baldwin 		db_printf("}");
8948b201c42SJohn Baldwin 	}
8958b201c42SJohn Baldwin 	db_printf("\n");
8968b201c42SJohn Baldwin 
8978b201c42SJohn Baldwin 	if (handlers)
898e0f66ef8SJohn Baldwin 		TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next)
8998b201c42SJohn Baldwin 		    db_dump_intrhand(ih);
9008b201c42SJohn Baldwin }
901e0f66ef8SJohn Baldwin 
902e0f66ef8SJohn Baldwin /*
903e0f66ef8SJohn Baldwin  * Dump data about interrupt handlers
904e0f66ef8SJohn Baldwin  */
905e0f66ef8SJohn Baldwin DB_SHOW_COMMAND(intr, db_show_intr)
906e0f66ef8SJohn Baldwin {
907e0f66ef8SJohn Baldwin 	struct intr_event *ie;
90819e9205aSJohn Baldwin 	int all, verbose;
909e0f66ef8SJohn Baldwin 
910e0f66ef8SJohn Baldwin 	verbose = index(modif, 'v') != NULL;
911e0f66ef8SJohn Baldwin 	all = index(modif, 'a') != NULL;
912e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ie, &event_list, ie_list) {
913e0f66ef8SJohn Baldwin 		if (!all && TAILQ_EMPTY(&ie->ie_handlers))
914e0f66ef8SJohn Baldwin 			continue;
915e0f66ef8SJohn Baldwin 		db_dump_intr_event(ie, verbose);
91619e9205aSJohn Baldwin 		if (db_pager_quit)
91719e9205aSJohn Baldwin 			break;
918e0f66ef8SJohn Baldwin 	}
919e0f66ef8SJohn Baldwin }
9208b201c42SJohn Baldwin #endif /* DDB */
9218b201c42SJohn Baldwin 
922b4151f71SJohn Baldwin /*
9238088699fSJohn Baldwin  * Start standard software interrupt threads
9241931cf94SJohn Baldwin  */
9251931cf94SJohn Baldwin static void
926b4151f71SJohn Baldwin start_softintr(void *dummy)
9271931cf94SJohn Baldwin {
9288804bf6bSJohn Baldwin 	struct proc *p;
929b4151f71SJohn Baldwin 
930e0f66ef8SJohn Baldwin 	if (swi_add(&clk_intr_event, "clock", softclock, NULL, SWI_CLOCK,
931b4151f71SJohn Baldwin 		INTR_MPSAFE, &softclock_ih) ||
93279501b66SScott Long 	    swi_add(NULL, "vm", swi_vm, NULL, SWI_VM, INTR_MPSAFE, &vm_ih))
933b4151f71SJohn Baldwin 		panic("died while creating standard software ithreads");
9343e5da754SJohn Baldwin 
935e0f66ef8SJohn Baldwin 	p = clk_intr_event->ie_thread->it_thread->td_proc;
9368804bf6bSJohn Baldwin 	PROC_LOCK(p);
9378804bf6bSJohn Baldwin 	p->p_flag |= P_NOLOAD;
9388804bf6bSJohn Baldwin 	PROC_UNLOCK(p);
9391931cf94SJohn Baldwin }
940b4151f71SJohn Baldwin SYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL)
9411931cf94SJohn Baldwin 
942d279178dSThomas Moestl /*
943d279178dSThomas Moestl  * Sysctls used by systat and others: hw.intrnames and hw.intrcnt.
944d279178dSThomas Moestl  * The data for this machine dependent, and the declarations are in machine
945d279178dSThomas Moestl  * dependent code.  The layout of intrnames and intrcnt however is machine
946d279178dSThomas Moestl  * independent.
947d279178dSThomas Moestl  *
948d279178dSThomas Moestl  * We do not know the length of intrcnt and intrnames at compile time, so
949d279178dSThomas Moestl  * calculate things at run time.
950d279178dSThomas Moestl  */
951d279178dSThomas Moestl static int
952d279178dSThomas Moestl sysctl_intrnames(SYSCTL_HANDLER_ARGS)
953d279178dSThomas Moestl {
954d279178dSThomas Moestl 	return (sysctl_handle_opaque(oidp, intrnames, eintrnames - intrnames,
955d279178dSThomas Moestl 	   req));
956d279178dSThomas Moestl }
957d279178dSThomas Moestl 
958d279178dSThomas Moestl SYSCTL_PROC(_hw, OID_AUTO, intrnames, CTLTYPE_OPAQUE | CTLFLAG_RD,
959d279178dSThomas Moestl     NULL, 0, sysctl_intrnames, "", "Interrupt Names");
960d279178dSThomas Moestl 
961d279178dSThomas Moestl static int
962d279178dSThomas Moestl sysctl_intrcnt(SYSCTL_HANDLER_ARGS)
963d279178dSThomas Moestl {
964d279178dSThomas Moestl 	return (sysctl_handle_opaque(oidp, intrcnt,
965d279178dSThomas Moestl 	    (char *)eintrcnt - (char *)intrcnt, req));
966d279178dSThomas Moestl }
967d279178dSThomas Moestl 
968d279178dSThomas Moestl SYSCTL_PROC(_hw, OID_AUTO, intrcnt, CTLTYPE_OPAQUE | CTLFLAG_RD,
969d279178dSThomas Moestl     NULL, 0, sysctl_intrcnt, "", "Interrupt Counts");
9708b201c42SJohn Baldwin 
9718b201c42SJohn Baldwin #ifdef DDB
9728b201c42SJohn Baldwin /*
9738b201c42SJohn Baldwin  * DDB command to dump the interrupt statistics.
9748b201c42SJohn Baldwin  */
9758b201c42SJohn Baldwin DB_SHOW_COMMAND(intrcnt, db_show_intrcnt)
9768b201c42SJohn Baldwin {
9778b201c42SJohn Baldwin 	u_long *i;
9788b201c42SJohn Baldwin 	char *cp;
9798b201c42SJohn Baldwin 
9808b201c42SJohn Baldwin 	cp = intrnames;
98119e9205aSJohn Baldwin 	for (i = intrcnt; i != eintrcnt && !db_pager_quit; i++) {
9828b201c42SJohn Baldwin 		if (*cp == '\0')
9838b201c42SJohn Baldwin 			break;
9848b201c42SJohn Baldwin 		if (*i != 0)
9858b201c42SJohn Baldwin 			db_printf("%s\t%lu\n", cp, *i);
9868b201c42SJohn Baldwin 		cp += strlen(cp) + 1;
9878b201c42SJohn Baldwin 	}
9888b201c42SJohn Baldwin }
9898b201c42SJohn Baldwin #endif
990