summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2003-11-03 21:25:52 +0000
committerjhb <jhb@FreeBSD.org>2003-11-03 21:25:52 +0000
commitd85aa501e2afbaac69438870bfcce9fc0d42cb75 (patch)
treebdc2c0ce1924654f98cab7e8a0ac5d1227ffce79
parent7ed7a0db1d37a705369625b8aef99f802fefbba3 (diff)
downloadFreeBSD-src-d85aa501e2afbaac69438870bfcce9fc0d42cb75.zip
FreeBSD-src-d85aa501e2afbaac69438870bfcce9fc0d42cb75.tar.gz
New device interrupt code. This defines an interrupt source abstraction
that provides methods via a PIC driver to do things like mask a source, unmask a source, enable it when the first interrupt handler is added, etc. The interrupt code provides a table of interrupt sources indexed by IRQ numbers, or vectors. These vectors are what new-bus uses for its IRQ resources and for bus_setup_intr()/bus_teardown_intr(). The interrupt code then maps that vector a given interrupt source object. When an interrupt comes in, the low-level interrupt code looks up the interrupt source for the source that triggered the interrupt and hands it off to this code to execute the appropriate handlers. By having an interrupt source abstraction, this allows us to have different types of interrupt source providers within the shared IRQ address space. For example, IRQ 0 may map to pin 0 of the master 8259A PIC, IRQs 1 through 60 may map to pins on various I/O APICs, and IRQs 120 through 128 may map to MSI interrupts for various PCI devices.
-rw-r--r--sys/amd64/amd64/intr_machdep.c296
-rw-r--r--sys/amd64/include/intr_machdep.h91
-rw-r--r--sys/i386/i386/intr_machdep.c296
-rw-r--r--sys/i386/include/intr_machdep.h91
4 files changed, 774 insertions, 0 deletions
diff --git a/sys/amd64/amd64/intr_machdep.c b/sys/amd64/amd64/intr_machdep.c
new file mode 100644
index 0000000..2a4ece4
--- /dev/null
+++ b/sys/amd64/amd64/intr_machdep.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Machine dependent interrupt code for i386. For the i386, we have to
+ * deal with different PICs. Thus, we use the passed in vector to lookup
+ * an interrupt source associated with that vector. The interrupt source
+ * describes which PIC the source belongs to and includes methods to handle
+ * that source.
+ */
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/lock.h>
+#include <sys/ktr.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <machine/intr_machdep.h>
+#ifdef DDB
+#include <ddb/ddb.h>
+#endif
+
+#define MAX_STRAY_LOG 5
+
+typedef void (*mask_fn)(int vector);
+
+static int intrcnt_index;
+static struct intsrc *interrupt_sources[NUM_IO_INTS];
+static struct mtx intr_table_lock;
+
+static void intr_init(void *__dummy);
+static void intrcnt_setname(const char *name, int index);
+static void intrcnt_updatename(struct intsrc *is);
+static void intrcnt_register(struct intsrc *is);
+
+/*
+ * Register a new interrupt source with the global interrupt system.
+ * The global interrupts need to be disabled when this function is
+ * called.
+ */
+int
+intr_register_source(struct intsrc *isrc)
+{
+ int error, vector;
+
+ vector = isrc->is_pic->pic_vector(isrc);
+ if (interrupt_sources[vector] != NULL)
+ return (EEXIST);
+ /*
+ * Ok, so this is kind of a nasty optimization that only works
+ * because sizeof(int) == sizeof(void *) on i386. If we passed
+ * in the actual vector to ithread_create and then used wrapper
+ * functions for disable_intsrc and enable_intsrc, then we'd
+ * have to go lookup in the table everytime we enabled/disabled
+ * the interrupt source. That involves looking at a lock, etc.
+ * and is just ugly. Instead, we cast the pointer to the intsrc
+ * to an int (yuck) and pass in the actual PIC methods meaning
+ * that when we enable/disable an interrupt we call the PIC
+ * methods directly.
+ */
+ error = ithread_create(&isrc->is_ithread, (intptr_t)isrc, 0,
+ (mask_fn)isrc->is_pic->pic_disable_source,
+ (mask_fn)isrc->is_pic->pic_enable_source, "irq%d:", vector);
+ if (error)
+ return (error);
+ mtx_lock_spin(&intr_table_lock);
+ if (interrupt_sources[vector] != NULL) {
+ mtx_unlock_spin(&intr_table_lock);
+ ithread_destroy(isrc->is_ithread);
+ return (EEXIST);
+ }
+ intrcnt_register(isrc);
+ interrupt_sources[vector] = isrc;
+ mtx_unlock_spin(&intr_table_lock);
+ return (0);
+}
+
+struct intsrc *
+intr_lookup_source(int vector)
+{
+
+ return (interrupt_sources[vector]);
+}
+
+int
+intr_add_handler(const char *name, int vector, driver_intr_t handler,
+ void *arg, enum intr_type flags, void **cookiep)
+{
+ struct intsrc *isrc;
+ int error;
+
+ isrc = intr_lookup_source(vector);
+ if (isrc == NULL)
+ return (EINVAL);
+ error = ithread_add_handler(isrc->is_ithread, name, handler, arg,
+ ithread_priority(flags), flags, cookiep);
+ if (error == 0) {
+ intrcnt_updatename(isrc);
+ isrc->is_pic->pic_enable_intr(isrc);
+ isrc->is_pic->pic_enable_source(isrc);
+ }
+ return (error);
+}
+
+int
+intr_remove_handler(void *cookie)
+{
+ int error;
+
+ error = ithread_remove_handler(cookie);
+#ifdef XXX
+ if (error == 0)
+ intrcnt_updatename(/* XXX */);
+#endif
+ return (error);
+}
+
+void
+intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe)
+{
+ struct ithd *it;
+ struct intrhand *ih;
+ int error, vector;
+
+ /*
+ * We count software interrupts when we process them. The
+ * code here follows previous practice, but there's an
+ * argument for counting hardware interrupts when they're
+ * processed too.
+ */
+ atomic_add_long(isrc->is_count, 1);
+ atomic_add_int(&cnt.v_intr, 1);
+
+ /*
+ * Execute fast interrupt handlers directly.
+ * To support clock handlers, if a handler registers
+ * with a NULL argument, then we pass it a pointer to
+ * a trapframe as its argument.
+ */
+ it = isrc->is_ithread;
+ ih = TAILQ_FIRST(&it->it_handlers);
+ critical_enter();
+ if (ih == NULL)
+ error = EINVAL;
+ else if (ih->ih_flags & IH_FAST) {
+ TAILQ_FOREACH(ih, &it->it_handlers, ih_next) {
+ MPASS(ih->ih_flags & IH_FAST);
+ CTR3(KTR_INTR, "%s: executing handler %p(%p)",
+ __func__, ih->ih_handler,
+ ih->ih_argument == NULL ? iframe :
+ ih->ih_argument);
+ if (ih->ih_argument == NULL)
+ ih->ih_handler(iframe);
+ else
+ ih->ih_handler(ih->ih_argument);
+ }
+ isrc->is_pic->pic_enable_source(isrc);
+ error = 0;
+ } else
+ error = ithread_schedule(it, !cold);
+ critical_exit();
+ if (error == EINVAL) {
+ vector = isrc->is_pic->pic_vector(isrc);
+ atomic_add_long(isrc->is_straycount, 1);
+ if (*isrc->is_straycount < MAX_STRAY_LOG)
+ log(LOG_ERR, "stray irq%d\n", vector);
+ else if (*isrc->is_straycount == MAX_STRAY_LOG)
+ log(LOG_CRIT,
+ "too many stray irq %d's: not logging anymore\n",
+ vector);
+ }
+}
+
+void
+intr_resume(void)
+{
+ struct intsrc **isrc;
+ int i;
+
+ mtx_lock_spin(&intr_table_lock);
+ for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++)
+ if (*isrc != NULL && (*isrc)->is_pic->pic_resume != NULL)
+ (*isrc)->is_pic->pic_resume(*isrc);
+ mtx_unlock_spin(&intr_table_lock);
+}
+
+void
+intr_suspend(void)
+{
+ struct intsrc **isrc;
+ int i;
+
+ mtx_lock_spin(&intr_table_lock);
+ for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++)
+ if (*isrc != NULL && (*isrc)->is_pic->pic_suspend != NULL)
+ (*isrc)->is_pic->pic_suspend(*isrc);
+ mtx_unlock_spin(&intr_table_lock);
+}
+
+static void
+intrcnt_setname(const char *name, int index)
+{
+
+ snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s",
+ MAXCOMLEN, name);
+}
+
+static void
+intrcnt_updatename(struct intsrc *is)
+{
+
+ intrcnt_setname(is->is_ithread->it_td->td_proc->p_comm, is->is_index);
+}
+
+static void
+intrcnt_register(struct intsrc *is)
+{
+ char straystr[MAXCOMLEN + 1];
+
+ /* mtx_assert(&intr_table_lock, MA_OWNED); */
+ KASSERT(is->is_ithread != NULL, ("%s: isrc with no ithread", __func__));
+ is->is_index = intrcnt_index;
+ intrcnt_index += 2;
+ snprintf(straystr, MAXCOMLEN + 1, "stray irq%d",
+ is->is_pic->pic_vector(is));
+ intrcnt_updatename(is);
+ is->is_count = &intrcnt[is->is_index];
+ intrcnt_setname(straystr, is->is_index + 1);
+ is->is_straycount = &intrcnt[is->is_index + 1];
+}
+
+static void
+intr_init(void *dummy __unused)
+{
+
+ intrcnt_setname("???", 0);
+ intrcnt_index = 1;
+ mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN);
+}
+SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL)
+
+#ifdef DDB
+/*
+ * Dump data about interrupt handlers
+ */
+DB_SHOW_COMMAND(irqs, db_show_irqs)
+{
+ struct intsrc **isrc;
+ int i, quit, verbose;
+
+ quit = 0;
+ if (strcmp(modif, "v") == 0)
+ verbose = 1;
+ else
+ verbose = 0;
+ isrc = interrupt_sources;
+ db_setup_paging(db_simple_pager, &quit, DB_LINES_PER_PAGE);
+ for (i = 0; i < NUM_IO_INTS && !quit; i++, isrc++)
+ if (*isrc != NULL)
+ db_dump_ithread((*isrc)->is_ithread, verbose);
+}
+#endif
diff --git a/sys/amd64/include/intr_machdep.h b/sys/amd64/include/intr_machdep.h
new file mode 100644
index 0000000..ed89186
--- /dev/null
+++ b/sys/amd64/include/intr_machdep.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __MACHINE_INTR_MACHDEP_H__
+#define __MACHINE_INTR_MACHDEP_H__
+
+#ifdef _KERNEL
+
+/* With I/O APIC's we can have up to 159 interrupts. */
+#define NUM_IO_INTS 159
+#define INTRCNT_COUNT (1 + NUM_IO_INTS * 2)
+
+#ifndef LOCORE
+
+typedef void inthand_t(u_int cs, u_int ef, u_int esp, u_int ss);
+
+#define IDTVEC(name) __CONCAT(X,name)
+
+struct intsrc;
+
+/*
+ * Methods that a PIC provides to mask/unmask a given interrupt source,
+ * "turn on" the interrupt on the CPU side by setting up an IDT entry, and
+ * return the vector associated with this source.
+ */
+struct pic {
+ void (*pic_enable_source)(struct intsrc *);
+ void (*pic_disable_source)(struct intsrc *);
+ void (*pic_eoi_source)(struct intsrc *);
+ void (*pic_enable_intr)(struct intsrc *);
+ int (*pic_vector)(struct intsrc *);
+ int (*pic_source_pending)(struct intsrc *);
+ void (*pic_suspend)(struct intsrc *);
+ void (*pic_resume)(struct intsrc *);
+};
+
+/*
+ * An interrupt source. The upper-layer code uses the PIC methods to
+ * control a given source. The lower-layer PIC drivers can store additional
+ * private data in a given interrupt source such as an interrupt pin number
+ * or an I/O APIC pointer.
+ */
+struct intsrc {
+ struct pic *is_pic;
+ struct ithd *is_ithread;
+ u_long *is_count;
+ u_long *is_straycount;
+ u_int is_index;
+};
+
+struct intrframe;
+
+extern struct mtx icu_lock;
+
+int intr_add_handler(const char *name, int vector, driver_intr_t handler,
+ void *arg, enum intr_type flags, void **cookiep);
+void intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe);
+struct intsrc *intr_lookup_source(int vector);
+int intr_register_source(struct intsrc *isrc);
+int intr_remove_handler(void *cookie);
+void intr_resume(void);
+void intr_suspend(void);
+
+#endif /* !LOCORE */
+#endif /* _KERNEL */
+#endif /* !__MACHINE_INTR_MACHDEP_H__ */
diff --git a/sys/i386/i386/intr_machdep.c b/sys/i386/i386/intr_machdep.c
new file mode 100644
index 0000000..2a4ece4
--- /dev/null
+++ b/sys/i386/i386/intr_machdep.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Machine dependent interrupt code for i386. For the i386, we have to
+ * deal with different PICs. Thus, we use the passed in vector to lookup
+ * an interrupt source associated with that vector. The interrupt source
+ * describes which PIC the source belongs to and includes methods to handle
+ * that source.
+ */
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/lock.h>
+#include <sys/ktr.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <machine/intr_machdep.h>
+#ifdef DDB
+#include <ddb/ddb.h>
+#endif
+
+#define MAX_STRAY_LOG 5
+
+typedef void (*mask_fn)(int vector);
+
+static int intrcnt_index;
+static struct intsrc *interrupt_sources[NUM_IO_INTS];
+static struct mtx intr_table_lock;
+
+static void intr_init(void *__dummy);
+static void intrcnt_setname(const char *name, int index);
+static void intrcnt_updatename(struct intsrc *is);
+static void intrcnt_register(struct intsrc *is);
+
+/*
+ * Register a new interrupt source with the global interrupt system.
+ * The global interrupts need to be disabled when this function is
+ * called.
+ */
+int
+intr_register_source(struct intsrc *isrc)
+{
+ int error, vector;
+
+ vector = isrc->is_pic->pic_vector(isrc);
+ if (interrupt_sources[vector] != NULL)
+ return (EEXIST);
+ /*
+ * Ok, so this is kind of a nasty optimization that only works
+ * because sizeof(int) == sizeof(void *) on i386. If we passed
+ * in the actual vector to ithread_create and then used wrapper
+ * functions for disable_intsrc and enable_intsrc, then we'd
+ * have to go lookup in the table everytime we enabled/disabled
+ * the interrupt source. That involves looking at a lock, etc.
+ * and is just ugly. Instead, we cast the pointer to the intsrc
+ * to an int (yuck) and pass in the actual PIC methods meaning
+ * that when we enable/disable an interrupt we call the PIC
+ * methods directly.
+ */
+ error = ithread_create(&isrc->is_ithread, (intptr_t)isrc, 0,
+ (mask_fn)isrc->is_pic->pic_disable_source,
+ (mask_fn)isrc->is_pic->pic_enable_source, "irq%d:", vector);
+ if (error)
+ return (error);
+ mtx_lock_spin(&intr_table_lock);
+ if (interrupt_sources[vector] != NULL) {
+ mtx_unlock_spin(&intr_table_lock);
+ ithread_destroy(isrc->is_ithread);
+ return (EEXIST);
+ }
+ intrcnt_register(isrc);
+ interrupt_sources[vector] = isrc;
+ mtx_unlock_spin(&intr_table_lock);
+ return (0);
+}
+
+struct intsrc *
+intr_lookup_source(int vector)
+{
+
+ return (interrupt_sources[vector]);
+}
+
+int
+intr_add_handler(const char *name, int vector, driver_intr_t handler,
+ void *arg, enum intr_type flags, void **cookiep)
+{
+ struct intsrc *isrc;
+ int error;
+
+ isrc = intr_lookup_source(vector);
+ if (isrc == NULL)
+ return (EINVAL);
+ error = ithread_add_handler(isrc->is_ithread, name, handler, arg,
+ ithread_priority(flags), flags, cookiep);
+ if (error == 0) {
+ intrcnt_updatename(isrc);
+ isrc->is_pic->pic_enable_intr(isrc);
+ isrc->is_pic->pic_enable_source(isrc);
+ }
+ return (error);
+}
+
+int
+intr_remove_handler(void *cookie)
+{
+ int error;
+
+ error = ithread_remove_handler(cookie);
+#ifdef XXX
+ if (error == 0)
+ intrcnt_updatename(/* XXX */);
+#endif
+ return (error);
+}
+
+void
+intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe)
+{
+ struct ithd *it;
+ struct intrhand *ih;
+ int error, vector;
+
+ /*
+ * We count software interrupts when we process them. The
+ * code here follows previous practice, but there's an
+ * argument for counting hardware interrupts when they're
+ * processed too.
+ */
+ atomic_add_long(isrc->is_count, 1);
+ atomic_add_int(&cnt.v_intr, 1);
+
+ /*
+ * Execute fast interrupt handlers directly.
+ * To support clock handlers, if a handler registers
+ * with a NULL argument, then we pass it a pointer to
+ * a trapframe as its argument.
+ */
+ it = isrc->is_ithread;
+ ih = TAILQ_FIRST(&it->it_handlers);
+ critical_enter();
+ if (ih == NULL)
+ error = EINVAL;
+ else if (ih->ih_flags & IH_FAST) {
+ TAILQ_FOREACH(ih, &it->it_handlers, ih_next) {
+ MPASS(ih->ih_flags & IH_FAST);
+ CTR3(KTR_INTR, "%s: executing handler %p(%p)",
+ __func__, ih->ih_handler,
+ ih->ih_argument == NULL ? iframe :
+ ih->ih_argument);
+ if (ih->ih_argument == NULL)
+ ih->ih_handler(iframe);
+ else
+ ih->ih_handler(ih->ih_argument);
+ }
+ isrc->is_pic->pic_enable_source(isrc);
+ error = 0;
+ } else
+ error = ithread_schedule(it, !cold);
+ critical_exit();
+ if (error == EINVAL) {
+ vector = isrc->is_pic->pic_vector(isrc);
+ atomic_add_long(isrc->is_straycount, 1);
+ if (*isrc->is_straycount < MAX_STRAY_LOG)
+ log(LOG_ERR, "stray irq%d\n", vector);
+ else if (*isrc->is_straycount == MAX_STRAY_LOG)
+ log(LOG_CRIT,
+ "too many stray irq %d's: not logging anymore\n",
+ vector);
+ }
+}
+
+void
+intr_resume(void)
+{
+ struct intsrc **isrc;
+ int i;
+
+ mtx_lock_spin(&intr_table_lock);
+ for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++)
+ if (*isrc != NULL && (*isrc)->is_pic->pic_resume != NULL)
+ (*isrc)->is_pic->pic_resume(*isrc);
+ mtx_unlock_spin(&intr_table_lock);
+}
+
+void
+intr_suspend(void)
+{
+ struct intsrc **isrc;
+ int i;
+
+ mtx_lock_spin(&intr_table_lock);
+ for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++)
+ if (*isrc != NULL && (*isrc)->is_pic->pic_suspend != NULL)
+ (*isrc)->is_pic->pic_suspend(*isrc);
+ mtx_unlock_spin(&intr_table_lock);
+}
+
+static void
+intrcnt_setname(const char *name, int index)
+{
+
+ snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s",
+ MAXCOMLEN, name);
+}
+
+static void
+intrcnt_updatename(struct intsrc *is)
+{
+
+ intrcnt_setname(is->is_ithread->it_td->td_proc->p_comm, is->is_index);
+}
+
+static void
+intrcnt_register(struct intsrc *is)
+{
+ char straystr[MAXCOMLEN + 1];
+
+ /* mtx_assert(&intr_table_lock, MA_OWNED); */
+ KASSERT(is->is_ithread != NULL, ("%s: isrc with no ithread", __func__));
+ is->is_index = intrcnt_index;
+ intrcnt_index += 2;
+ snprintf(straystr, MAXCOMLEN + 1, "stray irq%d",
+ is->is_pic->pic_vector(is));
+ intrcnt_updatename(is);
+ is->is_count = &intrcnt[is->is_index];
+ intrcnt_setname(straystr, is->is_index + 1);
+ is->is_straycount = &intrcnt[is->is_index + 1];
+}
+
+static void
+intr_init(void *dummy __unused)
+{
+
+ intrcnt_setname("???", 0);
+ intrcnt_index = 1;
+ mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN);
+}
+SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL)
+
+#ifdef DDB
+/*
+ * Dump data about interrupt handlers
+ */
+DB_SHOW_COMMAND(irqs, db_show_irqs)
+{
+ struct intsrc **isrc;
+ int i, quit, verbose;
+
+ quit = 0;
+ if (strcmp(modif, "v") == 0)
+ verbose = 1;
+ else
+ verbose = 0;
+ isrc = interrupt_sources;
+ db_setup_paging(db_simple_pager, &quit, DB_LINES_PER_PAGE);
+ for (i = 0; i < NUM_IO_INTS && !quit; i++, isrc++)
+ if (*isrc != NULL)
+ db_dump_ithread((*isrc)->is_ithread, verbose);
+}
+#endif
diff --git a/sys/i386/include/intr_machdep.h b/sys/i386/include/intr_machdep.h
new file mode 100644
index 0000000..ed89186
--- /dev/null
+++ b/sys/i386/include/intr_machdep.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __MACHINE_INTR_MACHDEP_H__
+#define __MACHINE_INTR_MACHDEP_H__
+
+#ifdef _KERNEL
+
+/* With I/O APIC's we can have up to 159 interrupts. */
+#define NUM_IO_INTS 159
+#define INTRCNT_COUNT (1 + NUM_IO_INTS * 2)
+
+#ifndef LOCORE
+
+typedef void inthand_t(u_int cs, u_int ef, u_int esp, u_int ss);
+
+#define IDTVEC(name) __CONCAT(X,name)
+
+struct intsrc;
+
+/*
+ * Methods that a PIC provides to mask/unmask a given interrupt source,
+ * "turn on" the interrupt on the CPU side by setting up an IDT entry, and
+ * return the vector associated with this source.
+ */
+struct pic {
+ void (*pic_enable_source)(struct intsrc *);
+ void (*pic_disable_source)(struct intsrc *);
+ void (*pic_eoi_source)(struct intsrc *);
+ void (*pic_enable_intr)(struct intsrc *);
+ int (*pic_vector)(struct intsrc *);
+ int (*pic_source_pending)(struct intsrc *);
+ void (*pic_suspend)(struct intsrc *);
+ void (*pic_resume)(struct intsrc *);
+};
+
+/*
+ * An interrupt source. The upper-layer code uses the PIC methods to
+ * control a given source. The lower-layer PIC drivers can store additional
+ * private data in a given interrupt source such as an interrupt pin number
+ * or an I/O APIC pointer.
+ */
+struct intsrc {
+ struct pic *is_pic;
+ struct ithd *is_ithread;
+ u_long *is_count;
+ u_long *is_straycount;
+ u_int is_index;
+};
+
+struct intrframe;
+
+extern struct mtx icu_lock;
+
+int intr_add_handler(const char *name, int vector, driver_intr_t handler,
+ void *arg, enum intr_type flags, void **cookiep);
+void intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe);
+struct intsrc *intr_lookup_source(int vector);
+int intr_register_source(struct intsrc *isrc);
+int intr_remove_handler(void *cookie);
+void intr_resume(void);
+void intr_suspend(void);
+
+#endif /* !LOCORE */
+#endif /* _KERNEL */
+#endif /* !__MACHINE_INTR_MACHDEP_H__ */
OpenPOWER on IntegriCloud