xref: /freebsd/sys/x86/x86/intr_machdep.c (revision 792655ab)
1646af7c6SJohn Baldwin /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3ebf5747bSPedro F. Giffuni  *
4646af7c6SJohn Baldwin  * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
5646af7c6SJohn Baldwin  *
6646af7c6SJohn Baldwin  * Redistribution and use in source and binary forms, with or without
7646af7c6SJohn Baldwin  * modification, are permitted provided that the following conditions
8646af7c6SJohn Baldwin  * are met:
9646af7c6SJohn Baldwin  * 1. Redistributions of source code must retain the above copyright
10646af7c6SJohn Baldwin  *    notice, this list of conditions and the following disclaimer.
11646af7c6SJohn Baldwin  * 2. Redistributions in binary form must reproduce the above copyright
12646af7c6SJohn Baldwin  *    notice, this list of conditions and the following disclaimer in the
13646af7c6SJohn Baldwin  *    documentation and/or other materials provided with the distribution.
14646af7c6SJohn Baldwin  *
15646af7c6SJohn Baldwin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16646af7c6SJohn Baldwin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17646af7c6SJohn Baldwin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18646af7c6SJohn Baldwin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19646af7c6SJohn Baldwin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20646af7c6SJohn Baldwin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21646af7c6SJohn Baldwin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22646af7c6SJohn Baldwin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23646af7c6SJohn Baldwin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24646af7c6SJohn Baldwin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25646af7c6SJohn Baldwin  * SUCH DAMAGE.
26646af7c6SJohn Baldwin  */
27646af7c6SJohn Baldwin 
28646af7c6SJohn Baldwin /*
29646af7c6SJohn Baldwin  * Machine dependent interrupt code for x86.  For x86, we have to
30646af7c6SJohn Baldwin  * deal with different PICs.  Thus, we use the passed in vector to lookup
31646af7c6SJohn Baldwin  * an interrupt source associated with that vector.  The interrupt source
32646af7c6SJohn Baldwin  * describes which PIC the source belongs to and includes methods to handle
33646af7c6SJohn Baldwin  * that source.
34646af7c6SJohn Baldwin  */
35646af7c6SJohn Baldwin 
36646af7c6SJohn Baldwin #include "opt_atpic.h"
37646af7c6SJohn Baldwin #include "opt_ddb.h"
38fd036deaSJohn Baldwin #include "opt_smp.h"
39646af7c6SJohn Baldwin 
40646af7c6SJohn Baldwin #include <sys/param.h>
41646af7c6SJohn Baldwin #include <sys/bus.h>
42646af7c6SJohn Baldwin #include <sys/interrupt.h>
43646af7c6SJohn Baldwin #include <sys/ktr.h>
44646af7c6SJohn Baldwin #include <sys/kernel.h>
45646af7c6SJohn Baldwin #include <sys/lock.h>
46fd036deaSJohn Baldwin #include <sys/malloc.h>
47646af7c6SJohn Baldwin #include <sys/mutex.h>
48646af7c6SJohn Baldwin #include <sys/proc.h>
49dc6a8280SConrad Meyer #include <sys/queue.h>
50dc6a8280SConrad Meyer #include <sys/sbuf.h>
51646af7c6SJohn Baldwin #include <sys/smp.h>
52b9f62e3aSSepherosa Ziehau #include <sys/sx.h>
53dc6a8280SConrad Meyer #include <sys/sysctl.h>
54646af7c6SJohn Baldwin #include <sys/syslog.h>
55646af7c6SJohn Baldwin #include <sys/systm.h>
56dc6a8280SConrad Meyer #include <sys/taskqueue.h>
579ed01c32SGleb Smirnoff #include <sys/vmmeter.h>
58646af7c6SJohn Baldwin #include <machine/clock.h>
59646af7c6SJohn Baldwin #include <machine/intr_machdep.h>
60646af7c6SJohn Baldwin #include <machine/smp.h>
61646af7c6SJohn Baldwin #ifdef DDB
62646af7c6SJohn Baldwin #include <ddb/ddb.h>
63646af7c6SJohn Baldwin #endif
64646af7c6SJohn Baldwin 
65646af7c6SJohn Baldwin #ifndef DEV_ATPIC
66646af7c6SJohn Baldwin #include <machine/segments.h>
67646af7c6SJohn Baldwin #include <machine/frame.h>
68646af7c6SJohn Baldwin #include <dev/ic/i8259.h>
69646af7c6SJohn Baldwin #include <x86/isa/icu.h>
70f79309d2SWarner Losh #include <isa/isareg.h>
71646af7c6SJohn Baldwin #endif
72646af7c6SJohn Baldwin 
7327a3c9d7SJeff Roberson #include <vm/vm.h>
7427a3c9d7SJeff Roberson 
75646af7c6SJohn Baldwin typedef void (*mask_fn)(void *);
76646af7c6SJohn Baldwin 
77646af7c6SJohn Baldwin static int intrcnt_index;
78fd036deaSJohn Baldwin static struct intsrc **interrupt_sources;
7935d87c7eSConrad Meyer #ifdef SMP
80fd036deaSJohn Baldwin static struct intsrc **interrupt_sorted;
81dc6a8280SConrad Meyer static int intrbalance;
821a352c3cSConrad Meyer SYSCTL_INT(_hw, OID_AUTO, intrbalance, CTLFLAG_RWTUN, &intrbalance, 0,
83dc6a8280SConrad Meyer     "Interrupt auto-balance interval (seconds).  Zero disables.");
84dc6a8280SConrad Meyer static struct timeout_task intrbalance_task;
8535d87c7eSConrad Meyer #endif
86b9f62e3aSSepherosa Ziehau static struct sx intrsrc_lock;
87b9f62e3aSSepherosa Ziehau static struct mtx intrpic_lock;
88646af7c6SJohn Baldwin static struct mtx intrcnt_lock;
89548b2016SAndriy Gapon static TAILQ_HEAD(pics_head, pic) pics;
90fd036deaSJohn Baldwin u_int num_io_irqs;
91646af7c6SJohn Baldwin 
92fdce57a0SJohn Baldwin #if defined(SMP) && !defined(EARLY_AP_STARTUP)
93*792655abSEd Maste #error EARLY_AP_STARTUP required on x86
94646af7c6SJohn Baldwin #endif
95646af7c6SJohn Baldwin 
965ad59b91SElliott Mitchell #define	INTRNAME_LEN	(MAXCOMLEN + 1)
97fd036deaSJohn Baldwin u_long *intrcnt;
98fd036deaSJohn Baldwin char *intrnames;
99646af7c6SJohn Baldwin size_t sintrcnt = sizeof(intrcnt);
100646af7c6SJohn Baldwin size_t sintrnames = sizeof(intrnames);
101fd036deaSJohn Baldwin int nintrcnt;
102fd036deaSJohn Baldwin 
103fd036deaSJohn Baldwin static MALLOC_DEFINE(M_INTR, "intr", "Interrupt Sources");
104646af7c6SJohn Baldwin 
105066da805SAdrian Chadd static int	intr_assign_cpu(void *arg, int cpu);
106646af7c6SJohn Baldwin static void	intr_disable_src(void *arg);
107646af7c6SJohn Baldwin static void	intr_init(void *__dummy);
108646af7c6SJohn Baldwin static int	intr_pic_registered(struct pic *pic);
109646af7c6SJohn Baldwin static void	intrcnt_setname(const char *name, int index);
110646af7c6SJohn Baldwin static void	intrcnt_updatename(struct intsrc *is);
111646af7c6SJohn Baldwin static void	intrcnt_register(struct intsrc *is);
112646af7c6SJohn Baldwin 
113fd036deaSJohn Baldwin /*
114fd036deaSJohn Baldwin  * SYSINIT levels for SI_SUB_INTR:
115fd036deaSJohn Baldwin  *
116fd036deaSJohn Baldwin  * SI_ORDER_FIRST: Initialize locks and pics TAILQ, xen_hvm_cpu_init
117fd036deaSJohn Baldwin  * SI_ORDER_SECOND: Xen PICs
118fd036deaSJohn Baldwin  * SI_ORDER_THIRD: Add I/O APIC PICs, alloc MSI and Xen IRQ ranges
119fd036deaSJohn Baldwin  * SI_ORDER_FOURTH: Add 8259A PICs
120fd036deaSJohn Baldwin  * SI_ORDER_FOURTH + 1: Finalize interrupt count and add interrupt sources
121fd036deaSJohn Baldwin  * SI_ORDER_MIDDLE: SMP interrupt counters
122fd036deaSJohn Baldwin  * SI_ORDER_ANY: Enable interrupts on BSP
123fd036deaSJohn Baldwin  */
124fd036deaSJohn Baldwin 
125646af7c6SJohn Baldwin static int
intr_pic_registered(struct pic * pic)126646af7c6SJohn Baldwin intr_pic_registered(struct pic *pic)
127646af7c6SJohn Baldwin {
128646af7c6SJohn Baldwin 	struct pic *p;
129646af7c6SJohn Baldwin 
130548b2016SAndriy Gapon 	TAILQ_FOREACH(p, &pics, pics) {
131646af7c6SJohn Baldwin 		if (p == pic)
132646af7c6SJohn Baldwin 			return (1);
133646af7c6SJohn Baldwin 	}
134646af7c6SJohn Baldwin 	return (0);
135646af7c6SJohn Baldwin }
136646af7c6SJohn Baldwin 
137646af7c6SJohn Baldwin /*
138646af7c6SJohn Baldwin  * Register a new interrupt controller (PIC).  This is to support suspend
139646af7c6SJohn Baldwin  * and resume where we suspend/resume controllers rather than individual
140646af7c6SJohn Baldwin  * sources.  This also allows controllers with no active sources (such as
141646af7c6SJohn Baldwin  * 8259As in a system using the APICs) to participate in suspend and resume.
142646af7c6SJohn Baldwin  */
143646af7c6SJohn Baldwin int
intr_register_pic(struct pic * pic)144646af7c6SJohn Baldwin intr_register_pic(struct pic *pic)
145646af7c6SJohn Baldwin {
146646af7c6SJohn Baldwin 	int error;
147646af7c6SJohn Baldwin 
148b9f62e3aSSepherosa Ziehau 	mtx_lock(&intrpic_lock);
149646af7c6SJohn Baldwin 	if (intr_pic_registered(pic))
150646af7c6SJohn Baldwin 		error = EBUSY;
151646af7c6SJohn Baldwin 	else {
152548b2016SAndriy Gapon 		TAILQ_INSERT_TAIL(&pics, pic, pics);
153646af7c6SJohn Baldwin 		error = 0;
154646af7c6SJohn Baldwin 	}
155b9f62e3aSSepherosa Ziehau 	mtx_unlock(&intrpic_lock);
156646af7c6SJohn Baldwin 	return (error);
157646af7c6SJohn Baldwin }
158646af7c6SJohn Baldwin 
159646af7c6SJohn Baldwin /*
160fd036deaSJohn Baldwin  * Allocate interrupt source arrays and register interrupt sources
161fd036deaSJohn Baldwin  * once the number of interrupts is known.
162fd036deaSJohn Baldwin  */
163fd036deaSJohn Baldwin static void
intr_init_sources(void * arg)164fd036deaSJohn Baldwin intr_init_sources(void *arg)
165fd036deaSJohn Baldwin {
166fd036deaSJohn Baldwin 	struct pic *pic;
167fd036deaSJohn Baldwin 
168fd036deaSJohn Baldwin 	MPASS(num_io_irqs > 0);
169fd036deaSJohn Baldwin 
170fd036deaSJohn Baldwin 	interrupt_sources = mallocarray(num_io_irqs, sizeof(*interrupt_sources),
171fd036deaSJohn Baldwin 	    M_INTR, M_WAITOK | M_ZERO);
172cdb6aa7eSJohn Baldwin #ifdef SMP
173fd036deaSJohn Baldwin 	interrupt_sorted = mallocarray(num_io_irqs, sizeof(*interrupt_sorted),
174fd036deaSJohn Baldwin 	    M_INTR, M_WAITOK | M_ZERO);
175cdb6aa7eSJohn Baldwin #endif
176fd036deaSJohn Baldwin 
177fd036deaSJohn Baldwin 	/*
178fd036deaSJohn Baldwin 	 * - 1 ??? dummy counter.
179fd036deaSJohn Baldwin 	 * - 2 counters for each I/O interrupt.
180fd036deaSJohn Baldwin 	 * - 1 counter for each CPU for lapic timer.
181fd036deaSJohn Baldwin 	 * - 1 counter for each CPU for the Hyper-V vmbus driver.
182fd036deaSJohn Baldwin 	 * - 8 counters for each CPU for IPI counters for SMP.
183fd036deaSJohn Baldwin 	 */
184fd036deaSJohn Baldwin 	nintrcnt = 1 + num_io_irqs * 2 + mp_ncpus * 2;
185fd036deaSJohn Baldwin #ifdef COUNT_IPIS
186fd036deaSJohn Baldwin 	if (mp_ncpus > 1)
187fd036deaSJohn Baldwin 		nintrcnt += 8 * mp_ncpus;
188fd036deaSJohn Baldwin #endif
189fd036deaSJohn Baldwin 	intrcnt = mallocarray(nintrcnt, sizeof(u_long), M_INTR, M_WAITOK |
190fd036deaSJohn Baldwin 	    M_ZERO);
1915ad59b91SElliott Mitchell 	intrnames = mallocarray(nintrcnt, INTRNAME_LEN, M_INTR, M_WAITOK |
192fd036deaSJohn Baldwin 	    M_ZERO);
193fd036deaSJohn Baldwin 	sintrcnt = nintrcnt * sizeof(u_long);
1945ad59b91SElliott Mitchell 	sintrnames = nintrcnt * INTRNAME_LEN;
195fd036deaSJohn Baldwin 
196fd036deaSJohn Baldwin 	intrcnt_setname("???", 0);
197fd036deaSJohn Baldwin 	intrcnt_index = 1;
198fd036deaSJohn Baldwin 
199fd036deaSJohn Baldwin 	/*
200fd036deaSJohn Baldwin 	 * NB: intrpic_lock is not held here to avoid LORs due to
201fd036deaSJohn Baldwin 	 * malloc() in intr_register_source().  However, we are still
202fd036deaSJohn Baldwin 	 * single-threaded at this point in startup so the list of
203fd036deaSJohn Baldwin 	 * PICs shouldn't change.
204fd036deaSJohn Baldwin 	 */
205fd036deaSJohn Baldwin 	TAILQ_FOREACH(pic, &pics, pics) {
206fd036deaSJohn Baldwin 		if (pic->pic_register_sources != NULL)
207fd036deaSJohn Baldwin 			pic->pic_register_sources(pic);
208fd036deaSJohn Baldwin 	}
209fd036deaSJohn Baldwin }
210fd036deaSJohn Baldwin SYSINIT(intr_init_sources, SI_SUB_INTR, SI_ORDER_FOURTH + 1, intr_init_sources,
211fd036deaSJohn Baldwin     NULL);
212fd036deaSJohn Baldwin 
213fd036deaSJohn Baldwin /*
214646af7c6SJohn Baldwin  * Register a new interrupt source with the global interrupt system.
215646af7c6SJohn Baldwin  * The global interrupts need to be disabled when this function is
216646af7c6SJohn Baldwin  * called.
217646af7c6SJohn Baldwin  */
218646af7c6SJohn Baldwin int
intr_register_source(struct intsrc * isrc)219646af7c6SJohn Baldwin intr_register_source(struct intsrc *isrc)
220646af7c6SJohn Baldwin {
221646af7c6SJohn Baldwin 	int error, vector;
222646af7c6SJohn Baldwin 
223646af7c6SJohn Baldwin 	KASSERT(intr_pic_registered(isrc->is_pic), ("unregistered PIC"));
224646af7c6SJohn Baldwin 	vector = isrc->is_pic->pic_vector(isrc);
225fd036deaSJohn Baldwin 	KASSERT(vector < num_io_irqs, ("IRQ %d too large (%u irqs)", vector,
226fd036deaSJohn Baldwin 	    num_io_irqs));
227646af7c6SJohn Baldwin 	if (interrupt_sources[vector] != NULL)
228646af7c6SJohn Baldwin 		return (EEXIST);
229646af7c6SJohn Baldwin 	error = intr_event_create(&isrc->is_event, isrc, 0, vector,
230646af7c6SJohn Baldwin 	    intr_disable_src, (mask_fn)isrc->is_pic->pic_enable_source,
231646af7c6SJohn Baldwin 	    (mask_fn)isrc->is_pic->pic_eoi_source, intr_assign_cpu, "irq%d:",
232646af7c6SJohn Baldwin 	    vector);
233646af7c6SJohn Baldwin 	if (error)
234646af7c6SJohn Baldwin 		return (error);
235b9f62e3aSSepherosa Ziehau 	sx_xlock(&intrsrc_lock);
236646af7c6SJohn Baldwin 	if (interrupt_sources[vector] != NULL) {
237b9f62e3aSSepherosa Ziehau 		sx_xunlock(&intrsrc_lock);
238646af7c6SJohn Baldwin 		intr_event_destroy(isrc->is_event);
239646af7c6SJohn Baldwin 		return (EEXIST);
240646af7c6SJohn Baldwin 	}
241646af7c6SJohn Baldwin 	intrcnt_register(isrc);
242646af7c6SJohn Baldwin 	interrupt_sources[vector] = isrc;
243646af7c6SJohn Baldwin 	isrc->is_handlers = 0;
244b9f62e3aSSepherosa Ziehau 	sx_xunlock(&intrsrc_lock);
245646af7c6SJohn Baldwin 	return (0);
246646af7c6SJohn Baldwin }
247646af7c6SJohn Baldwin 
248646af7c6SJohn Baldwin struct intsrc *
intr_lookup_source(int vector)249646af7c6SJohn Baldwin intr_lookup_source(int vector)
250646af7c6SJohn Baldwin {
251646af7c6SJohn Baldwin 
252fd036deaSJohn Baldwin 	if (vector < 0 || vector >= num_io_irqs)
25333099716SKonstantin Belousov 		return (NULL);
254646af7c6SJohn Baldwin 	return (interrupt_sources[vector]);
255646af7c6SJohn Baldwin }
256646af7c6SJohn Baldwin 
257646af7c6SJohn Baldwin int
intr_add_handler(const char * name,int vector,driver_filter_t filter,driver_intr_t handler,void * arg,enum intr_type flags,void ** cookiep,int domain)258646af7c6SJohn Baldwin intr_add_handler(const char *name, int vector, driver_filter_t filter,
25927a3c9d7SJeff Roberson     driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep,
26027a3c9d7SJeff Roberson     int domain)
261646af7c6SJohn Baldwin {
262646af7c6SJohn Baldwin 	struct intsrc *isrc;
263646af7c6SJohn Baldwin 	int error;
264646af7c6SJohn Baldwin 
265646af7c6SJohn Baldwin 	isrc = intr_lookup_source(vector);
266646af7c6SJohn Baldwin 	if (isrc == NULL)
267646af7c6SJohn Baldwin 		return (EINVAL);
268646af7c6SJohn Baldwin 	error = intr_event_add_handler(isrc->is_event, name, filter, handler,
269646af7c6SJohn Baldwin 	    arg, intr_priority(flags), flags, cookiep);
270646af7c6SJohn Baldwin 	if (error == 0) {
271b9f62e3aSSepherosa Ziehau 		sx_xlock(&intrsrc_lock);
272646af7c6SJohn Baldwin 		intrcnt_updatename(isrc);
273646af7c6SJohn Baldwin 		isrc->is_handlers++;
274646af7c6SJohn Baldwin 		if (isrc->is_handlers == 1) {
27527a3c9d7SJeff Roberson 			isrc->is_domain = domain;
276646af7c6SJohn Baldwin 			isrc->is_pic->pic_enable_intr(isrc);
277646af7c6SJohn Baldwin 			isrc->is_pic->pic_enable_source(isrc);
278646af7c6SJohn Baldwin 		}
279b9f62e3aSSepherosa Ziehau 		sx_xunlock(&intrsrc_lock);
280646af7c6SJohn Baldwin 	}
281646af7c6SJohn Baldwin 	return (error);
282646af7c6SJohn Baldwin }
283646af7c6SJohn Baldwin 
284646af7c6SJohn Baldwin int
intr_remove_handler(void * cookie)285646af7c6SJohn Baldwin intr_remove_handler(void *cookie)
286646af7c6SJohn Baldwin {
287646af7c6SJohn Baldwin 	struct intsrc *isrc;
28823006680SRoger Pau Monné 	int error;
289646af7c6SJohn Baldwin 
290646af7c6SJohn Baldwin 	isrc = intr_handler_source(cookie);
291646af7c6SJohn Baldwin 	error = intr_event_remove_handler(cookie);
292646af7c6SJohn Baldwin 	if (error == 0) {
293b9f62e3aSSepherosa Ziehau 		sx_xlock(&intrsrc_lock);
294646af7c6SJohn Baldwin 		isrc->is_handlers--;
295646af7c6SJohn Baldwin 		if (isrc->is_handlers == 0) {
296646af7c6SJohn Baldwin 			isrc->is_pic->pic_disable_source(isrc, PIC_NO_EOI);
297646af7c6SJohn Baldwin 			isrc->is_pic->pic_disable_intr(isrc);
298646af7c6SJohn Baldwin 		}
299646af7c6SJohn Baldwin 		intrcnt_updatename(isrc);
300b9f62e3aSSepherosa Ziehau 		sx_xunlock(&intrsrc_lock);
301646af7c6SJohn Baldwin 	}
302646af7c6SJohn Baldwin 	return (error);
303646af7c6SJohn Baldwin }
304646af7c6SJohn Baldwin 
305646af7c6SJohn Baldwin int
intr_config_intr(int vector,enum intr_trigger trig,enum intr_polarity pol)306646af7c6SJohn Baldwin intr_config_intr(int vector, enum intr_trigger trig, enum intr_polarity pol)
307646af7c6SJohn Baldwin {
308646af7c6SJohn Baldwin 	struct intsrc *isrc;
309646af7c6SJohn Baldwin 
310646af7c6SJohn Baldwin 	isrc = intr_lookup_source(vector);
311646af7c6SJohn Baldwin 	if (isrc == NULL)
312646af7c6SJohn Baldwin 		return (EINVAL);
313646af7c6SJohn Baldwin 	return (isrc->is_pic->pic_config_intr(isrc, trig, pol));
314646af7c6SJohn Baldwin }
315646af7c6SJohn Baldwin 
316646af7c6SJohn Baldwin static void
intr_disable_src(void * arg)317646af7c6SJohn Baldwin intr_disable_src(void *arg)
318646af7c6SJohn Baldwin {
319646af7c6SJohn Baldwin 	struct intsrc *isrc;
320646af7c6SJohn Baldwin 
321646af7c6SJohn Baldwin 	isrc = arg;
322646af7c6SJohn Baldwin 	isrc->is_pic->pic_disable_source(isrc, PIC_EOI);
323646af7c6SJohn Baldwin }
324646af7c6SJohn Baldwin 
325646af7c6SJohn Baldwin void
intr_execute_handlers(struct intsrc * isrc,struct trapframe * frame)326646af7c6SJohn Baldwin intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame)
327646af7c6SJohn Baldwin {
328646af7c6SJohn Baldwin 	struct intr_event *ie;
329646af7c6SJohn Baldwin 	int vector;
330646af7c6SJohn Baldwin 
331646af7c6SJohn Baldwin 	/*
332646af7c6SJohn Baldwin 	 * We count software interrupts when we process them.  The
333646af7c6SJohn Baldwin 	 * code here follows previous practice, but there's an
334646af7c6SJohn Baldwin 	 * argument for counting hardware interrupts when they're
335646af7c6SJohn Baldwin 	 * processed too.
336646af7c6SJohn Baldwin 	 */
337646af7c6SJohn Baldwin 	(*isrc->is_count)++;
33883c9dea1SGleb Smirnoff 	VM_CNT_INC(v_intr);
339646af7c6SJohn Baldwin 
340646af7c6SJohn Baldwin 	ie = isrc->is_event;
341646af7c6SJohn Baldwin 
342646af7c6SJohn Baldwin 	/*
343646af7c6SJohn Baldwin 	 * XXX: We assume that IRQ 0 is only used for the ISA timer
344646af7c6SJohn Baldwin 	 * device (clk).
345646af7c6SJohn Baldwin 	 */
346646af7c6SJohn Baldwin 	vector = isrc->is_pic->pic_vector(isrc);
347646af7c6SJohn Baldwin 	if (vector == 0)
348646af7c6SJohn Baldwin 		clkintr_pending = 1;
349646af7c6SJohn Baldwin 
350646af7c6SJohn Baldwin 	/*
351646af7c6SJohn Baldwin 	 * For stray interrupts, mask and EOI the source, bump the
352646af7c6SJohn Baldwin 	 * stray count, and log the condition.
353646af7c6SJohn Baldwin 	 */
354646af7c6SJohn Baldwin 	if (intr_event_handle(ie, frame) != 0) {
355646af7c6SJohn Baldwin 		isrc->is_pic->pic_disable_source(isrc, PIC_EOI);
356646af7c6SJohn Baldwin 		(*isrc->is_straycount)++;
357d8099e33SElliott Mitchell 		if (*isrc->is_straycount < INTR_STRAY_LOG_MAX)
358646af7c6SJohn Baldwin 			log(LOG_ERR, "stray irq%d\n", vector);
359d8099e33SElliott Mitchell 		else if (*isrc->is_straycount == INTR_STRAY_LOG_MAX)
360646af7c6SJohn Baldwin 			log(LOG_CRIT,
361646af7c6SJohn Baldwin 			    "too many stray irq %d's: not logging anymore\n",
362646af7c6SJohn Baldwin 			    vector);
363646af7c6SJohn Baldwin 	}
364646af7c6SJohn Baldwin }
365646af7c6SJohn Baldwin 
366646af7c6SJohn Baldwin void
intr_resume(bool suspend_cancelled)367428b7ca2SJustin T. Gibbs intr_resume(bool suspend_cancelled)
368646af7c6SJohn Baldwin {
369646af7c6SJohn Baldwin 	struct pic *pic;
370646af7c6SJohn Baldwin 
371646af7c6SJohn Baldwin #ifndef DEV_ATPIC
372646af7c6SJohn Baldwin 	atpic_reset();
373646af7c6SJohn Baldwin #endif
374b9f62e3aSSepherosa Ziehau 	mtx_lock(&intrpic_lock);
375548b2016SAndriy Gapon 	TAILQ_FOREACH(pic, &pics, pics) {
376646af7c6SJohn Baldwin 		if (pic->pic_resume != NULL)
377428b7ca2SJustin T. Gibbs 			pic->pic_resume(pic, suspend_cancelled);
378646af7c6SJohn Baldwin 	}
379b9f62e3aSSepherosa Ziehau 	mtx_unlock(&intrpic_lock);
380646af7c6SJohn Baldwin }
381646af7c6SJohn Baldwin 
382646af7c6SJohn Baldwin void
intr_suspend(void)383646af7c6SJohn Baldwin intr_suspend(void)
384646af7c6SJohn Baldwin {
385646af7c6SJohn Baldwin 	struct pic *pic;
386646af7c6SJohn Baldwin 
387b9f62e3aSSepherosa Ziehau 	mtx_lock(&intrpic_lock);
388548b2016SAndriy Gapon 	TAILQ_FOREACH_REVERSE(pic, &pics, pics_head, pics) {
389646af7c6SJohn Baldwin 		if (pic->pic_suspend != NULL)
390646af7c6SJohn Baldwin 			pic->pic_suspend(pic);
391646af7c6SJohn Baldwin 	}
392b9f62e3aSSepherosa Ziehau 	mtx_unlock(&intrpic_lock);
393646af7c6SJohn Baldwin }
394646af7c6SJohn Baldwin 
395646af7c6SJohn Baldwin static int
intr_assign_cpu(void * arg,int cpu)396066da805SAdrian Chadd intr_assign_cpu(void *arg, int cpu)
397646af7c6SJohn Baldwin {
398646af7c6SJohn Baldwin #ifdef SMP
399646af7c6SJohn Baldwin 	struct intsrc *isrc;
400646af7c6SJohn Baldwin 	int error;
401646af7c6SJohn Baldwin 
402fdce57a0SJohn Baldwin 	MPASS(mp_ncpus == 1 || smp_started);
403fecabb72SJohn Baldwin 
404fecabb72SJohn Baldwin 	/* Nothing to do if there is only a single CPU. */
405fecabb72SJohn Baldwin 	if (mp_ncpus > 1 && cpu != NOCPU) {
406646af7c6SJohn Baldwin 		isrc = arg;
407b9f62e3aSSepherosa Ziehau 		sx_xlock(&intrsrc_lock);
408646af7c6SJohn Baldwin 		error = isrc->is_pic->pic_assign_cpu(isrc, cpu_apic_ids[cpu]);
409dc6a8280SConrad Meyer 		if (error == 0)
410dc6a8280SConrad Meyer 			isrc->is_cpu = cpu;
411b9f62e3aSSepherosa Ziehau 		sx_xunlock(&intrsrc_lock);
412646af7c6SJohn Baldwin 	} else
413646af7c6SJohn Baldwin 		error = 0;
414646af7c6SJohn Baldwin 	return (error);
415646af7c6SJohn Baldwin #else
416646af7c6SJohn Baldwin 	return (EOPNOTSUPP);
417646af7c6SJohn Baldwin #endif
418646af7c6SJohn Baldwin }
419646af7c6SJohn Baldwin 
420646af7c6SJohn Baldwin static void
intrcnt_setname(const char * name,int index)421646af7c6SJohn Baldwin intrcnt_setname(const char *name, int index)
422646af7c6SJohn Baldwin {
423646af7c6SJohn Baldwin 
4245ad59b91SElliott Mitchell 	snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s",
4255ad59b91SElliott Mitchell 	    INTRNAME_LEN - 1, name);
426646af7c6SJohn Baldwin }
427646af7c6SJohn Baldwin 
428646af7c6SJohn Baldwin static void
intrcnt_updatename(struct intsrc * is)429646af7c6SJohn Baldwin intrcnt_updatename(struct intsrc *is)
430646af7c6SJohn Baldwin {
431646af7c6SJohn Baldwin 
432646af7c6SJohn Baldwin 	intrcnt_setname(is->is_event->ie_fullname, is->is_index);
433646af7c6SJohn Baldwin }
434646af7c6SJohn Baldwin 
435646af7c6SJohn Baldwin static void
intrcnt_register(struct intsrc * is)436646af7c6SJohn Baldwin intrcnt_register(struct intsrc *is)
437646af7c6SJohn Baldwin {
4385ad59b91SElliott Mitchell 	char straystr[INTRNAME_LEN];
439646af7c6SJohn Baldwin 
440646af7c6SJohn Baldwin 	KASSERT(is->is_event != NULL, ("%s: isrc with no event", __func__));
441646af7c6SJohn Baldwin 	mtx_lock_spin(&intrcnt_lock);
442fd036deaSJohn Baldwin 	MPASS(intrcnt_index + 2 <= nintrcnt);
443646af7c6SJohn Baldwin 	is->is_index = intrcnt_index;
444646af7c6SJohn Baldwin 	intrcnt_index += 2;
4455ad59b91SElliott Mitchell 	snprintf(straystr, sizeof(straystr), "stray irq%d",
446646af7c6SJohn Baldwin 	    is->is_pic->pic_vector(is));
447646af7c6SJohn Baldwin 	intrcnt_updatename(is);
448646af7c6SJohn Baldwin 	is->is_count = &intrcnt[is->is_index];
449646af7c6SJohn Baldwin 	intrcnt_setname(straystr, is->is_index + 1);
450646af7c6SJohn Baldwin 	is->is_straycount = &intrcnt[is->is_index + 1];
451646af7c6SJohn Baldwin 	mtx_unlock_spin(&intrcnt_lock);
452646af7c6SJohn Baldwin }
453646af7c6SJohn Baldwin 
454646af7c6SJohn Baldwin void
intrcnt_add(const char * name,u_long ** countp)455646af7c6SJohn Baldwin intrcnt_add(const char *name, u_long **countp)
456646af7c6SJohn Baldwin {
457646af7c6SJohn Baldwin 
458646af7c6SJohn Baldwin 	mtx_lock_spin(&intrcnt_lock);
459fd036deaSJohn Baldwin 	MPASS(intrcnt_index < nintrcnt);
460646af7c6SJohn Baldwin 	*countp = &intrcnt[intrcnt_index];
461646af7c6SJohn Baldwin 	intrcnt_setname(name, intrcnt_index);
462646af7c6SJohn Baldwin 	intrcnt_index++;
463646af7c6SJohn Baldwin 	mtx_unlock_spin(&intrcnt_lock);
464646af7c6SJohn Baldwin }
465646af7c6SJohn Baldwin 
466646af7c6SJohn Baldwin static void
intr_init(void * dummy __unused)467646af7c6SJohn Baldwin intr_init(void *dummy __unused)
468646af7c6SJohn Baldwin {
469646af7c6SJohn Baldwin 
470548b2016SAndriy Gapon 	TAILQ_INIT(&pics);
471b9f62e3aSSepherosa Ziehau 	mtx_init(&intrpic_lock, "intrpic", NULL, MTX_DEF);
472b9f62e3aSSepherosa Ziehau 	sx_init(&intrsrc_lock, "intrsrc");
473646af7c6SJohn Baldwin 	mtx_init(&intrcnt_lock, "intrcnt", NULL, MTX_SPIN);
474646af7c6SJohn Baldwin }
475646af7c6SJohn Baldwin SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL);
476646af7c6SJohn Baldwin 
4777a2c1d8cSJohn Baldwin static void
intr_init_final(void * dummy __unused)4787a2c1d8cSJohn Baldwin intr_init_final(void *dummy __unused)
4797a2c1d8cSJohn Baldwin {
4807a2c1d8cSJohn Baldwin 
4817a2c1d8cSJohn Baldwin 	/*
4827a2c1d8cSJohn Baldwin 	 * Enable interrupts on the BSP after all of the interrupt
4837a2c1d8cSJohn Baldwin 	 * controllers are initialized.  Device interrupts are still
4847a2c1d8cSJohn Baldwin 	 * disabled in the interrupt controllers until interrupt
4857a2c1d8cSJohn Baldwin 	 * handlers are registered.  Interrupts are enabled on each AP
4867a2c1d8cSJohn Baldwin 	 * after their first context switch.
4877a2c1d8cSJohn Baldwin 	 */
4887a2c1d8cSJohn Baldwin 	enable_intr();
4897a2c1d8cSJohn Baldwin }
4907a2c1d8cSJohn Baldwin SYSINIT(intr_init_final, SI_SUB_INTR, SI_ORDER_ANY, intr_init_final, NULL);
4917a2c1d8cSJohn Baldwin 
492646af7c6SJohn Baldwin #ifndef DEV_ATPIC
493646af7c6SJohn Baldwin /* Initialize the two 8259A's to a known-good shutdown state. */
494646af7c6SJohn Baldwin void
atpic_reset(void)495646af7c6SJohn Baldwin atpic_reset(void)
496646af7c6SJohn Baldwin {
497646af7c6SJohn Baldwin 
498646af7c6SJohn Baldwin 	outb(IO_ICU1, ICW1_RESET | ICW1_IC4);
499646af7c6SJohn Baldwin 	outb(IO_ICU1 + ICU_IMR_OFFSET, IDT_IO_INTS);
500dff207f8SYoshihiro Takahashi 	outb(IO_ICU1 + ICU_IMR_OFFSET, IRQ_MASK(ICU_SLAVEID));
501dff207f8SYoshihiro Takahashi 	outb(IO_ICU1 + ICU_IMR_OFFSET, MASTER_MODE);
502646af7c6SJohn Baldwin 	outb(IO_ICU1 + ICU_IMR_OFFSET, 0xff);
503646af7c6SJohn Baldwin 	outb(IO_ICU1, OCW3_SEL | OCW3_RR);
504646af7c6SJohn Baldwin 
505646af7c6SJohn Baldwin 	outb(IO_ICU2, ICW1_RESET | ICW1_IC4);
506646af7c6SJohn Baldwin 	outb(IO_ICU2 + ICU_IMR_OFFSET, IDT_IO_INTS + 8);
507dff207f8SYoshihiro Takahashi 	outb(IO_ICU2 + ICU_IMR_OFFSET, ICU_SLAVEID);
508dff207f8SYoshihiro Takahashi 	outb(IO_ICU2 + ICU_IMR_OFFSET, SLAVE_MODE);
509646af7c6SJohn Baldwin 	outb(IO_ICU2 + ICU_IMR_OFFSET, 0xff);
510646af7c6SJohn Baldwin 	outb(IO_ICU2, OCW3_SEL | OCW3_RR);
511646af7c6SJohn Baldwin }
512646af7c6SJohn Baldwin #endif
513646af7c6SJohn Baldwin 
514646af7c6SJohn Baldwin /* Add a description to an active interrupt handler. */
515646af7c6SJohn Baldwin int
intr_describe(u_int vector,void * ih,const char * descr)516646af7c6SJohn Baldwin intr_describe(u_int vector, void *ih, const char *descr)
517646af7c6SJohn Baldwin {
518646af7c6SJohn Baldwin 	struct intsrc *isrc;
519646af7c6SJohn Baldwin 	int error;
520646af7c6SJohn Baldwin 
521646af7c6SJohn Baldwin 	isrc = intr_lookup_source(vector);
522646af7c6SJohn Baldwin 	if (isrc == NULL)
523646af7c6SJohn Baldwin 		return (EINVAL);
524646af7c6SJohn Baldwin 	error = intr_event_describe_handler(isrc->is_event, ih, descr);
525646af7c6SJohn Baldwin 	if (error)
526646af7c6SJohn Baldwin 		return (error);
527646af7c6SJohn Baldwin 	intrcnt_updatename(isrc);
528646af7c6SJohn Baldwin 	return (0);
529646af7c6SJohn Baldwin }
530646af7c6SJohn Baldwin 
5310a110d5bSKonstantin Belousov void
intr_reprogram(void)5320a110d5bSKonstantin Belousov intr_reprogram(void)
5330a110d5bSKonstantin Belousov {
5340a110d5bSKonstantin Belousov 	struct intsrc *is;
535fd036deaSJohn Baldwin 	u_int v;
5360a110d5bSKonstantin Belousov 
537b9f62e3aSSepherosa Ziehau 	sx_xlock(&intrsrc_lock);
538fd036deaSJohn Baldwin 	for (v = 0; v < num_io_irqs; v++) {
5390a110d5bSKonstantin Belousov 		is = interrupt_sources[v];
5400a110d5bSKonstantin Belousov 		if (is == NULL)
5410a110d5bSKonstantin Belousov 			continue;
5420a110d5bSKonstantin Belousov 		if (is->is_pic->pic_reprogram_pin != NULL)
5430a110d5bSKonstantin Belousov 			is->is_pic->pic_reprogram_pin(is);
5440a110d5bSKonstantin Belousov 	}
545b9f62e3aSSepherosa Ziehau 	sx_xunlock(&intrsrc_lock);
5460a110d5bSKonstantin Belousov }
5470a110d5bSKonstantin Belousov 
548646af7c6SJohn Baldwin #ifdef DDB
549646af7c6SJohn Baldwin /*
550646af7c6SJohn Baldwin  * Dump data about interrupt handlers
551646af7c6SJohn Baldwin  */
DB_SHOW_COMMAND(irqs,db_show_irqs)552646af7c6SJohn Baldwin DB_SHOW_COMMAND(irqs, db_show_irqs)
553646af7c6SJohn Baldwin {
554646af7c6SJohn Baldwin 	struct intsrc **isrc;
555fd036deaSJohn Baldwin 	u_int i;
556fd036deaSJohn Baldwin 	int verbose;
557646af7c6SJohn Baldwin 
558646af7c6SJohn Baldwin 	if (strcmp(modif, "v") == 0)
559646af7c6SJohn Baldwin 		verbose = 1;
560646af7c6SJohn Baldwin 	else
561646af7c6SJohn Baldwin 		verbose = 0;
562646af7c6SJohn Baldwin 	isrc = interrupt_sources;
563fd036deaSJohn Baldwin 	for (i = 0; i < num_io_irqs && !db_pager_quit; i++, isrc++)
564646af7c6SJohn Baldwin 		if (*isrc != NULL)
565646af7c6SJohn Baldwin 			db_dump_intr_event((*isrc)->is_event, verbose);
566646af7c6SJohn Baldwin }
567646af7c6SJohn Baldwin #endif
568646af7c6SJohn Baldwin 
569646af7c6SJohn Baldwin #ifdef SMP
570646af7c6SJohn Baldwin /*
571646af7c6SJohn Baldwin  * Support for balancing interrupt sources across CPUs.  For now we just
572646af7c6SJohn Baldwin  * allocate CPUs round-robin.
5734258eb5aSEd Maste  *
5744258eb5aSEd Maste  * XXX If the system has a domain with without any usable CPUs (e.g., where all
5754258eb5aSEd Maste  * APIC IDs are 256 or greater and we do not have an IOMMU) we use
5764258eb5aSEd Maste  * intr_no_domain to fall back to assigning interrupts without regard for
5774258eb5aSEd Maste  * domain.  Once we can rely on the presence of an IOMMU on all x86 platforms
5784258eb5aSEd Maste  * we can revert this.
579646af7c6SJohn Baldwin  */
580646af7c6SJohn Baldwin 
5818d791e5aSJohn Baldwin cpuset_t intr_cpus = CPUSET_T_INITIALIZER(0x1);
58227a3c9d7SJeff Roberson static int current_cpu[MAXMEMDOM];
5834258eb5aSEd Maste static bool intr_no_domain;
58427a3c9d7SJeff Roberson 
58527a3c9d7SJeff Roberson static void
intr_init_cpus(void)58627a3c9d7SJeff Roberson intr_init_cpus(void)
58727a3c9d7SJeff Roberson {
58827a3c9d7SJeff Roberson 	int i;
58927a3c9d7SJeff Roberson 
59027a3c9d7SJeff Roberson 	for (i = 0; i < vm_ndomains; i++) {
5914258eb5aSEd Maste 		if (CPU_OVERLAP(&cpuset_domain[i], &intr_cpus) == 0) {
5924258eb5aSEd Maste 			intr_no_domain = true;
5934258eb5aSEd Maste 			printf("%s: unable to route interrupts to CPUs in domain %d\n",
5944258eb5aSEd Maste 			    __func__, i);
5954258eb5aSEd Maste 		}
5964258eb5aSEd Maste 
59727a3c9d7SJeff Roberson 		current_cpu[i] = 0;
5984258eb5aSEd Maste 		if (intr_no_domain && i > 0)
5994258eb5aSEd Maste 			continue;
60027a3c9d7SJeff Roberson 		if (!CPU_ISSET(current_cpu[i], &intr_cpus) ||
60127a3c9d7SJeff Roberson 		    !CPU_ISSET(current_cpu[i], &cpuset_domain[i]))
60227a3c9d7SJeff Roberson 			intr_next_cpu(i);
60327a3c9d7SJeff Roberson 	}
60427a3c9d7SJeff Roberson }
605646af7c6SJohn Baldwin 
606646af7c6SJohn Baldwin /*
607646af7c6SJohn Baldwin  * Return the CPU that the next interrupt source should use.  For now
608646af7c6SJohn Baldwin  * this just returns the next local APIC according to round-robin.
609646af7c6SJohn Baldwin  */
610646af7c6SJohn Baldwin u_int
intr_next_cpu(int domain)61127a3c9d7SJeff Roberson intr_next_cpu(int domain)
612646af7c6SJohn Baldwin {
613646af7c6SJohn Baldwin 	u_int apic_id;
614646af7c6SJohn Baldwin 
615fdce57a0SJohn Baldwin 	MPASS(mp_ncpus == 1 || smp_started);
616fecabb72SJohn Baldwin 	if (mp_ncpus == 1)
617fecabb72SJohn Baldwin 		return (PCPU_GET(apic_id));
618646af7c6SJohn Baldwin 
6194258eb5aSEd Maste 	if (intr_no_domain)
6204258eb5aSEd Maste 		domain = 0;
621646af7c6SJohn Baldwin 	mtx_lock_spin(&icu_lock);
62227a3c9d7SJeff Roberson 	apic_id = cpu_apic_ids[current_cpu[domain]];
623646af7c6SJohn Baldwin 	do {
62427a3c9d7SJeff Roberson 		current_cpu[domain]++;
62527a3c9d7SJeff Roberson 		if (current_cpu[domain] > mp_maxid)
62627a3c9d7SJeff Roberson 			current_cpu[domain] = 0;
62727a3c9d7SJeff Roberson 	} while (!CPU_ISSET(current_cpu[domain], &intr_cpus) ||
6284258eb5aSEd Maste 	    (!CPU_ISSET(current_cpu[domain], &cpuset_domain[domain]) &&
6294258eb5aSEd Maste 	    !intr_no_domain));
630646af7c6SJohn Baldwin 	mtx_unlock_spin(&icu_lock);
631646af7c6SJohn Baldwin 	return (apic_id);
632646af7c6SJohn Baldwin }
633646af7c6SJohn Baldwin 
634646af7c6SJohn Baldwin /*
635646af7c6SJohn Baldwin  * Add a CPU to our mask of valid CPUs that can be destinations of
636646af7c6SJohn Baldwin  * interrupts.
637646af7c6SJohn Baldwin  */
638646af7c6SJohn Baldwin void
intr_add_cpu(u_int cpu)639646af7c6SJohn Baldwin intr_add_cpu(u_int cpu)
640646af7c6SJohn Baldwin {
641646af7c6SJohn Baldwin 
642646af7c6SJohn Baldwin 	if (cpu >= MAXCPU)
643fe5d8f7aSEd Maste 		panic("%s: Invalid CPU ID %u", __func__, cpu);
644646af7c6SJohn Baldwin 	if (bootverbose)
645646af7c6SJohn Baldwin 		printf("INTR: Adding local APIC %d as a target\n",
646646af7c6SJohn Baldwin 		    cpu_apic_ids[cpu]);
647646af7c6SJohn Baldwin 
648646af7c6SJohn Baldwin 	CPU_SET(cpu, &intr_cpus);
649646af7c6SJohn Baldwin }
650646af7c6SJohn Baldwin 
65127a3c9d7SJeff Roberson static void
intr_smp_startup(void * arg __unused)65227a3c9d7SJeff Roberson intr_smp_startup(void *arg __unused)
65327a3c9d7SJeff Roberson {
65427a3c9d7SJeff Roberson 
65527a3c9d7SJeff Roberson 	intr_init_cpus();
65627a3c9d7SJeff Roberson 	return;
65727a3c9d7SJeff Roberson }
65827a3c9d7SJeff Roberson SYSINIT(intr_smp_startup, SI_SUB_SMP, SI_ORDER_SECOND, intr_smp_startup,
65927a3c9d7SJeff Roberson     NULL);
66027a3c9d7SJeff Roberson 
661dc6a8280SConrad Meyer /*
662dc6a8280SConrad Meyer  * TODO: Export this information in a non-MD fashion, integrate with vmstat -i.
663dc6a8280SConrad Meyer  */
664dc6a8280SConrad Meyer static int
sysctl_hw_intrs(SYSCTL_HANDLER_ARGS)665dc6a8280SConrad Meyer sysctl_hw_intrs(SYSCTL_HANDLER_ARGS)
666dc6a8280SConrad Meyer {
667dc6a8280SConrad Meyer 	struct sbuf sbuf;
668dc6a8280SConrad Meyer 	struct intsrc *isrc;
669fd036deaSJohn Baldwin 	u_int i;
670dc6a8280SConrad Meyer 	int error;
671dc6a8280SConrad Meyer 
672dc6a8280SConrad Meyer 	error = sysctl_wire_old_buffer(req, 0);
673dc6a8280SConrad Meyer 	if (error != 0)
674dc6a8280SConrad Meyer 		return (error);
675dc6a8280SConrad Meyer 
676dc6a8280SConrad Meyer 	sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
677dc6a8280SConrad Meyer 	sx_slock(&intrsrc_lock);
678fd036deaSJohn Baldwin 	for (i = 0; i < num_io_irqs; i++) {
679dc6a8280SConrad Meyer 		isrc = interrupt_sources[i];
680dc6a8280SConrad Meyer 		if (isrc == NULL)
681dc6a8280SConrad Meyer 			continue;
68227a3c9d7SJeff Roberson 		sbuf_printf(&sbuf, "%s:%d @cpu%d(domain%d): %ld\n",
683dc6a8280SConrad Meyer 		    isrc->is_event->ie_fullname,
684dc6a8280SConrad Meyer 		    isrc->is_index,
685dc6a8280SConrad Meyer 		    isrc->is_cpu,
68627a3c9d7SJeff Roberson 		    isrc->is_domain,
687dc6a8280SConrad Meyer 		    *isrc->is_count);
688dc6a8280SConrad Meyer 	}
689dc6a8280SConrad Meyer 
690dc6a8280SConrad Meyer 	sx_sunlock(&intrsrc_lock);
691dc6a8280SConrad Meyer 	error = sbuf_finish(&sbuf);
692dc6a8280SConrad Meyer 	sbuf_delete(&sbuf);
693dc6a8280SConrad Meyer 	return (error);
694dc6a8280SConrad Meyer }
6957029da5cSPawel Biernacki SYSCTL_PROC(_hw, OID_AUTO, intrs,
6967029da5cSPawel Biernacki     CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
6977029da5cSPawel Biernacki     0, 0, sysctl_hw_intrs, "A",
6987029da5cSPawel Biernacki     "interrupt:number @cpu: count");
699dc6a8280SConrad Meyer 
700dc6a8280SConrad Meyer /*
701dc6a8280SConrad Meyer  * Compare two, possibly NULL, entries in the interrupt source array
702dc6a8280SConrad Meyer  * by load.
703dc6a8280SConrad Meyer  */
704dc6a8280SConrad Meyer static int
intrcmp(const void * one,const void * two)705dc6a8280SConrad Meyer intrcmp(const void *one, const void *two)
706dc6a8280SConrad Meyer {
707dc6a8280SConrad Meyer 	const struct intsrc *i1, *i2;
708dc6a8280SConrad Meyer 
709dc6a8280SConrad Meyer 	i1 = *(const struct intsrc * const *)one;
710dc6a8280SConrad Meyer 	i2 = *(const struct intsrc * const *)two;
711dc6a8280SConrad Meyer 	if (i1 != NULL && i2 != NULL)
712dc6a8280SConrad Meyer 		return (*i1->is_count - *i2->is_count);
713dc6a8280SConrad Meyer 	if (i1 != NULL)
714dc6a8280SConrad Meyer 		return (1);
715dc6a8280SConrad Meyer 	if (i2 != NULL)
716dc6a8280SConrad Meyer 		return (-1);
717dc6a8280SConrad Meyer 	return (0);
718dc6a8280SConrad Meyer }
719dc6a8280SConrad Meyer 
720dc6a8280SConrad Meyer /*
721dc6a8280SConrad Meyer  * Balance IRQs across available CPUs according to load.
722dc6a8280SConrad Meyer  */
723dc6a8280SConrad Meyer static void
intr_balance(void * dummy __unused,int pending __unused)724dc6a8280SConrad Meyer intr_balance(void *dummy __unused, int pending __unused)
725dc6a8280SConrad Meyer {
726dc6a8280SConrad Meyer 	struct intsrc *isrc;
727dc6a8280SConrad Meyer 	int interval;
728dc6a8280SConrad Meyer 	u_int cpu;
729dc6a8280SConrad Meyer 	int i;
730dc6a8280SConrad Meyer 
731dc6a8280SConrad Meyer 	interval = intrbalance;
732dc6a8280SConrad Meyer 	if (interval == 0)
733dc6a8280SConrad Meyer 		goto out;
734dc6a8280SConrad Meyer 
735dc6a8280SConrad Meyer 	/*
736dc6a8280SConrad Meyer 	 * Sort interrupts according to count.
737dc6a8280SConrad Meyer 	 */
738dc6a8280SConrad Meyer 	sx_xlock(&intrsrc_lock);
739fd036deaSJohn Baldwin 	memcpy(interrupt_sorted, interrupt_sources, num_io_irqs *
740fd036deaSJohn Baldwin 	    sizeof(interrupt_sorted[0]));
741fd036deaSJohn Baldwin 	qsort(interrupt_sorted, num_io_irqs, sizeof(interrupt_sorted[0]),
742dc6a8280SConrad Meyer 	    intrcmp);
743dc6a8280SConrad Meyer 
744dc6a8280SConrad Meyer 	/*
745dc6a8280SConrad Meyer 	 * Restart the scan from the same location to avoid moving in the
746dc6a8280SConrad Meyer 	 * common case.
747dc6a8280SConrad Meyer 	 */
74827a3c9d7SJeff Roberson 	intr_init_cpus();
749dc6a8280SConrad Meyer 
750dc6a8280SConrad Meyer 	/*
751dc6a8280SConrad Meyer 	 * Assign round-robin from most loaded to least.
752dc6a8280SConrad Meyer 	 */
753fd036deaSJohn Baldwin 	for (i = num_io_irqs - 1; i >= 0; i--) {
754dc6a8280SConrad Meyer 		isrc = interrupt_sorted[i];
755dc6a8280SConrad Meyer 		if (isrc == NULL  || isrc->is_event->ie_cpu != NOCPU)
756dc6a8280SConrad Meyer 			continue;
75727a3c9d7SJeff Roberson 		cpu = current_cpu[isrc->is_domain];
75827a3c9d7SJeff Roberson 		intr_next_cpu(isrc->is_domain);
759dc6a8280SConrad Meyer 		if (isrc->is_cpu != cpu &&
760dc6a8280SConrad Meyer 		    isrc->is_pic->pic_assign_cpu(isrc,
761dc6a8280SConrad Meyer 		    cpu_apic_ids[cpu]) == 0)
762dc6a8280SConrad Meyer 			isrc->is_cpu = cpu;
763dc6a8280SConrad Meyer 	}
764dc6a8280SConrad Meyer 	sx_xunlock(&intrsrc_lock);
765dc6a8280SConrad Meyer out:
766dc6a8280SConrad Meyer 	taskqueue_enqueue_timeout(taskqueue_thread, &intrbalance_task,
767dc6a8280SConrad Meyer 	    interval ? hz * interval : hz * 60);
768dc6a8280SConrad Meyer 
769dc6a8280SConrad Meyer }
770dc6a8280SConrad Meyer 
771dc6a8280SConrad Meyer static void
intr_balance_init(void * dummy __unused)772dc6a8280SConrad Meyer intr_balance_init(void *dummy __unused)
773dc6a8280SConrad Meyer {
774dc6a8280SConrad Meyer 
775dc6a8280SConrad Meyer 	TIMEOUT_TASK_INIT(taskqueue_thread, &intrbalance_task, 0, intr_balance,
776dc6a8280SConrad Meyer 	    NULL);
777dc6a8280SConrad Meyer 	taskqueue_enqueue_timeout(taskqueue_thread, &intrbalance_task, hz);
778dc6a8280SConrad Meyer }
779dc6a8280SConrad Meyer SYSINIT(intr_balance_init, SI_SUB_SMP, SI_ORDER_ANY, intr_balance_init, NULL);
780dc6a8280SConrad Meyer 
781646af7c6SJohn Baldwin #else
782646af7c6SJohn Baldwin /*
783646af7c6SJohn Baldwin  * Always route interrupts to the current processor in the UP case.
784646af7c6SJohn Baldwin  */
785646af7c6SJohn Baldwin u_int
intr_next_cpu(int domain)78627a3c9d7SJeff Roberson intr_next_cpu(int domain)
787646af7c6SJohn Baldwin {
788646af7c6SJohn Baldwin 
789646af7c6SJohn Baldwin 	return (PCPU_GET(apic_id));
790646af7c6SJohn Baldwin }
791646af7c6SJohn Baldwin #endif
792