Index: head/sys/alpha/alpha/interrupt.c =================================================================== --- head/sys/alpha/alpha/interrupt.c (revision 132407) +++ head/sys/alpha/alpha/interrupt.c (revision 132408) @@ -1,496 +1,498 @@ /* $NetBSD: interrupt.c,v 1.23 1998/02/24 07:38:01 thorpej Exp $ */ /* * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Authors: Keith Bostic, Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ /* * Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center. * Redistribute and modify at will, leaving only this additional copyright * notice. */ #include /* RCS ID & Copyright macro defns */ /* __KERNEL_RCSID(0, "$NetBSD: interrupt.c,v 1.23 1998/02/24 07:38:01 thorpej Exp $");*/ __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EVCNT_COUNTERS struct evcnt clock_intr_evcnt; /* event counter for clock intrs. */ #else #include #endif volatile int mc_expected, mc_received; static void dummy_perf(unsigned long vector, struct trapframe *framep) { printf("performance interrupt!\n"); } void (*perf_irq)(unsigned long, struct trapframe *) = dummy_perf; static u_int schedclk2; static void alpha_clock_interrupt(struct trapframe *framep); void interrupt(a0, a1, a2, framep) unsigned long a0, a1, a2; struct trapframe *framep; { struct thread *td; #ifdef SMP register_t s; #endif /* * Find our per-cpu globals. */ #ifdef SMP s = intr_disable(); #endif pcpup = (struct pcpu *) alpha_pal_rdval(); td = curthread; #ifdef SMP td->td_md.md_kernnest++; intr_restore(s); #endif atomic_add_int(&td->td_intr_nesting_level, 1); #if KSTACK_GUARD_PAGES == 0 #ifndef SMP { if ((caddr_t) framep < (caddr_t) td->td_pcb + 1024) { panic("possible stack overflow\n"); } } #endif #endif framep->tf_regs[FRAME_TRAPARG_A0] = a0; framep->tf_regs[FRAME_TRAPARG_A1] = a1; framep->tf_regs[FRAME_TRAPARG_A2] = a2; switch (a0) { #ifdef SMP case ALPHA_INTR_XPROC: /* interprocessor interrupt */ CTR0(KTR_INTR|KTR_SMP, "interprocessor interrupt"); smp_handle_ipi(framep); /* note: lock not taken */ break; #endif case ALPHA_INTR_CLOCK: /* clock interrupt */ CTR0(KTR_INTR, "clock interrupt"); alpha_clock_interrupt(framep); break; case ALPHA_INTR_ERROR: /* Machine Check or Correctable Error */ a0 = alpha_pal_rdmces(); if (platform.mcheck_handler) (*platform.mcheck_handler)(a0, framep, a1, a2); else machine_check(a0, framep, a1, a2); break; case ALPHA_INTR_DEVICE: /* I/O device interrupt */ cnt.v_intr++; if (platform.iointr) (*platform.iointr)(framep, a1); break; case ALPHA_INTR_PERF: /* interprocessor interrupt */ perf_irq(a1, framep); break; case ALPHA_INTR_PASSIVE: #if 0 printf("passive release interrupt vec 0x%lx (ignoring)\n", a1); #endif break; default: panic("unexpected interrupt: type 0x%lx vec 0x%lx a2 0x%lx\n", a0, a1, a2); /* NOTREACHED */ } atomic_subtract_int(&td->td_intr_nesting_level, 1); } void set_iointr(niointr) void (*niointr)(void *, unsigned long); { if (platform.iointr) panic("set iointr twice"); platform.iointr = niointr; } void machine_check(mces, framep, vector, param) unsigned long mces; struct trapframe *framep; unsigned long vector, param; { const char *type; /* Make sure it's an error we know about. */ if ((mces & (ALPHA_MCES_MIP|ALPHA_MCES_SCE|ALPHA_MCES_PCE)) == 0) { type = "fatal machine check or error (unknown type)"; goto fatal; } /* Machine checks. */ if (mces & ALPHA_MCES_MIP) { /* If we weren't expecting it, then we punt. */ if (!mc_expected) { type = "unexpected machine check"; goto fatal; } mc_expected = 0; mc_received = 1; } /* System correctable errors. */ if (mces & ALPHA_MCES_SCE) printf("Warning: received system correctable error.\n"); /* Processor correctable errors. */ if (mces & ALPHA_MCES_PCE) printf("Warning: received processor correctable error.\n"); /* Clear pending machine checks and correctable errors */ alpha_pal_wrmces(mces); return; fatal: /* Clear pending machine checks and correctable errors */ alpha_pal_wrmces(mces); printf("\n"); printf("%s:\n", type); printf("\n"); printf(" mces = 0x%lx\n", mces); printf(" vector = 0x%lx\n", vector); printf(" param = 0x%lx\n", param); printf(" pc = 0x%lx\n", framep->tf_regs[FRAME_PC]); printf(" ra = 0x%lx\n", framep->tf_regs[FRAME_RA]); printf(" curproc = %p\n", curproc); if (curproc != NULL) printf(" pid = %d, comm = %s\n", curproc->p_pid, curproc->p_comm); printf("\n"); #ifdef KDB kdb_trap(ALPHA_KENTRY_MM, mces, framep); #endif panic("machine check"); } int badaddr(addr, size) void *addr; size_t size; { return(badaddr_read(addr, size, NULL)); } int badaddr_read(addr, size, rptr) void *addr; size_t size; void *rptr; { long rcpt; /* Get rid of any stale machine checks that have been waiting. */ alpha_pal_draina(); /* Tell the trap code to expect a machine check. */ mc_received = 0; mc_expected = 1; /* Read from the test address, and make sure the read happens. */ alpha_mb(); switch (size) { case sizeof (u_int8_t): if (alpha_implver() >= ALPHA_IMPLVER_EV5 && alpha_amask(ALPHA_AMASK_BWX) == 0) rcpt = ldbu((vm_offset_t)addr); else rcpt = *(volatile u_int8_t *)addr; break; case sizeof (u_int16_t): if (alpha_implver() >= ALPHA_IMPLVER_EV5 && alpha_amask(ALPHA_AMASK_BWX) == 0) rcpt = ldwu((vm_offset_t)addr); else rcpt = *(volatile u_int16_t *)addr; break; case sizeof (u_int32_t): rcpt = *(volatile u_int32_t *)addr; break; case sizeof (u_int64_t): rcpt = *(volatile u_int64_t *)addr; break; default: panic("badaddr: invalid size (%ld)\n", size); } alpha_mb(); alpha_mb(); /* magic for ev5 2100A & maybe more */ /* Make sure we took the machine check, if we caused one. */ alpha_pal_draina(); /* disallow further machine checks */ mc_expected = 0; if (rptr && mc_received == 0) { switch (size) { case sizeof (u_int8_t): *(volatile u_int8_t *)rptr = rcpt; break; case sizeof (u_int16_t): *(volatile u_int16_t *)rptr = rcpt; break; case sizeof (u_int32_t): *(volatile u_int32_t *)rptr = rcpt; break; case sizeof (u_int64_t): *(volatile u_int64_t *)rptr = rcpt; break; } } /* Return non-zero (i.e. true) if it's a bad address. */ return (mc_received); } #define HASHVEC(vector) ((vector) % 31) LIST_HEAD(alpha_intr_list, alpha_intr); struct alpha_intr { LIST_ENTRY(alpha_intr) list; /* chain handlers in this hash bucket */ uintptr_t vector; /* vector to match */ struct ithd *ithd; /* interrupt thread */ volatile long *cntp; /* interrupt counter */ }; static struct mtx alpha_intr_hash_lock; static struct alpha_intr_list alpha_intr_hash[31]; static void ithds_init(void *dummy); static void ithds_init(void *dummy) { mtx_init(&alpha_intr_hash_lock, "ithread table lock", NULL, MTX_SPIN); } SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL); int alpha_setup_intr(const char *name, uintptr_t vector, driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep, volatile long *cntp, void (*disable)(uintptr_t), void (*enable)(uintptr_t)) { int h = HASHVEC(vector); struct alpha_intr *i; int errcode; /* * XXX - Can we have more than one device on a vector? If so, we have * a race condition here that needs to be worked around similar to * the fashion done in the i386 inthand_add() function. */ /* First, check for an existing hash table entry for this vector. */ mtx_lock_spin(&alpha_intr_hash_lock); for (i = LIST_FIRST(&alpha_intr_hash[h]); i && i->vector != vector; i = LIST_NEXT(i, list)) ; /* nothing */ mtx_unlock_spin(&alpha_intr_hash_lock); if (i == NULL) { /* None was found, so create an entry. */ i = malloc(sizeof(struct alpha_intr), M_DEVBUF, M_NOWAIT); if (i == NULL) return ENOMEM; i->vector = vector; i->cntp = cntp; errcode = ithread_create(&i->ithd, vector, 0, disable, enable, "intr:"); if (errcode) { free(i, M_DEVBUF); return errcode; } mtx_lock_spin(&alpha_intr_hash_lock); LIST_INSERT_HEAD(&alpha_intr_hash[h], i, list); mtx_unlock_spin(&alpha_intr_hash_lock); } /* Second, add this handler. */ return (ithread_add_handler(i->ithd, name, handler, arg, ithread_priority(flags), flags, cookiep)); } int alpha_teardown_intr(void *cookie) { return (ithread_remove_handler(cookie)); } void alpha_dispatch_intr(void *frame, unsigned long vector) { int h = HASHVEC(vector); struct alpha_intr *i; struct ithd *ithd; /* our interrupt thread */ struct intrhand *ih; int error; /* * Walk the hash bucket for this vector looking for this vector's * interrupt thread. */ for (i = LIST_FIRST(&alpha_intr_hash[h]); i && i->vector != vector; i = LIST_NEXT(i, list)) ; /* nothing */ if (i == NULL) return; /* no ithread for this vector */ ithd = i->ithd; KASSERT(ithd != NULL, ("interrupt vector without a thread")); /* * As an optimization, if an ithread has no handlers, don't * schedule it to run. */ if (TAILQ_EMPTY(&ithd->it_handlers)) return; atomic_add_long(i->cntp, 1); /* * Handle a fast interrupt if there is no actual thread for this * interrupt by calling the handler directly without Giant. Note * that this means that any fast interrupt handler must be MP safe. */ ih = TAILQ_FIRST(&ithd->it_handlers); if ((ih->ih_flags & IH_FAST) != 0) { critical_enter(); ih->ih_handler(ih->ih_argument); + /* XXX */ + td->td_pflags &= ~TDP_OWEPREEMPT; critical_exit(); return; } if (ithd->it_disable) { CTR1(KTR_INTR, "alpha_dispatch_intr: disabling vector 0x%x", i->vector); ithd->it_disable(ithd->it_vector); } /* * It seems that we need to return from an interrupt back to PAL * on the same CPU that received the interrupt, so pin the interrupted * thread to the current CPU until we return from the interrupt. */ sched_pin(); error = ithread_schedule(ithd); KASSERT(error == 0, ("got an impossible stray interrupt")); sched_unpin(); } static void alpha_clock_interrupt(struct trapframe *framep) { cnt.v_intr++; #ifdef EVCNT_COUNTERS clock_intr_evcnt.ev_count++; #else intrcnt[INTRCNT_CLOCK]++; #endif if (platform.clockintr) { critical_enter(); #ifdef SMP /* * Only one processor drives the actual timer. */ if (PCPU_GET(cpuid) == boot_cpu_id) { #endif (*platform.clockintr)(framep); /* divide hz (1024) by 8 to get stathz (128) */ if ((++schedclk2 & 0x7) == 0) { if (profprocs != 0) profclock((struct clockframe *)framep); statclock((struct clockframe *)framep); } #ifdef SMP } else { hardclock_process((struct clockframe *)framep); if ((schedclk2 & 0x7) == 0) { if (profprocs != 0) profclock((struct clockframe *)framep); statclock((struct clockframe *)framep); } } #endif critical_exit(); } } Index: head/sys/amd64/amd64/intr_machdep.c =================================================================== --- head/sys/amd64/amd64/intr_machdep.c (revision 132407) +++ head/sys/amd64/amd64/intr_machdep.c (revision 132408) @@ -1,320 +1,322 @@ /*- * Copyright (c) 2003 John Baldwin * 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 #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif #define MAX_STRAY_LOG 5 typedef void (*mask_fn)(uintptr_t 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); error = ithread_create(&isrc->is_ithread, (uintptr_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); } int intr_config_intr(int vector, enum intr_trigger trig, enum intr_polarity pol) { struct intsrc *isrc; isrc = intr_lookup_source(vector); if (isrc == NULL) return (EINVAL); return (isrc->is_pic->pic_config_intr(isrc, trig, pol)); } void intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe) { struct thread *td; struct ithd *it; struct intrhand *ih; int error, vector; td = curthread; td->td_intr_nesting_level++; /* * 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); it = isrc->is_ithread; if (it == NULL) ih = NULL; else ih = TAILQ_FIRST(&it->it_handlers); /* * XXX: We assume that IRQ 0 is only used for the ISA timer * device (clk). */ vector = isrc->is_pic->pic_vector(isrc); if (vector == 0) clkintr_pending = 1; if (ih != NULL && ih->ih_flags & IH_FAST) { /* * 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. */ critical_enter(); 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_eoi_source(isrc); error = 0; + /* XXX */ + td->td_pflags &= ~TDP_OWEPREEMPT; critical_exit(); } else { /* * For stray and threaded interrupts, we mask and EOI the * source. */ isrc->is_pic->pic_disable_source(isrc); isrc->is_pic->pic_eoi_source(isrc); if (ih == NULL) error = EINVAL; else error = ithread_schedule(it); } if (error == EINVAL) { 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); } td->td_intr_nesting_level--; } 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 Index: head/sys/i386/i386/intr_machdep.c =================================================================== --- head/sys/i386/i386/intr_machdep.c (revision 132407) +++ head/sys/i386/i386/intr_machdep.c (revision 132408) @@ -1,320 +1,322 @@ /*- * Copyright (c) 2003 John Baldwin * 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 #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif #define MAX_STRAY_LOG 5 typedef void (*mask_fn)(uintptr_t 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); error = ithread_create(&isrc->is_ithread, (uintptr_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); } int intr_config_intr(int vector, enum intr_trigger trig, enum intr_polarity pol) { struct intsrc *isrc; isrc = intr_lookup_source(vector); if (isrc == NULL) return (EINVAL); return (isrc->is_pic->pic_config_intr(isrc, trig, pol)); } void intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe) { struct thread *td; struct ithd *it; struct intrhand *ih; int error, vector; td = curthread; td->td_intr_nesting_level++; /* * 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); it = isrc->is_ithread; if (it == NULL) ih = NULL; else ih = TAILQ_FIRST(&it->it_handlers); /* * XXX: We assume that IRQ 0 is only used for the ISA timer * device (clk). */ vector = isrc->is_pic->pic_vector(isrc); if (vector == 0) clkintr_pending = 1; if (ih != NULL && ih->ih_flags & IH_FAST) { /* * 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. */ critical_enter(); 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_eoi_source(isrc); error = 0; + /* XXX */ + td->td_pflags &= ~TDP_OWEPREEMPT; critical_exit(); } else { /* * For stray and threaded interrupts, we mask and EOI the * source. */ isrc->is_pic->pic_disable_source(isrc); isrc->is_pic->pic_eoi_source(isrc); if (ih == NULL) error = EINVAL; else error = ithread_schedule(it); } if (error == EINVAL) { 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); } td->td_intr_nesting_level--; } 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