xref: /freebsd/sys/kern/kern_intr.c (revision 0ae62c18a07d9d8eff31f40711ecba714ab0be4f)
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 
860ae62c18SNate Lawson static int intr_storm_threshold = 1000;
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 
102bc17acb2SJohn Baldwin /* Map an interrupt type to an ithread priority. */
103b4151f71SJohn Baldwin u_char
104e0f66ef8SJohn Baldwin intr_priority(enum intr_type flags)
1059a94c9c5SJohn Baldwin {
106b4151f71SJohn Baldwin 	u_char pri;
1079a94c9c5SJohn Baldwin 
108b4151f71SJohn Baldwin 	flags &= (INTR_TYPE_TTY | INTR_TYPE_BIO | INTR_TYPE_NET |
1095a280d9cSPeter Wemm 	    INTR_TYPE_CAM | INTR_TYPE_MISC | INTR_TYPE_CLK | INTR_TYPE_AV);
1109a94c9c5SJohn Baldwin 	switch (flags) {
111b4151f71SJohn Baldwin 	case INTR_TYPE_TTY:
1129a94c9c5SJohn Baldwin 		pri = PI_TTYLOW;
1139a94c9c5SJohn Baldwin 		break;
1149a94c9c5SJohn Baldwin 	case INTR_TYPE_BIO:
1159a94c9c5SJohn Baldwin 		/*
1169a94c9c5SJohn Baldwin 		 * XXX We need to refine this.  BSD/OS distinguishes
1179a94c9c5SJohn Baldwin 		 * between tape and disk priorities.
1189a94c9c5SJohn Baldwin 		 */
1199a94c9c5SJohn Baldwin 		pri = PI_DISK;
1209a94c9c5SJohn Baldwin 		break;
1219a94c9c5SJohn Baldwin 	case INTR_TYPE_NET:
1229a94c9c5SJohn Baldwin 		pri = PI_NET;
1239a94c9c5SJohn Baldwin 		break;
1249a94c9c5SJohn Baldwin 	case INTR_TYPE_CAM:
1259a94c9c5SJohn Baldwin 		pri = PI_DISK;          /* XXX or PI_CAM? */
1269a94c9c5SJohn Baldwin 		break;
1275a280d9cSPeter Wemm 	case INTR_TYPE_AV:		/* Audio/video */
1285a280d9cSPeter Wemm 		pri = PI_AV;
1295a280d9cSPeter Wemm 		break;
130b4151f71SJohn Baldwin 	case INTR_TYPE_CLK:
131b4151f71SJohn Baldwin 		pri = PI_REALTIME;
132b4151f71SJohn Baldwin 		break;
1339a94c9c5SJohn Baldwin 	case INTR_TYPE_MISC:
1349a94c9c5SJohn Baldwin 		pri = PI_DULL;          /* don't care */
1359a94c9c5SJohn Baldwin 		break;
1369a94c9c5SJohn Baldwin 	default:
137b4151f71SJohn Baldwin 		/* We didn't specify an interrupt level. */
138e0f66ef8SJohn Baldwin 		panic("intr_priority: no interrupt type in flags");
1399a94c9c5SJohn Baldwin 	}
1409a94c9c5SJohn Baldwin 
1419a94c9c5SJohn Baldwin 	return pri;
1429a94c9c5SJohn Baldwin }
1439a94c9c5SJohn Baldwin 
144b4151f71SJohn Baldwin /*
145e0f66ef8SJohn Baldwin  * Update an ithread based on the associated intr_event.
146b4151f71SJohn Baldwin  */
147b4151f71SJohn Baldwin static void
148e0f66ef8SJohn Baldwin ithread_update(struct intr_thread *ithd)
149b4151f71SJohn Baldwin {
150e0f66ef8SJohn Baldwin 	struct intr_event *ie;
151b40ce416SJulian Elischer 	struct thread *td;
152e0f66ef8SJohn Baldwin 	u_char pri;
1538088699fSJohn Baldwin 
154e0f66ef8SJohn Baldwin 	ie = ithd->it_event;
155e0f66ef8SJohn Baldwin 	td = ithd->it_thread;
156b4151f71SJohn Baldwin 
157e0f66ef8SJohn Baldwin 	/* Determine the overall priority of this event. */
158e0f66ef8SJohn Baldwin 	if (TAILQ_EMPTY(&ie->ie_handlers))
159e0f66ef8SJohn Baldwin 		pri = PRI_MAX_ITHD;
160e0f66ef8SJohn Baldwin 	else
161e0f66ef8SJohn Baldwin 		pri = TAILQ_FIRST(&ie->ie_handlers)->ih_pri;
162e80fb434SRobert Drehmel 
163e0f66ef8SJohn Baldwin 	/* Update name and priority. */
164e0f66ef8SJohn Baldwin 	strlcpy(td->td_proc->p_comm, ie->ie_fullname,
165e0f66ef8SJohn Baldwin 	    sizeof(td->td_proc->p_comm));
166b106d2f5SJohn Baldwin 	mtx_lock_spin(&sched_lock);
167e0f66ef8SJohn Baldwin 	sched_prio(td, pri);
168b106d2f5SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
169b4151f71SJohn Baldwin }
170e0f66ef8SJohn Baldwin 
171e0f66ef8SJohn Baldwin /*
172e0f66ef8SJohn Baldwin  * Regenerate the full name of an interrupt event and update its priority.
173e0f66ef8SJohn Baldwin  */
174e0f66ef8SJohn Baldwin static void
175e0f66ef8SJohn Baldwin intr_event_update(struct intr_event *ie)
176e0f66ef8SJohn Baldwin {
177e0f66ef8SJohn Baldwin 	struct intr_handler *ih;
178e0f66ef8SJohn Baldwin 	char *last;
179e0f66ef8SJohn Baldwin 	int missed, space;
180e0f66ef8SJohn Baldwin 
181e0f66ef8SJohn Baldwin 	/* Start off with no entropy and just the name of the event. */
182e0f66ef8SJohn Baldwin 	mtx_assert(&ie->ie_lock, MA_OWNED);
183e0f66ef8SJohn Baldwin 	strlcpy(ie->ie_fullname, ie->ie_name, sizeof(ie->ie_fullname));
184e0f66ef8SJohn Baldwin 	ie->ie_flags &= ~IE_ENTROPY;
1850811d60aSJohn Baldwin 	missed = 0;
186e0f66ef8SJohn Baldwin 	space = 1;
187e0f66ef8SJohn Baldwin 
188e0f66ef8SJohn Baldwin 	/* Run through all the handlers updating values. */
189e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
190e0f66ef8SJohn Baldwin 		if (strlen(ie->ie_fullname) + strlen(ih->ih_name) + 1 <
191e0f66ef8SJohn Baldwin 		    sizeof(ie->ie_fullname)) {
192e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, " ");
193e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, ih->ih_name);
194e0f66ef8SJohn Baldwin 			space = 0;
1950811d60aSJohn Baldwin 		} else
1960811d60aSJohn Baldwin 			missed++;
1970811d60aSJohn Baldwin 		if (ih->ih_flags & IH_ENTROPY)
198e0f66ef8SJohn Baldwin 			ie->ie_flags |= IE_ENTROPY;
1990811d60aSJohn Baldwin 	}
200e0f66ef8SJohn Baldwin 
201e0f66ef8SJohn Baldwin 	/*
202e0f66ef8SJohn Baldwin 	 * If the handler names were too long, add +'s to indicate missing
203e0f66ef8SJohn Baldwin 	 * names. If we run out of room and still have +'s to add, change
204e0f66ef8SJohn Baldwin 	 * the last character from a + to a *.
205e0f66ef8SJohn Baldwin 	 */
206e0f66ef8SJohn Baldwin 	last = &ie->ie_fullname[sizeof(ie->ie_fullname) - 2];
2070811d60aSJohn Baldwin 	while (missed-- > 0) {
208e0f66ef8SJohn Baldwin 		if (strlen(ie->ie_fullname) + 1 == sizeof(ie->ie_fullname)) {
209e0f66ef8SJohn Baldwin 			if (*last == '+') {
210e0f66ef8SJohn Baldwin 				*last = '*';
211e0f66ef8SJohn Baldwin 				break;
212b4151f71SJohn Baldwin 			} else
213e0f66ef8SJohn Baldwin 				*last = '+';
214e0f66ef8SJohn Baldwin 		} else if (space) {
215e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, " +");
216e0f66ef8SJohn Baldwin 			space = 0;
217e0f66ef8SJohn Baldwin 		} else
218e0f66ef8SJohn Baldwin 			strcat(ie->ie_fullname, "+");
219b4151f71SJohn Baldwin 	}
220e0f66ef8SJohn Baldwin 
221e0f66ef8SJohn Baldwin 	/*
222e0f66ef8SJohn Baldwin 	 * If this event has an ithread, update it's priority and
223e0f66ef8SJohn Baldwin 	 * name.
224e0f66ef8SJohn Baldwin 	 */
225e0f66ef8SJohn Baldwin 	if (ie->ie_thread != NULL)
226e0f66ef8SJohn Baldwin 		ithread_update(ie->ie_thread);
227e0f66ef8SJohn Baldwin 	CTR2(KTR_INTR, "%s: updated %s", __func__, ie->ie_fullname);
228b4151f71SJohn Baldwin }
229b4151f71SJohn Baldwin 
230b4151f71SJohn Baldwin int
231e0f66ef8SJohn Baldwin intr_event_create(struct intr_event **event, void *source, int flags,
232e0f66ef8SJohn Baldwin     void (*enable)(void *), const char *fmt, ...)
233b4151f71SJohn Baldwin {
234e0f66ef8SJohn Baldwin 	struct intr_event *ie;
235b4151f71SJohn Baldwin 	va_list ap;
236b4151f71SJohn Baldwin 
237e0f66ef8SJohn Baldwin 	/* The only valid flag during creation is IE_SOFT. */
238e0f66ef8SJohn Baldwin 	if ((flags & ~IE_SOFT) != 0)
2393e5da754SJohn Baldwin 		return (EINVAL);
240e0f66ef8SJohn Baldwin 	ie = malloc(sizeof(struct intr_event), M_ITHREAD, M_WAITOK | M_ZERO);
241e0f66ef8SJohn Baldwin 	ie->ie_source = source;
242e0f66ef8SJohn Baldwin 	ie->ie_enable = enable;
243e0f66ef8SJohn Baldwin 	ie->ie_flags = flags;
244e0f66ef8SJohn Baldwin 	TAILQ_INIT(&ie->ie_handlers);
245e0f66ef8SJohn Baldwin 	mtx_init(&ie->ie_lock, "intr event", NULL, MTX_DEF);
246b4151f71SJohn Baldwin 
247b4151f71SJohn Baldwin 	va_start(ap, fmt);
248e0f66ef8SJohn Baldwin 	vsnprintf(ie->ie_name, sizeof(ie->ie_name), fmt, ap);
249b4151f71SJohn Baldwin 	va_end(ap);
250e0f66ef8SJohn Baldwin 	strlcpy(ie->ie_fullname, ie->ie_name, sizeof(ie->ie_fullname));
251e0f66ef8SJohn Baldwin 	mtx_pool_lock(mtxpool_sleep, &event_list);
252e0f66ef8SJohn Baldwin 	TAILQ_INSERT_TAIL(&event_list, ie, ie_list);
253e0f66ef8SJohn Baldwin 	mtx_pool_unlock(mtxpool_sleep, &event_list);
254e0f66ef8SJohn Baldwin 	if (event != NULL)
255e0f66ef8SJohn Baldwin 		*event = ie;
256e0f66ef8SJohn Baldwin 	CTR2(KTR_INTR, "%s: created %s", __func__, ie->ie_name);
257b4151f71SJohn Baldwin 	return (0);
258b4151f71SJohn Baldwin }
259b4151f71SJohn Baldwin 
260b4151f71SJohn Baldwin int
261e0f66ef8SJohn Baldwin intr_event_destroy(struct intr_event *ie)
262b4151f71SJohn Baldwin {
263b4151f71SJohn Baldwin 
264e0f66ef8SJohn Baldwin 	mtx_lock(&ie->ie_lock);
265e0f66ef8SJohn Baldwin 	if (!TAILQ_EMPTY(&ie->ie_handlers)) {
266e0f66ef8SJohn Baldwin 		mtx_unlock(&ie->ie_lock);
267e0f66ef8SJohn Baldwin 		return (EBUSY);
2684d29cb2dSJohn Baldwin 	}
269e0f66ef8SJohn Baldwin 	mtx_pool_lock(mtxpool_sleep, &event_list);
270e0f66ef8SJohn Baldwin 	TAILQ_REMOVE(&event_list, ie, ie_list);
271e0f66ef8SJohn Baldwin 	mtx_pool_unlock(mtxpool_sleep, &event_list);
2729477358dSJohn Baldwin #ifndef notyet
2739477358dSJohn Baldwin 	if (ie->ie_thread != NULL) {
2749477358dSJohn Baldwin 		ithread_destroy(ie->ie_thread);
2759477358dSJohn Baldwin 		ie->ie_thread = NULL;
2769477358dSJohn Baldwin 	}
2779477358dSJohn Baldwin #endif
278e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
279e0f66ef8SJohn Baldwin 	mtx_destroy(&ie->ie_lock);
280e0f66ef8SJohn Baldwin 	free(ie, M_ITHREAD);
281e0f66ef8SJohn Baldwin 	return (0);
282e0f66ef8SJohn Baldwin }
283e0f66ef8SJohn Baldwin 
284e0f66ef8SJohn Baldwin static struct intr_thread *
285e0f66ef8SJohn Baldwin ithread_create(const char *name)
286e0f66ef8SJohn Baldwin {
287e0f66ef8SJohn Baldwin 	struct intr_thread *ithd;
288e0f66ef8SJohn Baldwin 	struct thread *td;
289e0f66ef8SJohn Baldwin 	struct proc *p;
290e0f66ef8SJohn Baldwin 	int error;
291e0f66ef8SJohn Baldwin 
292e0f66ef8SJohn Baldwin 	ithd = malloc(sizeof(struct intr_thread), M_ITHREAD, M_WAITOK | M_ZERO);
293e0f66ef8SJohn Baldwin 
294e0f66ef8SJohn Baldwin 	error = kthread_create(ithread_loop, ithd, &p, RFSTOPPED | RFHIGHPID,
295e0f66ef8SJohn Baldwin 	    0, "%s", name);
296e0f66ef8SJohn Baldwin 	if (error)
297e0f66ef8SJohn Baldwin 		panic("kthread_create() failed with %d", error);
298e0f66ef8SJohn Baldwin 	td = FIRST_THREAD_IN_PROC(p);	/* XXXKSE */
2994d29cb2dSJohn Baldwin 	mtx_lock_spin(&sched_lock);
300ad1e7d28SJulian Elischer 	sched_class(td, PRI_ITHD);
301e0f66ef8SJohn Baldwin 	TD_SET_IWAIT(td);
302e0f66ef8SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
303e0f66ef8SJohn Baldwin 	td->td_pflags |= TDP_ITHREAD;
304e0f66ef8SJohn Baldwin 	ithd->it_thread = td;
305e0f66ef8SJohn Baldwin 	CTR2(KTR_INTR, "%s: created %s", __func__, name);
306e0f66ef8SJohn Baldwin 	return (ithd);
307e0f66ef8SJohn Baldwin }
308e0f66ef8SJohn Baldwin 
309e0f66ef8SJohn Baldwin static void
310e0f66ef8SJohn Baldwin ithread_destroy(struct intr_thread *ithread)
311e0f66ef8SJohn Baldwin {
312e0f66ef8SJohn Baldwin 	struct thread *td;
313e0f66ef8SJohn Baldwin 
314bb141be1SScott Long 	CTR2(KTR_INTR, "%s: killing %s", __func__, ithread->it_event->ie_name);
315e0f66ef8SJohn Baldwin 	td = ithread->it_thread;
316e0f66ef8SJohn Baldwin 	mtx_lock_spin(&sched_lock);
317e0f66ef8SJohn Baldwin 	ithread->it_flags |= IT_DEAD;
31871fad9fdSJulian Elischer 	if (TD_AWAITING_INTR(td)) {
31971fad9fdSJulian Elischer 		TD_CLR_IWAIT(td);
320f0393f06SJeff Roberson 		sched_add(td, SRQ_INTR);
321b4151f71SJohn Baldwin 	}
322b4151f71SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
323b4151f71SJohn Baldwin }
324b4151f71SJohn Baldwin 
325b4151f71SJohn Baldwin int
326e0f66ef8SJohn Baldwin intr_event_add_handler(struct intr_event *ie, const char *name,
327ef544f63SPaolo Pisati     driver_filter_t filter, driver_intr_t handler, void *arg, u_char pri,
328ef544f63SPaolo Pisati     enum intr_type flags, void **cookiep)
329b4151f71SJohn Baldwin {
330e0f66ef8SJohn Baldwin 	struct intr_handler *ih, *temp_ih;
331e0f66ef8SJohn Baldwin 	struct intr_thread *it;
332b4151f71SJohn Baldwin 
333ef544f63SPaolo Pisati 	if (ie == NULL || name == NULL || (handler == NULL && filter == NULL))
334b4151f71SJohn Baldwin 		return (EINVAL);
335b4151f71SJohn Baldwin 
336e0f66ef8SJohn Baldwin 	/* Allocate and populate an interrupt handler structure. */
337e0f66ef8SJohn Baldwin 	ih = malloc(sizeof(struct intr_handler), M_ITHREAD, M_WAITOK | M_ZERO);
338ef544f63SPaolo Pisati 	ih->ih_filter = filter;
339b4151f71SJohn Baldwin 	ih->ih_handler = handler;
340b4151f71SJohn Baldwin 	ih->ih_argument = arg;
341b4151f71SJohn Baldwin 	ih->ih_name = name;
342e0f66ef8SJohn Baldwin 	ih->ih_event = ie;
343b4151f71SJohn Baldwin 	ih->ih_pri = pri;
344ef544f63SPaolo Pisati 	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. */
374ef544f63SPaolo Pisati 	while (ie->ie_thread == NULL && handler != NULL) {
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 
398c3045318SJohn Baldwin /*
399c3045318SJohn Baldwin  * Return the ie_source field from the intr_event an intr_handler is
400c3045318SJohn Baldwin  * associated with.
401c3045318SJohn Baldwin  */
402c3045318SJohn Baldwin void *
403c3045318SJohn Baldwin intr_handler_source(void *cookie)
404c3045318SJohn Baldwin {
405c3045318SJohn Baldwin 	struct intr_handler *ih;
406c3045318SJohn Baldwin 	struct intr_event *ie;
407c3045318SJohn Baldwin 
408c3045318SJohn Baldwin 	ih = (struct intr_handler *)cookie;
409c3045318SJohn Baldwin 	if (ih == NULL)
410c3045318SJohn Baldwin 		return (NULL);
411c3045318SJohn Baldwin 	ie = ih->ih_event;
412c3045318SJohn Baldwin 	KASSERT(ie != NULL,
413c3045318SJohn Baldwin 	    ("interrupt handler \"%s\" has a NULL interrupt event",
414c3045318SJohn Baldwin 	    ih->ih_name));
415c3045318SJohn Baldwin 	return (ie->ie_source);
416c3045318SJohn Baldwin }
417c3045318SJohn Baldwin 
418b4151f71SJohn Baldwin int
419e0f66ef8SJohn Baldwin intr_event_remove_handler(void *cookie)
420b4151f71SJohn Baldwin {
421e0f66ef8SJohn Baldwin 	struct intr_handler *handler = (struct intr_handler *)cookie;
422e0f66ef8SJohn Baldwin 	struct intr_event *ie;
423b4151f71SJohn Baldwin #ifdef INVARIANTS
424e0f66ef8SJohn Baldwin 	struct intr_handler *ih;
425e0f66ef8SJohn Baldwin #endif
426e0f66ef8SJohn Baldwin #ifdef notyet
427e0f66ef8SJohn Baldwin 	int dead;
428b4151f71SJohn Baldwin #endif
429b4151f71SJohn Baldwin 
4303e5da754SJohn Baldwin 	if (handler == NULL)
431b4151f71SJohn Baldwin 		return (EINVAL);
432e0f66ef8SJohn Baldwin 	ie = handler->ih_event;
433e0f66ef8SJohn Baldwin 	KASSERT(ie != NULL,
434e0f66ef8SJohn Baldwin 	    ("interrupt handler \"%s\" has a NULL interrupt event",
4353e5da754SJohn Baldwin 	    handler->ih_name));
436e0f66ef8SJohn Baldwin 	mtx_lock(&ie->ie_lock);
43791f91617SDavid E. O'Brien 	CTR3(KTR_INTR, "%s: removing %s from %s", __func__, handler->ih_name,
438e0f66ef8SJohn Baldwin 	    ie->ie_name);
439b4151f71SJohn Baldwin #ifdef INVARIANTS
440e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next)
4413e5da754SJohn Baldwin 		if (ih == handler)
4423e5da754SJohn Baldwin 			goto ok;
443e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
444e0f66ef8SJohn Baldwin 	panic("interrupt handler \"%s\" not found in interrupt event \"%s\"",
445e0f66ef8SJohn Baldwin 	    ih->ih_name, ie->ie_name);
4463e5da754SJohn Baldwin ok:
447b4151f71SJohn Baldwin #endif
448de271f01SJohn Baldwin 	/*
449e0f66ef8SJohn Baldwin 	 * If there is no ithread, then just remove the handler and return.
450e0f66ef8SJohn Baldwin 	 * XXX: Note that an INTR_FAST handler might be running on another
451e0f66ef8SJohn Baldwin 	 * CPU!
452e0f66ef8SJohn Baldwin 	 */
453e0f66ef8SJohn Baldwin 	if (ie->ie_thread == NULL) {
454e0f66ef8SJohn Baldwin 		TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next);
455e0f66ef8SJohn Baldwin 		mtx_unlock(&ie->ie_lock);
456e0f66ef8SJohn Baldwin 		free(handler, M_ITHREAD);
457e0f66ef8SJohn Baldwin 		return (0);
458e0f66ef8SJohn Baldwin 	}
459e0f66ef8SJohn Baldwin 
460e0f66ef8SJohn Baldwin 	/*
461de271f01SJohn Baldwin 	 * If the interrupt thread is already running, then just mark this
462de271f01SJohn Baldwin 	 * handler as being dead and let the ithread do the actual removal.
463288e351bSDon Lewis 	 *
464288e351bSDon Lewis 	 * During a cold boot while cold is set, msleep() does not sleep,
465288e351bSDon Lewis 	 * so we have to remove the handler here rather than letting the
466288e351bSDon Lewis 	 * thread do it.
467de271f01SJohn Baldwin 	 */
468de271f01SJohn Baldwin 	mtx_lock_spin(&sched_lock);
469e0f66ef8SJohn Baldwin 	if (!TD_AWAITING_INTR(ie->ie_thread->it_thread) && !cold) {
470de271f01SJohn Baldwin 		handler->ih_flags |= IH_DEAD;
471de271f01SJohn Baldwin 
472de271f01SJohn Baldwin 		/*
473de271f01SJohn Baldwin 		 * Ensure that the thread will process the handler list
474de271f01SJohn Baldwin 		 * again and remove this handler if it has already passed
475de271f01SJohn Baldwin 		 * it on the list.
476de271f01SJohn Baldwin 		 */
477e0f66ef8SJohn Baldwin 		ie->ie_thread->it_need = 1;
4784d29cb2dSJohn Baldwin 	} else
479e0f66ef8SJohn Baldwin 		TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next);
480de271f01SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
481e0f66ef8SJohn Baldwin 	while (handler->ih_flags & IH_DEAD)
4820f180a7cSJohn Baldwin 		msleep(handler, &ie->ie_lock, 0, "iev_rmh", 0);
483e0f66ef8SJohn Baldwin 	intr_event_update(ie);
484e0f66ef8SJohn Baldwin #ifdef notyet
485e0f66ef8SJohn Baldwin 	/*
486e0f66ef8SJohn Baldwin 	 * XXX: This could be bad in the case of ppbus(8).  Also, I think
487e0f66ef8SJohn Baldwin 	 * this could lead to races of stale data when servicing an
488e0f66ef8SJohn Baldwin 	 * interrupt.
489e0f66ef8SJohn Baldwin 	 */
490e0f66ef8SJohn Baldwin 	dead = 1;
491e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
492e0f66ef8SJohn Baldwin 		if (!(ih->ih_flags & IH_FAST)) {
493e0f66ef8SJohn Baldwin 			dead = 0;
494e0f66ef8SJohn Baldwin 			break;
495e0f66ef8SJohn Baldwin 		}
496e0f66ef8SJohn Baldwin 	}
497e0f66ef8SJohn Baldwin 	if (dead) {
498e0f66ef8SJohn Baldwin 		ithread_destroy(ie->ie_thread);
499e0f66ef8SJohn Baldwin 		ie->ie_thread = NULL;
500e0f66ef8SJohn Baldwin 	}
501e0f66ef8SJohn Baldwin #endif
502e0f66ef8SJohn Baldwin 	mtx_unlock(&ie->ie_lock);
503b4151f71SJohn Baldwin 	free(handler, M_ITHREAD);
504b4151f71SJohn Baldwin 	return (0);
505b4151f71SJohn Baldwin }
506b4151f71SJohn Baldwin 
507b4151f71SJohn Baldwin int
508e0f66ef8SJohn Baldwin intr_event_schedule_thread(struct intr_event *ie)
5093e5da754SJohn Baldwin {
510e0f66ef8SJohn Baldwin 	struct intr_entropy entropy;
511e0f66ef8SJohn Baldwin 	struct intr_thread *it;
512b40ce416SJulian Elischer 	struct thread *td;
51304774f23SJulian Elischer 	struct thread *ctd;
5143e5da754SJohn Baldwin 	struct proc *p;
5153e5da754SJohn Baldwin 
5163e5da754SJohn Baldwin 	/*
5173e5da754SJohn Baldwin 	 * If no ithread or no handlers, then we have a stray interrupt.
5183e5da754SJohn Baldwin 	 */
519e0f66ef8SJohn Baldwin 	if (ie == NULL || TAILQ_EMPTY(&ie->ie_handlers) ||
520e0f66ef8SJohn Baldwin 	    ie->ie_thread == NULL)
5213e5da754SJohn Baldwin 		return (EINVAL);
5223e5da754SJohn Baldwin 
52304774f23SJulian Elischer 	ctd = curthread;
524e0f66ef8SJohn Baldwin 	it = ie->ie_thread;
525e0f66ef8SJohn Baldwin 	td = it->it_thread;
5266f40c417SRobert Watson 	p = td->td_proc;
527e0f66ef8SJohn Baldwin 
5283e5da754SJohn Baldwin 	/*
5293e5da754SJohn Baldwin 	 * If any of the handlers for this ithread claim to be good
5303e5da754SJohn Baldwin 	 * sources of entropy, then gather some.
5313e5da754SJohn Baldwin 	 */
532e0f66ef8SJohn Baldwin 	if (harvest.interrupt && ie->ie_flags & IE_ENTROPY) {
5336f40c417SRobert Watson 		CTR3(KTR_INTR, "%s: pid %d (%s) gathering entropy", __func__,
5346f40c417SRobert Watson 		    p->p_pid, p->p_comm);
535e0f66ef8SJohn Baldwin 		entropy.event = (uintptr_t)ie;
536e0f66ef8SJohn Baldwin 		entropy.td = ctd;
5373e5da754SJohn Baldwin 		random_harvest(&entropy, sizeof(entropy), 2, 0,
5383e5da754SJohn Baldwin 		    RANDOM_INTERRUPT);
5393e5da754SJohn Baldwin 	}
5403e5da754SJohn Baldwin 
541e0f66ef8SJohn Baldwin 	KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name));
5423e5da754SJohn Baldwin 
5433e5da754SJohn Baldwin 	/*
5443e5da754SJohn Baldwin 	 * Set it_need to tell the thread to keep running if it is already
5453e5da754SJohn Baldwin 	 * running.  Then, grab sched_lock and see if we actually need to
5460c0b25aeSJohn Baldwin 	 * put this thread on the runqueue.
5473e5da754SJohn Baldwin 	 */
548e0f66ef8SJohn Baldwin 	it->it_need = 1;
5493e5da754SJohn Baldwin 	mtx_lock_spin(&sched_lock);
55071fad9fdSJulian Elischer 	if (TD_AWAITING_INTR(td)) {
551e0f66ef8SJohn Baldwin 		CTR3(KTR_INTR, "%s: schedule pid %d (%s)", __func__, p->p_pid,
552e0f66ef8SJohn Baldwin 		    p->p_comm);
55371fad9fdSJulian Elischer 		TD_CLR_IWAIT(td);
554f0393f06SJeff Roberson 		sched_add(td, SRQ_INTR);
5553e5da754SJohn Baldwin 	} else {
556e0f66ef8SJohn Baldwin 		CTR5(KTR_INTR, "%s: pid %d (%s): it_need %d, state %d",
557e0f66ef8SJohn Baldwin 		    __func__, p->p_pid, p->p_comm, it->it_need, td->td_state);
5583e5da754SJohn Baldwin 	}
5593e5da754SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
5603e5da754SJohn Baldwin 
5613e5da754SJohn Baldwin 	return (0);
5623e5da754SJohn Baldwin }
5633e5da754SJohn Baldwin 
564fe486a37SJohn Baldwin /*
565fe486a37SJohn Baldwin  * Add a software interrupt handler to a specified event.  If a given event
566fe486a37SJohn Baldwin  * is not specified, then a new event is created.
567fe486a37SJohn Baldwin  */
5683e5da754SJohn Baldwin int
569e0f66ef8SJohn Baldwin swi_add(struct intr_event **eventp, const char *name, driver_intr_t handler,
570b4151f71SJohn Baldwin 	    void *arg, int pri, enum intr_type flags, void **cookiep)
5718088699fSJohn Baldwin {
572e0f66ef8SJohn Baldwin 	struct intr_event *ie;
573b4151f71SJohn Baldwin 	int error;
5748088699fSJohn Baldwin 
5753e5da754SJohn Baldwin 	if (flags & (INTR_FAST | INTR_ENTROPY))
5763e5da754SJohn Baldwin 		return (EINVAL);
5773e5da754SJohn Baldwin 
578e0f66ef8SJohn Baldwin 	ie = (eventp != NULL) ? *eventp : NULL;
5798088699fSJohn Baldwin 
580e0f66ef8SJohn Baldwin 	if (ie != NULL) {
581e0f66ef8SJohn Baldwin 		if (!(ie->ie_flags & IE_SOFT))
5823e5da754SJohn Baldwin 			return (EINVAL);
5833e5da754SJohn Baldwin 	} else {
584e0f66ef8SJohn Baldwin 		error = intr_event_create(&ie, NULL, IE_SOFT, NULL,
585b4151f71SJohn Baldwin 		    "swi%d:", pri);
5868088699fSJohn Baldwin 		if (error)
587b4151f71SJohn Baldwin 			return (error);
588e0f66ef8SJohn Baldwin 		if (eventp != NULL)
589e0f66ef8SJohn Baldwin 			*eventp = ie;
5908088699fSJohn Baldwin 	}
591ef544f63SPaolo Pisati 	return (intr_event_add_handler(ie, name, NULL, handler, arg,
592d5a08a60SJake Burkholder 		    (pri * RQ_PPQ) + PI_SOFT, flags, cookiep));
593ed062c8dSJulian Elischer 		    /* XXKSE.. think of a better way to get separate queues */
5948088699fSJohn Baldwin }
5958088699fSJohn Baldwin 
5961931cf94SJohn Baldwin /*
597e0f66ef8SJohn Baldwin  * Schedule a software interrupt thread.
5981931cf94SJohn Baldwin  */
5991931cf94SJohn Baldwin void
600b4151f71SJohn Baldwin swi_sched(void *cookie, int flags)
6011931cf94SJohn Baldwin {
602e0f66ef8SJohn Baldwin 	struct intr_handler *ih = (struct intr_handler *)cookie;
603e0f66ef8SJohn Baldwin 	struct intr_event *ie = ih->ih_event;
6043e5da754SJohn Baldwin 	int error;
6058088699fSJohn Baldwin 
606e0f66ef8SJohn Baldwin 	CTR3(KTR_INTR, "swi_sched: %s %s need=%d", ie->ie_name, ih->ih_name,
607e0f66ef8SJohn Baldwin 	    ih->ih_need);
6081931cf94SJohn Baldwin 
6091931cf94SJohn Baldwin 	/*
6103e5da754SJohn Baldwin 	 * Set ih_need for this handler so that if the ithread is already
6113e5da754SJohn Baldwin 	 * running it will execute this handler on the next pass.  Otherwise,
6123e5da754SJohn Baldwin 	 * it will execute it the next time it runs.
6131931cf94SJohn Baldwin 	 */
614b4151f71SJohn Baldwin 	atomic_store_rel_int(&ih->ih_need, 1);
6151ca2c018SBruce Evans 
616b4151f71SJohn Baldwin 	if (!(flags & SWI_DELAY)) {
6171ca2c018SBruce Evans 		PCPU_LAZY_INC(cnt.v_soft);
618e0f66ef8SJohn Baldwin 		error = intr_event_schedule_thread(ie);
6193e5da754SJohn Baldwin 		KASSERT(error == 0, ("stray software interrupt"));
6208088699fSJohn Baldwin 	}
6218088699fSJohn Baldwin }
6228088699fSJohn Baldwin 
623fe486a37SJohn Baldwin /*
624fe486a37SJohn Baldwin  * Remove a software interrupt handler.  Currently this code does not
625fe486a37SJohn Baldwin  * remove the associated interrupt event if it becomes empty.  Calling code
626fe486a37SJohn Baldwin  * may do so manually via intr_event_destroy(), but that's not really
627fe486a37SJohn Baldwin  * an optimal interface.
628fe486a37SJohn Baldwin  */
629fe486a37SJohn Baldwin int
630fe486a37SJohn Baldwin swi_remove(void *cookie)
631fe486a37SJohn Baldwin {
632fe486a37SJohn Baldwin 
633fe486a37SJohn Baldwin 	return (intr_event_remove_handler(cookie));
634fe486a37SJohn Baldwin }
635fe486a37SJohn Baldwin 
636e0f66ef8SJohn Baldwin static void
637e0f66ef8SJohn Baldwin ithread_execute_handlers(struct proc *p, struct intr_event *ie)
638e0f66ef8SJohn Baldwin {
639e0f66ef8SJohn Baldwin 	struct intr_handler *ih, *ihn;
640e0f66ef8SJohn Baldwin 
641e0f66ef8SJohn Baldwin 	/* Interrupt handlers should not sleep. */
642e0f66ef8SJohn Baldwin 	if (!(ie->ie_flags & IE_SOFT))
643e0f66ef8SJohn Baldwin 		THREAD_NO_SLEEPING();
644e0f66ef8SJohn Baldwin 	TAILQ_FOREACH_SAFE(ih, &ie->ie_handlers, ih_next, ihn) {
645e0f66ef8SJohn Baldwin 
646e0f66ef8SJohn Baldwin 		/*
647e0f66ef8SJohn Baldwin 		 * If this handler is marked for death, remove it from
648e0f66ef8SJohn Baldwin 		 * the list of handlers and wake up the sleeper.
649e0f66ef8SJohn Baldwin 		 */
650e0f66ef8SJohn Baldwin 		if (ih->ih_flags & IH_DEAD) {
651e0f66ef8SJohn Baldwin 			mtx_lock(&ie->ie_lock);
652e0f66ef8SJohn Baldwin 			TAILQ_REMOVE(&ie->ie_handlers, ih, ih_next);
653e0f66ef8SJohn Baldwin 			ih->ih_flags &= ~IH_DEAD;
654e0f66ef8SJohn Baldwin 			wakeup(ih);
655e0f66ef8SJohn Baldwin 			mtx_unlock(&ie->ie_lock);
656e0f66ef8SJohn Baldwin 			continue;
657e0f66ef8SJohn Baldwin 		}
658e0f66ef8SJohn Baldwin 
659f2d619c8SPaolo Pisati 		/* Skip filter only handlers */
660f2d619c8SPaolo Pisati 		if (ih->ih_handler == NULL)
661f2d619c8SPaolo Pisati 			continue;
662f2d619c8SPaolo Pisati 
663e0f66ef8SJohn Baldwin 		/*
664e0f66ef8SJohn Baldwin 		 * For software interrupt threads, we only execute
665e0f66ef8SJohn Baldwin 		 * handlers that have their need flag set.  Hardware
666e0f66ef8SJohn Baldwin 		 * interrupt threads always invoke all of their handlers.
667e0f66ef8SJohn Baldwin 		 */
668e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_SOFT) {
669e0f66ef8SJohn Baldwin 			if (!ih->ih_need)
670e0f66ef8SJohn Baldwin 				continue;
671e0f66ef8SJohn Baldwin 			else
672e0f66ef8SJohn Baldwin 				atomic_store_rel_int(&ih->ih_need, 0);
673e0f66ef8SJohn Baldwin 		}
674e0f66ef8SJohn Baldwin 
675e0f66ef8SJohn Baldwin 		/* Execute this handler. */
676e0f66ef8SJohn Baldwin 		CTR6(KTR_INTR, "%s: pid %d exec %p(%p) for %s flg=%x",
677e0f66ef8SJohn Baldwin 		    __func__, p->p_pid, (void *)ih->ih_handler, ih->ih_argument,
678e0f66ef8SJohn Baldwin 		    ih->ih_name, ih->ih_flags);
679e0f66ef8SJohn Baldwin 
680e0f66ef8SJohn Baldwin 		if (!(ih->ih_flags & IH_MPSAFE))
681e0f66ef8SJohn Baldwin 			mtx_lock(&Giant);
682e0f66ef8SJohn Baldwin 		ih->ih_handler(ih->ih_argument);
683e0f66ef8SJohn Baldwin 		if (!(ih->ih_flags & IH_MPSAFE))
684e0f66ef8SJohn Baldwin 			mtx_unlock(&Giant);
685e0f66ef8SJohn Baldwin 	}
686e0f66ef8SJohn Baldwin 	if (!(ie->ie_flags & IE_SOFT))
687e0f66ef8SJohn Baldwin 		THREAD_SLEEPING_OK();
688e0f66ef8SJohn Baldwin 
689e0f66ef8SJohn Baldwin 	/*
690e0f66ef8SJohn Baldwin 	 * Interrupt storm handling:
691e0f66ef8SJohn Baldwin 	 *
692e0f66ef8SJohn Baldwin 	 * If this interrupt source is currently storming, then throttle
693e0f66ef8SJohn Baldwin 	 * it to only fire the handler once  per clock tick.
694e0f66ef8SJohn Baldwin 	 *
695e0f66ef8SJohn Baldwin 	 * If this interrupt source is not currently storming, but the
696e0f66ef8SJohn Baldwin 	 * number of back to back interrupts exceeds the storm threshold,
697e0f66ef8SJohn Baldwin 	 * then enter storming mode.
698e0f66ef8SJohn Baldwin 	 */
699e41bcf3cSJohn Baldwin 	if (intr_storm_threshold != 0 && ie->ie_count >= intr_storm_threshold &&
700e41bcf3cSJohn Baldwin 	    !(ie->ie_flags & IE_SOFT)) {
7010ae62c18SNate Lawson 		/* Report the message only once every second. */
7020ae62c18SNate Lawson 		if (ppsratecheck(&ie->ie_warntm, &ie->ie_warncnt, 1)) {
703e0f66ef8SJohn Baldwin 			printf(
7040ae62c18SNate Lawson 	"interrupt storm detected on \"%s\"; throttling interrupt source\n",
705e0f66ef8SJohn Baldwin 			    ie->ie_name);
706e0f66ef8SJohn Baldwin 		}
707e41bcf3cSJohn Baldwin 		pause("istorm", 1);
708e0f66ef8SJohn Baldwin 	} else
709e0f66ef8SJohn Baldwin 		ie->ie_count++;
710e0f66ef8SJohn Baldwin 
711e0f66ef8SJohn Baldwin 	/*
712e0f66ef8SJohn Baldwin 	 * Now that all the handlers have had a chance to run, reenable
713e0f66ef8SJohn Baldwin 	 * the interrupt source.
714e0f66ef8SJohn Baldwin 	 */
715e0f66ef8SJohn Baldwin 	if (ie->ie_enable != NULL)
716e0f66ef8SJohn Baldwin 		ie->ie_enable(ie->ie_source);
717e0f66ef8SJohn Baldwin }
718e0f66ef8SJohn Baldwin 
7198088699fSJohn Baldwin /*
720b4151f71SJohn Baldwin  * This is the main code for interrupt threads.
7218088699fSJohn Baldwin  */
72237c84183SPoul-Henning Kamp static void
723b4151f71SJohn Baldwin ithread_loop(void *arg)
7248088699fSJohn Baldwin {
725e0f66ef8SJohn Baldwin 	struct intr_thread *ithd;
726e0f66ef8SJohn Baldwin 	struct intr_event *ie;
727b40ce416SJulian Elischer 	struct thread *td;
728b4151f71SJohn Baldwin 	struct proc *p;
7298088699fSJohn Baldwin 
730b40ce416SJulian Elischer 	td = curthread;
731b40ce416SJulian Elischer 	p = td->td_proc;
732e0f66ef8SJohn Baldwin 	ithd = (struct intr_thread *)arg;
733e0f66ef8SJohn Baldwin 	KASSERT(ithd->it_thread == td,
73491f91617SDavid E. O'Brien 	    ("%s: ithread and proc linkage out of sync", __func__));
735e0f66ef8SJohn Baldwin 	ie = ithd->it_event;
736e0f66ef8SJohn Baldwin 	ie->ie_count = 0;
7378088699fSJohn Baldwin 
7388088699fSJohn Baldwin 	/*
7398088699fSJohn Baldwin 	 * As long as we have interrupts outstanding, go through the
7408088699fSJohn Baldwin 	 * list of handlers, giving each one a go at it.
7418088699fSJohn Baldwin 	 */
7428088699fSJohn Baldwin 	for (;;) {
743b4151f71SJohn Baldwin 		/*
744b4151f71SJohn Baldwin 		 * If we are an orphaned thread, then just die.
745b4151f71SJohn Baldwin 		 */
746b4151f71SJohn Baldwin 		if (ithd->it_flags & IT_DEAD) {
747e0f66ef8SJohn Baldwin 			CTR3(KTR_INTR, "%s: pid %d (%s) exiting", __func__,
748b4151f71SJohn Baldwin 			    p->p_pid, p->p_comm);
749b4151f71SJohn Baldwin 			free(ithd, M_ITHREAD);
750b4151f71SJohn Baldwin 			kthread_exit(0);
751b4151f71SJohn Baldwin 		}
752b4151f71SJohn Baldwin 
753e0f66ef8SJohn Baldwin 		/*
754e0f66ef8SJohn Baldwin 		 * Service interrupts.  If another interrupt arrives while
755e0f66ef8SJohn Baldwin 		 * we are running, it will set it_need to note that we
756e0f66ef8SJohn Baldwin 		 * should make another pass.
757e0f66ef8SJohn Baldwin 		 */
758b4151f71SJohn Baldwin 		while (ithd->it_need) {
7598088699fSJohn Baldwin 			/*
760e0f66ef8SJohn Baldwin 			 * This might need a full read and write barrier
761e0f66ef8SJohn Baldwin 			 * to make sure that this write posts before any
762e0f66ef8SJohn Baldwin 			 * of the memory or device accesses in the
763e0f66ef8SJohn Baldwin 			 * handlers.
7648088699fSJohn Baldwin 			 */
765b4151f71SJohn Baldwin 			atomic_store_rel_int(&ithd->it_need, 0);
766e0f66ef8SJohn Baldwin 			ithread_execute_handlers(p, ie);
7678088699fSJohn Baldwin 		}
7687870c3c6SJohn Baldwin 		WITNESS_WARN(WARN_PANIC, NULL, "suspending ithread");
7697870c3c6SJohn Baldwin 		mtx_assert(&Giant, MA_NOTOWNED);
7708088699fSJohn Baldwin 
7718088699fSJohn Baldwin 		/*
7728088699fSJohn Baldwin 		 * Processed all our interrupts.  Now get the sched
7738088699fSJohn Baldwin 		 * lock.  This may take a while and it_need may get
7748088699fSJohn Baldwin 		 * set again, so we have to check it again.
7758088699fSJohn Baldwin 		 */
7769ed346baSBosko Milekic 		mtx_lock_spin(&sched_lock);
777e0f66ef8SJohn Baldwin 		if (!ithd->it_need && !(ithd->it_flags & IT_DEAD)) {
7787870c3c6SJohn Baldwin 			TD_SET_IWAIT(td);
779e0f66ef8SJohn Baldwin 			ie->ie_count = 0;
780bf0acc27SJohn Baldwin 			mi_switch(SW_VOL, NULL);
7818088699fSJohn Baldwin 		}
7829ed346baSBosko Milekic 		mtx_unlock_spin(&sched_lock);
7838088699fSJohn Baldwin 	}
7841931cf94SJohn Baldwin }
7851931cf94SJohn Baldwin 
7868b201c42SJohn Baldwin #ifdef DDB
7878b201c42SJohn Baldwin /*
7888b201c42SJohn Baldwin  * Dump details about an interrupt handler
7898b201c42SJohn Baldwin  */
7908b201c42SJohn Baldwin static void
791e0f66ef8SJohn Baldwin db_dump_intrhand(struct intr_handler *ih)
7928b201c42SJohn Baldwin {
7938b201c42SJohn Baldwin 	int comma;
7948b201c42SJohn Baldwin 
7958b201c42SJohn Baldwin 	db_printf("\t%-10s ", ih->ih_name);
7968b201c42SJohn Baldwin 	switch (ih->ih_pri) {
7978b201c42SJohn Baldwin 	case PI_REALTIME:
7988b201c42SJohn Baldwin 		db_printf("CLK ");
7998b201c42SJohn Baldwin 		break;
8008b201c42SJohn Baldwin 	case PI_AV:
8018b201c42SJohn Baldwin 		db_printf("AV  ");
8028b201c42SJohn Baldwin 		break;
8038b201c42SJohn Baldwin 	case PI_TTYHIGH:
8048b201c42SJohn Baldwin 	case PI_TTYLOW:
8058b201c42SJohn Baldwin 		db_printf("TTY ");
8068b201c42SJohn Baldwin 		break;
8078b201c42SJohn Baldwin 	case PI_TAPE:
8088b201c42SJohn Baldwin 		db_printf("TAPE");
8098b201c42SJohn Baldwin 		break;
8108b201c42SJohn Baldwin 	case PI_NET:
8118b201c42SJohn Baldwin 		db_printf("NET ");
8128b201c42SJohn Baldwin 		break;
8138b201c42SJohn Baldwin 	case PI_DISK:
8148b201c42SJohn Baldwin 	case PI_DISKLOW:
8158b201c42SJohn Baldwin 		db_printf("DISK");
8168b201c42SJohn Baldwin 		break;
8178b201c42SJohn Baldwin 	case PI_DULL:
8188b201c42SJohn Baldwin 		db_printf("DULL");
8198b201c42SJohn Baldwin 		break;
8208b201c42SJohn Baldwin 	default:
8218b201c42SJohn Baldwin 		if (ih->ih_pri >= PI_SOFT)
8228b201c42SJohn Baldwin 			db_printf("SWI ");
8238b201c42SJohn Baldwin 		else
8248b201c42SJohn Baldwin 			db_printf("%4u", ih->ih_pri);
8258b201c42SJohn Baldwin 		break;
8268b201c42SJohn Baldwin 	}
8278b201c42SJohn Baldwin 	db_printf(" ");
8288b201c42SJohn Baldwin 	db_printsym((uintptr_t)ih->ih_handler, DB_STGY_PROC);
8298b201c42SJohn Baldwin 	db_printf("(%p)", ih->ih_argument);
8308b201c42SJohn Baldwin 	if (ih->ih_need ||
831ef544f63SPaolo Pisati 	    (ih->ih_flags & (IH_EXCLUSIVE | IH_ENTROPY | IH_DEAD |
8328b201c42SJohn Baldwin 	    IH_MPSAFE)) != 0) {
8338b201c42SJohn Baldwin 		db_printf(" {");
8348b201c42SJohn Baldwin 		comma = 0;
8358b201c42SJohn Baldwin 		if (ih->ih_flags & IH_EXCLUSIVE) {
8368b201c42SJohn Baldwin 			if (comma)
8378b201c42SJohn Baldwin 				db_printf(", ");
8388b201c42SJohn Baldwin 			db_printf("EXCL");
8398b201c42SJohn Baldwin 			comma = 1;
8408b201c42SJohn Baldwin 		}
8418b201c42SJohn Baldwin 		if (ih->ih_flags & IH_ENTROPY) {
8428b201c42SJohn Baldwin 			if (comma)
8438b201c42SJohn Baldwin 				db_printf(", ");
8448b201c42SJohn Baldwin 			db_printf("ENTROPY");
8458b201c42SJohn Baldwin 			comma = 1;
8468b201c42SJohn Baldwin 		}
8478b201c42SJohn Baldwin 		if (ih->ih_flags & IH_DEAD) {
8488b201c42SJohn Baldwin 			if (comma)
8498b201c42SJohn Baldwin 				db_printf(", ");
8508b201c42SJohn Baldwin 			db_printf("DEAD");
8518b201c42SJohn Baldwin 			comma = 1;
8528b201c42SJohn Baldwin 		}
8538b201c42SJohn Baldwin 		if (ih->ih_flags & IH_MPSAFE) {
8548b201c42SJohn Baldwin 			if (comma)
8558b201c42SJohn Baldwin 				db_printf(", ");
8568b201c42SJohn Baldwin 			db_printf("MPSAFE");
8578b201c42SJohn Baldwin 			comma = 1;
8588b201c42SJohn Baldwin 		}
8598b201c42SJohn Baldwin 		if (ih->ih_need) {
8608b201c42SJohn Baldwin 			if (comma)
8618b201c42SJohn Baldwin 				db_printf(", ");
8628b201c42SJohn Baldwin 			db_printf("NEED");
8638b201c42SJohn Baldwin 		}
8648b201c42SJohn Baldwin 		db_printf("}");
8658b201c42SJohn Baldwin 	}
8668b201c42SJohn Baldwin 	db_printf("\n");
8678b201c42SJohn Baldwin }
8688b201c42SJohn Baldwin 
8698b201c42SJohn Baldwin /*
870e0f66ef8SJohn Baldwin  * Dump details about a event.
8718b201c42SJohn Baldwin  */
8728b201c42SJohn Baldwin void
873e0f66ef8SJohn Baldwin db_dump_intr_event(struct intr_event *ie, int handlers)
8748b201c42SJohn Baldwin {
875e0f66ef8SJohn Baldwin 	struct intr_handler *ih;
876e0f66ef8SJohn Baldwin 	struct intr_thread *it;
8778b201c42SJohn Baldwin 	int comma;
8788b201c42SJohn Baldwin 
879e0f66ef8SJohn Baldwin 	db_printf("%s ", ie->ie_fullname);
880e0f66ef8SJohn Baldwin 	it = ie->ie_thread;
881e0f66ef8SJohn Baldwin 	if (it != NULL)
882e0f66ef8SJohn Baldwin 		db_printf("(pid %d)", it->it_thread->td_proc->p_pid);
883e0f66ef8SJohn Baldwin 	else
884e0f66ef8SJohn Baldwin 		db_printf("(no thread)");
885e0f66ef8SJohn Baldwin 	if ((ie->ie_flags & (IE_SOFT | IE_ENTROPY | IE_ADDING_THREAD)) != 0 ||
886e0f66ef8SJohn Baldwin 	    (it != NULL && it->it_need)) {
8878b201c42SJohn Baldwin 		db_printf(" {");
8888b201c42SJohn Baldwin 		comma = 0;
889e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_SOFT) {
8908b201c42SJohn Baldwin 			db_printf("SOFT");
8918b201c42SJohn Baldwin 			comma = 1;
8928b201c42SJohn Baldwin 		}
893e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_ENTROPY) {
8948b201c42SJohn Baldwin 			if (comma)
8958b201c42SJohn Baldwin 				db_printf(", ");
8968b201c42SJohn Baldwin 			db_printf("ENTROPY");
8978b201c42SJohn Baldwin 			comma = 1;
8988b201c42SJohn Baldwin 		}
899e0f66ef8SJohn Baldwin 		if (ie->ie_flags & IE_ADDING_THREAD) {
9008b201c42SJohn Baldwin 			if (comma)
9018b201c42SJohn Baldwin 				db_printf(", ");
902e0f66ef8SJohn Baldwin 			db_printf("ADDING_THREAD");
9038b201c42SJohn Baldwin 			comma = 1;
9048b201c42SJohn Baldwin 		}
905e0f66ef8SJohn Baldwin 		if (it != NULL && it->it_need) {
9068b201c42SJohn Baldwin 			if (comma)
9078b201c42SJohn Baldwin 				db_printf(", ");
9088b201c42SJohn Baldwin 			db_printf("NEED");
9098b201c42SJohn Baldwin 		}
9108b201c42SJohn Baldwin 		db_printf("}");
9118b201c42SJohn Baldwin 	}
9128b201c42SJohn Baldwin 	db_printf("\n");
9138b201c42SJohn Baldwin 
9148b201c42SJohn Baldwin 	if (handlers)
915e0f66ef8SJohn Baldwin 		TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next)
9168b201c42SJohn Baldwin 		    db_dump_intrhand(ih);
9178b201c42SJohn Baldwin }
918e0f66ef8SJohn Baldwin 
919e0f66ef8SJohn Baldwin /*
920e0f66ef8SJohn Baldwin  * Dump data about interrupt handlers
921e0f66ef8SJohn Baldwin  */
922e0f66ef8SJohn Baldwin DB_SHOW_COMMAND(intr, db_show_intr)
923e0f66ef8SJohn Baldwin {
924e0f66ef8SJohn Baldwin 	struct intr_event *ie;
92519e9205aSJohn Baldwin 	int all, verbose;
926e0f66ef8SJohn Baldwin 
927e0f66ef8SJohn Baldwin 	verbose = index(modif, 'v') != NULL;
928e0f66ef8SJohn Baldwin 	all = index(modif, 'a') != NULL;
929e0f66ef8SJohn Baldwin 	TAILQ_FOREACH(ie, &event_list, ie_list) {
930e0f66ef8SJohn Baldwin 		if (!all && TAILQ_EMPTY(&ie->ie_handlers))
931e0f66ef8SJohn Baldwin 			continue;
932e0f66ef8SJohn Baldwin 		db_dump_intr_event(ie, verbose);
93319e9205aSJohn Baldwin 		if (db_pager_quit)
93419e9205aSJohn Baldwin 			break;
935e0f66ef8SJohn Baldwin 	}
936e0f66ef8SJohn Baldwin }
9378b201c42SJohn Baldwin #endif /* DDB */
9388b201c42SJohn Baldwin 
939b4151f71SJohn Baldwin /*
9408088699fSJohn Baldwin  * Start standard software interrupt threads
9411931cf94SJohn Baldwin  */
9421931cf94SJohn Baldwin static void
943b4151f71SJohn Baldwin start_softintr(void *dummy)
9441931cf94SJohn Baldwin {
9458804bf6bSJohn Baldwin 	struct proc *p;
946b4151f71SJohn Baldwin 
947e0f66ef8SJohn Baldwin 	if (swi_add(&clk_intr_event, "clock", softclock, NULL, SWI_CLOCK,
948b4151f71SJohn Baldwin 		INTR_MPSAFE, &softclock_ih) ||
94979501b66SScott Long 	    swi_add(NULL, "vm", swi_vm, NULL, SWI_VM, INTR_MPSAFE, &vm_ih))
950b4151f71SJohn Baldwin 		panic("died while creating standard software ithreads");
9513e5da754SJohn Baldwin 
952e0f66ef8SJohn Baldwin 	p = clk_intr_event->ie_thread->it_thread->td_proc;
9538804bf6bSJohn Baldwin 	PROC_LOCK(p);
9548804bf6bSJohn Baldwin 	p->p_flag |= P_NOLOAD;
9558804bf6bSJohn Baldwin 	PROC_UNLOCK(p);
9561931cf94SJohn Baldwin }
957b4151f71SJohn Baldwin SYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL)
9581931cf94SJohn Baldwin 
959d279178dSThomas Moestl /*
960d279178dSThomas Moestl  * Sysctls used by systat and others: hw.intrnames and hw.intrcnt.
961d279178dSThomas Moestl  * The data for this machine dependent, and the declarations are in machine
962d279178dSThomas Moestl  * dependent code.  The layout of intrnames and intrcnt however is machine
963d279178dSThomas Moestl  * independent.
964d279178dSThomas Moestl  *
965d279178dSThomas Moestl  * We do not know the length of intrcnt and intrnames at compile time, so
966d279178dSThomas Moestl  * calculate things at run time.
967d279178dSThomas Moestl  */
968d279178dSThomas Moestl static int
969d279178dSThomas Moestl sysctl_intrnames(SYSCTL_HANDLER_ARGS)
970d279178dSThomas Moestl {
971d279178dSThomas Moestl 	return (sysctl_handle_opaque(oidp, intrnames, eintrnames - intrnames,
972d279178dSThomas Moestl 	   req));
973d279178dSThomas Moestl }
974d279178dSThomas Moestl 
975d279178dSThomas Moestl SYSCTL_PROC(_hw, OID_AUTO, intrnames, CTLTYPE_OPAQUE | CTLFLAG_RD,
976d279178dSThomas Moestl     NULL, 0, sysctl_intrnames, "", "Interrupt Names");
977d279178dSThomas Moestl 
978d279178dSThomas Moestl static int
979d279178dSThomas Moestl sysctl_intrcnt(SYSCTL_HANDLER_ARGS)
980d279178dSThomas Moestl {
981d279178dSThomas Moestl 	return (sysctl_handle_opaque(oidp, intrcnt,
982d279178dSThomas Moestl 	    (char *)eintrcnt - (char *)intrcnt, req));
983d279178dSThomas Moestl }
984d279178dSThomas Moestl 
985d279178dSThomas Moestl SYSCTL_PROC(_hw, OID_AUTO, intrcnt, CTLTYPE_OPAQUE | CTLFLAG_RD,
986d279178dSThomas Moestl     NULL, 0, sysctl_intrcnt, "", "Interrupt Counts");
9878b201c42SJohn Baldwin 
9888b201c42SJohn Baldwin #ifdef DDB
9898b201c42SJohn Baldwin /*
9908b201c42SJohn Baldwin  * DDB command to dump the interrupt statistics.
9918b201c42SJohn Baldwin  */
9928b201c42SJohn Baldwin DB_SHOW_COMMAND(intrcnt, db_show_intrcnt)
9938b201c42SJohn Baldwin {
9948b201c42SJohn Baldwin 	u_long *i;
9958b201c42SJohn Baldwin 	char *cp;
9968b201c42SJohn Baldwin 
9978b201c42SJohn Baldwin 	cp = intrnames;
99819e9205aSJohn Baldwin 	for (i = intrcnt; i != eintrcnt && !db_pager_quit; i++) {
9998b201c42SJohn Baldwin 		if (*cp == '\0')
10008b201c42SJohn Baldwin 			break;
10018b201c42SJohn Baldwin 		if (*i != 0)
10028b201c42SJohn Baldwin 			db_printf("%s\t%lu\n", cp, *i);
10038b201c42SJohn Baldwin 		cp += strlen(cp) + 1;
10048b201c42SJohn Baldwin 	}
10058b201c42SJohn Baldwin }
10068b201c42SJohn Baldwin #endif
1007