diff options
author | jhb <jhb@FreeBSD.org> | 2001-02-09 17:47:44 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2001-02-09 17:47:44 +0000 |
commit | a0b05a52af8750d38933ae348f2f0e67135df35f (patch) | |
tree | 63226881d3381798f464e06aa971474f6cf1c5b6 | |
parent | b30904d8405e0fc49c1043759a015628e89a2a2c (diff) | |
download | FreeBSD-src-a0b05a52af8750d38933ae348f2f0e67135df35f.zip FreeBSD-src-a0b05a52af8750d38933ae348f2f0e67135df35f.tar.gz |
Use the MI ithread helper functions in the x86 interrupt code.
-rw-r--r-- | sys/amd64/amd64/legacy.c | 18 | ||||
-rw-r--r-- | sys/amd64/amd64/nexus.c | 18 | ||||
-rw-r--r-- | sys/amd64/isa/intr_machdep.c | 243 | ||||
-rw-r--r-- | sys/amd64/isa/intr_machdep.h | 11 | ||||
-rw-r--r-- | sys/amd64/isa/ithread.c | 111 | ||||
-rw-r--r-- | sys/amd64/isa/nmi.c | 243 | ||||
-rw-r--r-- | sys/i386/i386/legacy.c | 18 | ||||
-rw-r--r-- | sys/i386/i386/nexus.c | 18 | ||||
-rw-r--r-- | sys/i386/isa/intr_machdep.c | 243 | ||||
-rw-r--r-- | sys/i386/isa/intr_machdep.h | 11 | ||||
-rw-r--r-- | sys/i386/isa/ithread.c | 111 | ||||
-rw-r--r-- | sys/i386/isa/nmi.c | 243 |
12 files changed, 402 insertions, 886 deletions
diff --git a/sys/amd64/amd64/legacy.c b/sys/amd64/amd64/legacy.c index 76c41ae..ce2c9cd 100644 --- a/sys/amd64/amd64/legacy.c +++ b/sys/amd64/amd64/legacy.c @@ -560,23 +560,17 @@ nexus_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, void (*ihand)(void *), void *arg, void **cookiep) { driver_t *driver; - int error, icflags; - int pri; /* interrupt thread priority */ + int error; /* somebody tried to setup an irq that failed to allocate! */ if (irq == NULL) panic("nexus_setup_intr: NULL irq resource!"); *cookiep = 0; - if (irq->r_flags & RF_SHAREABLE) - icflags = 0; - else - icflags = INTR_EXCL; + if ((irq->r_flags & RF_SHAREABLE) == 0) + flags |= INTR_EXCL; driver = device_get_driver(child); - pri = ithread_priority(flags); - if (flags & INTR_FAST) - icflags |= INTR_FAST; /* * We depend here on rman_activate_resource() being idempotent. @@ -585,10 +579,8 @@ nexus_setup_intr(device_t bus, device_t child, struct resource *irq, if (error) return (error); - *cookiep = inthand_add(device_get_nameunit(child), irq->r_start, - ihand, arg, pri, icflags); - if (*cookiep == NULL) - error = EINVAL; /* XXX ??? */ + error = inthand_add(device_get_nameunit(child), irq->r_start, + ihand, arg, flags, cookiep); return (error); } diff --git a/sys/amd64/amd64/nexus.c b/sys/amd64/amd64/nexus.c index 76c41ae..ce2c9cd 100644 --- a/sys/amd64/amd64/nexus.c +++ b/sys/amd64/amd64/nexus.c @@ -560,23 +560,17 @@ nexus_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, void (*ihand)(void *), void *arg, void **cookiep) { driver_t *driver; - int error, icflags; - int pri; /* interrupt thread priority */ + int error; /* somebody tried to setup an irq that failed to allocate! */ if (irq == NULL) panic("nexus_setup_intr: NULL irq resource!"); *cookiep = 0; - if (irq->r_flags & RF_SHAREABLE) - icflags = 0; - else - icflags = INTR_EXCL; + if ((irq->r_flags & RF_SHAREABLE) == 0) + flags |= INTR_EXCL; driver = device_get_driver(child); - pri = ithread_priority(flags); - if (flags & INTR_FAST) - icflags |= INTR_FAST; /* * We depend here on rman_activate_resource() being idempotent. @@ -585,10 +579,8 @@ nexus_setup_intr(device_t bus, device_t child, struct resource *irq, if (error) return (error); - *cookiep = inthand_add(device_get_nameunit(child), irq->r_start, - ihand, arg, pri, icflags); - if (*cookiep == NULL) - error = EINVAL; /* XXX ??? */ + error = inthand_add(device_get_nameunit(child), irq->r_start, + ihand, arg, flags, cookiep); return (error); } diff --git a/sys/amd64/isa/intr_machdep.c b/sys/amd64/isa/intr_machdep.c index 70b9378..2dd96d7 100644 --- a/sys/amd64/isa/intr_machdep.c +++ b/sys/amd64/isa/intr_machdep.c @@ -93,10 +93,12 @@ * Per-interrupt data. */ u_long *intr_countp[ICU_LEN]; /* pointers to interrupt counters */ -driver_intr_t *intr_handler[ICU_LEN]; /* first level interrupt handler */ -struct ithd *ithds[ICU_LEN]; /* real interrupt handler */ +driver_intr_t *intr_handler[ICU_LEN]; /* first level interrupt handler */ +struct ithd *ithds[ICU_LEN]; /* real interrupt handler */ void *intr_unit[ICU_LEN]; +static struct mtx ithds_table_lock; /* protect the ithds table */ + static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), @@ -133,6 +135,10 @@ static inthand_t *slowintr[ICU_LEN] = { static driver_intr_t isa_strayintr; +static void ithds_init(void *dummy); +static void ithread_enable(int vector); +static void ithread_disable(int vector); + #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 @@ -388,7 +394,7 @@ isa_irq_pending() * vmstat(8) and the like. */ static void -update_intrname(int intr, char *name) +update_intrname(int intr, const char *name) { char buf[32]; char *cp; @@ -536,132 +542,90 @@ icu_unset(intr, handler) return (0); } -struct intrhand * +static void +ithds_init(void *dummy) +{ + + mtx_init(&ithds_table_lock, "ithread table lock", MTX_SPIN); +} +SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL); + +static void +ithread_enable(int vector) +{ + + INTREN(1 << vector); +} + +static void +ithread_disable(int vector) +{ + + INTRDIS(1 << vector); +} + +int inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, - int pri, int flags) + enum intr_type flags, void **cookiep) { - struct ithd *ithd = ithds[irq]; /* descriptor for the IRQ */ - struct intrhand *head; /* chain of handlers for IRQ */ - struct intrhand *idesc; /* descriptor for this handler */ - struct proc *p; /* interrupt thread */ + struct ithd *ithd; /* descriptor for the IRQ */ int errcode = 0; + int created_ithd = 0; - if (name == NULL) /* no name? */ - panic ("anonymous interrupt"); - if (ithd == NULL || ithd->it_ih == NULL) { - /* first handler for this irq. */ - if (ithd == NULL) { - ithd = malloc(sizeof (struct ithd), M_DEVBUF, - M_WAITOK | M_ZERO); - if (ithd == NULL) - return (NULL); - ithd->irq = irq; + /* + * Work around a race where more than one CPU may be registering + * handlers on the same IRQ at the same time. + */ + mtx_lock_spin(&ithds_table_lock); + ithd = ithds[irq]; + mtx_unlock_spin(&ithds_table_lock); + if (ithd == NULL) { + errcode = ithread_create(&ithd, irq, 0, ithread_disable, + ithread_enable, "irq%d:", irq); + if (errcode) + return (errcode); + mtx_lock_spin(&ithds_table_lock); + if (ithds[irq] == NULL) { ithds[irq] = ithd; + created_ithd++; + mtx_unlock_spin(&ithds_table_lock); + } else { + struct ithd *orphan; + + orphan = ithd; + ithd = ithds[irq]; + mtx_unlock_spin(&ithds_table_lock); + ithread_destroy(orphan); } + } + + errcode = ithread_add_handler(ithd, name, handler, arg, + ithread_priority(flags), flags, cookiep); + + if ((flags & INTR_FAST) == 0 || errcode) /* - * If we have a fast interrupt, we need to set the - * handler address directly. Do that below. For a - * slow interrupt, we don't need to know more details, - * so do it here because it's tidier. - */ - if ((flags & INTR_FAST) == 0) { - /* - * Only create a kernel thread if we don't already - * have one. - */ - if (ithd->it_proc == NULL) { - errcode = kthread_create(ithd_loop, NULL, &p, - RFSTOPPED | RFHIGHPID, "irq%d: %s", irq, - name); - if (errcode) - panic("inthand_add: Can't create " - "interrupt thread"); - p->p_intr_nesting_level = 1; - p->p_rtprio.type = RTP_PRIO_ITHREAD; - p->p_stat = SWAIT; /* we're idle */ - - /* Put in linkages. */ - ithd->it_proc = p; - p->p_ithd = ithd; - } else - snprintf(ithd->it_proc->p_comm, MAXCOMLEN, - "irq%d: %s", irq, name); - p->p_rtprio.prio = pri; - - /* - * The interrupt process must be in place, but - * not necessarily schedulable, before we - * initialize the ICU, since it may cause an - * immediate interrupt. - */ - if (icu_setup(irq, &sched_ithd, arg, flags) != 0) - panic("inthand_add: Can't initialize ICU"); - } - } else if ((flags & INTR_EXCL) != 0 - || (ithd->it_ih->ih_flags & INTR_EXCL) != 0) { - /* - * We can't append the new handler if either - * list ithd or new handler do not allow - * interrupts to be shared. + * The interrupt process must be in place, but + * not necessarily schedulable, before we + * initialize the ICU, since it may cause an + * immediate interrupt. */ - if (bootverbose) - printf("\tdevice combination %s and %s " - "doesn't support shared irq%d\n", - ithd->it_ih->ih_name, name, irq); - return(NULL); - } else if (flags & INTR_FAST) { - /* We can only have one fast interrupt by itself. */ - if (bootverbose) - printf("\tCan't add fast interrupt %s" - " to normal interrupt %s on irq%d", - name, ithd->it_ih->ih_name, irq); - return (NULL); - } else { /* update p_comm */ - p = ithd->it_proc; - if (strlen(p->p_comm) + strlen(name) < MAXCOMLEN) { - strcat(p->p_comm, " "); - strcat(p->p_comm, name); - } else if (strlen(p->p_comm) == MAXCOMLEN) - p->p_comm[MAXCOMLEN - 1] = '+'; - else - strcat(p->p_comm, "+"); - } - idesc = malloc(sizeof (struct intrhand), M_DEVBUF, M_WAITOK | M_ZERO); - if (idesc == NULL) - return (NULL); - - idesc->ih_handler = handler; - idesc->ih_argument = arg; - idesc->ih_flags = flags; - idesc->ih_ithd = ithd; - - idesc->ih_name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); - if (idesc->ih_name == NULL) { - free(idesc, M_DEVBUF); - return (NULL); - } - strcpy(idesc->ih_name, name); + if (icu_setup(irq, &sched_ithd, arg, flags) != 0) + panic("inthand_add: Can't initialize ICU"); - /* Slow interrupts got set up above. */ - if ((flags & INTR_FAST) - && (icu_setup(irq, idesc->ih_handler, idesc->ih_argument, - idesc->ih_flags) != 0) ) { - if (bootverbose) + if (errcode) + return (errcode); + + if (flags & INTR_FAST) { + errcode = icu_setup(irq, handler, arg, flags); + if (errcode && bootverbose) printf("\tinthand_add(irq%d) failed, result=%d\n", irq, errcode); - free(idesc->ih_name, M_DEVBUF); - free(idesc, M_DEVBUF); - return NULL; + if (errcode) + return (errcode); } - head = ithd->it_ih; /* look at chain of handlers */ - if (head) { - while (head->ih_next != NULL) - head = head->ih_next; /* find the end */ - head->ih_next = idesc; /* hook it in there */ - } else - ithd->it_ih = idesc; /* put it up front */ - update_intrname(irq, idesc->ih_name); - return (idesc); + + update_intrname(irq, name); + return (0); } /* @@ -673,50 +637,9 @@ inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, * structure to the system. First ensure the handler is not actively * in use. */ - int -inthand_remove(struct intrhand *idesc) +inthand_remove(void *cookie) { - struct ithd *ithd; /* descriptor for the IRQ */ - struct intrhand *ih; /* chain of handlers */ - if (idesc == NULL) - return (-1); - ithd = idesc->ih_ithd; - ih = ithd->it_ih; - - if (ih == idesc) /* first in the chain */ - ithd->it_ih = idesc->ih_next; /* unhook it */ - else { - while ((ih != NULL) - && (ih->ih_next != idesc) ) - ih = ih->ih_next; - if (ih->ih_next != idesc) - return (-1); - ih->ih_next = ih->ih_next->ih_next; - } - - if (ithd->it_ih == NULL) { /* no handlers left, */ - icu_unset(ithd->irq, idesc->ih_handler); - ithds[ithd->irq] = NULL; - - if ((idesc->ih_flags & INTR_FAST) == 0) { - mtx_lock_spin(&sched_lock); - if (ithd->it_proc->p_stat == SWAIT) { - ithd->it_proc->p_intr_nesting_level = 0; - ithd->it_proc->p_stat = SRUN; - setrunqueue(ithd->it_proc); - /* - * We don't do an ast here because we really - * don't care when it runs next. - * - * XXX: should we lower the threads priority? - */ - } - mtx_unlock_spin(&sched_lock); - } - } - free(idesc->ih_name, M_DEVBUF); - free(idesc, M_DEVBUF); - return (0); + return (ithread_remove_handler(cookie)); } diff --git a/sys/amd64/isa/intr_machdep.h b/sys/amd64/isa/intr_machdep.h index 53d782f..b42c348 100644 --- a/sys/amd64/isa/intr_machdep.h +++ b/sys/amd64/isa/intr_machdep.h @@ -220,13 +220,10 @@ int icu_unset __P((int intr, driver_intr_t *handler)); * WARNING: These are internal functions and not to be used by device drivers! * They are subject to change without notice. */ -struct intrhand *inthand_add(const char *name, int irq, driver_intr_t handler, - void *arg, int pri, int flags); -int inthand_remove(struct intrhand *idesc); -void sched_ithd(void *); -void ithd_loop(void *); -void start_softintr(void *); -void intr_soft(void *); +int inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, + enum intr_type flags, void **cookiep); +int inthand_remove(void *cookie); +void sched_ithd(void *dummy); #endif /* LOCORE */ diff --git a/sys/amd64/isa/ithread.c b/sys/amd64/isa/ithread.c index 99a1abf..8fb1924 100644 --- a/sys/amd64/isa/ithread.c +++ b/sys/amd64/isa/ithread.c @@ -51,6 +51,8 @@ #include <sys/unistd.h> #include <sys/errno.h> #include <sys/interrupt.h> +#include <sys/random.h> +#include <sys/time.h> #include <machine/md_var.h> #include <machine/segments.h> @@ -64,6 +66,11 @@ #include <sys/ktr.h> #include <machine/cpu.h> +struct int_entropy { + struct proc *p; + int irq; +}; + static u_int straycount[NHWI]; #define MAX_STRAY_LOG 5 @@ -87,6 +94,19 @@ sched_ithd(void *cookie) */ atomic_add_long(intr_countp[irq], 1); /* one more for this IRQ */ atomic_add_int(&cnt.v_intr, 1); /* one more global interrupt */ + + /* + * If this interrupt is marked as being a source of entropy, use + * the current timestamp to feed entropy to the PRNG. + */ + if (ir != NULL && (ir->it_flags & IT_ENTROPY)) { + struct int_entropy entropy; + + entropy.irq = irq; + entropy.p = curproc; + random_harvest(&entropy, sizeof(entropy), 2, 0, + RANDOM_INTERRUPT); + } /* * If we don't have an interrupt resource or an interrupt thread for @@ -121,102 +141,13 @@ sched_ithd(void *cookie) /* membar_lock(); */ ir->it_proc->p_stat = SRUN; setrunqueue(ir->it_proc); - if (!cold) { - if (curproc != PCPU_GET(idleproc)) - setrunqueue(curproc); - mi_switch(); - } + need_resched(); } else { CTR3(KTR_INTR, "sched_ithd %d: it_need %d, state %d", ir->it_proc->p_pid, ir->it_need, ir->it_proc->p_stat ); - need_resched(); } mtx_unlock_spin(&sched_lock); } - -/* - * This is the main code for all interrupt threads. It gets put on - * whichkqs by setrunqueue above. - */ -void -ithd_loop(void *dummy) -{ - struct ithd *me; /* our thread context */ - struct intrhand *ih; /* and our interrupt handler chain */ - - me = curproc->p_ithd; /* point to myself */ - - /* - * As long as we have interrupts outstanding, go through the - * list of handlers, giving each one a go at it. - */ - for (;;) { - /* - * If we don't have any handlers, then we are an orphaned - * thread and just need to die. - */ - if (me->it_ih == NULL) { - CTR2(KTR_INTR, "ithd_loop pid %d(%s) exiting", - me->it_proc->p_pid, me->it_proc->p_comm); - curproc->p_ithd = NULL; - free(me, M_DEVBUF); - mtx_lock(&Giant); - kthread_exit(0); - } - - CTR3(KTR_INTR, "ithd_loop pid %d(%s) need=%d", - me->it_proc->p_pid, me->it_proc->p_comm, me->it_need); - while (me->it_need) { - /* - * Service interrupts. If another interrupt - * arrives while we are running, they will set - * it_need to denote that we should make - * another pass. - */ - me->it_need = 0; -#if 0 - membar_unlock(); /* push out "it_need=0" */ -#endif - for (ih = me->it_ih; ih != NULL; ih = ih->ih_next) { - CTR5(KTR_INTR, - "ithd_loop pid %d ih=%p: %p(%p) flg=%x", - me->it_proc->p_pid, (void *)ih, - (void *)ih->ih_handler, ih->ih_argument, - ih->ih_flags); - - if ((ih->ih_flags & INTR_MPSAFE) == 0) - mtx_lock(&Giant); - ih->ih_handler(ih->ih_argument); - if ((ih->ih_flags & INTR_MPSAFE) == 0) - mtx_unlock(&Giant); - } - } - - /* - * Processed all our interrupts. Now get the sched - * lock. This may take a while and it_need may get - * set again, so we have to check it again. - */ - mtx_assert(&Giant, MA_NOTOWNED); - mtx_lock_spin(&sched_lock); - if (!me->it_need) { - - INTREN (1 << me->irq); /* reset the mask bit */ - me->it_proc->p_stat = SWAIT; /* we're idle */ -#ifdef APIC_IO - CTR2(KTR_INTR, "ithd_loop pid %d: done, apic_imen=%x", - me->it_proc->p_pid, apic_imen); -#else - CTR2(KTR_INTR, "ithd_loop pid %d: done, imen=%x", - me->it_proc->p_pid, imen); -#endif - mi_switch(); - CTR1(KTR_INTR, "ithd_loop pid %d: resumed", - me->it_proc->p_pid); - } - mtx_unlock_spin(&sched_lock); - } -} diff --git a/sys/amd64/isa/nmi.c b/sys/amd64/isa/nmi.c index 70b9378..2dd96d7 100644 --- a/sys/amd64/isa/nmi.c +++ b/sys/amd64/isa/nmi.c @@ -93,10 +93,12 @@ * Per-interrupt data. */ u_long *intr_countp[ICU_LEN]; /* pointers to interrupt counters */ -driver_intr_t *intr_handler[ICU_LEN]; /* first level interrupt handler */ -struct ithd *ithds[ICU_LEN]; /* real interrupt handler */ +driver_intr_t *intr_handler[ICU_LEN]; /* first level interrupt handler */ +struct ithd *ithds[ICU_LEN]; /* real interrupt handler */ void *intr_unit[ICU_LEN]; +static struct mtx ithds_table_lock; /* protect the ithds table */ + static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), @@ -133,6 +135,10 @@ static inthand_t *slowintr[ICU_LEN] = { static driver_intr_t isa_strayintr; +static void ithds_init(void *dummy); +static void ithread_enable(int vector); +static void ithread_disable(int vector); + #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 @@ -388,7 +394,7 @@ isa_irq_pending() * vmstat(8) and the like. */ static void -update_intrname(int intr, char *name) +update_intrname(int intr, const char *name) { char buf[32]; char *cp; @@ -536,132 +542,90 @@ icu_unset(intr, handler) return (0); } -struct intrhand * +static void +ithds_init(void *dummy) +{ + + mtx_init(&ithds_table_lock, "ithread table lock", MTX_SPIN); +} +SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL); + +static void +ithread_enable(int vector) +{ + + INTREN(1 << vector); +} + +static void +ithread_disable(int vector) +{ + + INTRDIS(1 << vector); +} + +int inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, - int pri, int flags) + enum intr_type flags, void **cookiep) { - struct ithd *ithd = ithds[irq]; /* descriptor for the IRQ */ - struct intrhand *head; /* chain of handlers for IRQ */ - struct intrhand *idesc; /* descriptor for this handler */ - struct proc *p; /* interrupt thread */ + struct ithd *ithd; /* descriptor for the IRQ */ int errcode = 0; + int created_ithd = 0; - if (name == NULL) /* no name? */ - panic ("anonymous interrupt"); - if (ithd == NULL || ithd->it_ih == NULL) { - /* first handler for this irq. */ - if (ithd == NULL) { - ithd = malloc(sizeof (struct ithd), M_DEVBUF, - M_WAITOK | M_ZERO); - if (ithd == NULL) - return (NULL); - ithd->irq = irq; + /* + * Work around a race where more than one CPU may be registering + * handlers on the same IRQ at the same time. + */ + mtx_lock_spin(&ithds_table_lock); + ithd = ithds[irq]; + mtx_unlock_spin(&ithds_table_lock); + if (ithd == NULL) { + errcode = ithread_create(&ithd, irq, 0, ithread_disable, + ithread_enable, "irq%d:", irq); + if (errcode) + return (errcode); + mtx_lock_spin(&ithds_table_lock); + if (ithds[irq] == NULL) { ithds[irq] = ithd; + created_ithd++; + mtx_unlock_spin(&ithds_table_lock); + } else { + struct ithd *orphan; + + orphan = ithd; + ithd = ithds[irq]; + mtx_unlock_spin(&ithds_table_lock); + ithread_destroy(orphan); } + } + + errcode = ithread_add_handler(ithd, name, handler, arg, + ithread_priority(flags), flags, cookiep); + + if ((flags & INTR_FAST) == 0 || errcode) /* - * If we have a fast interrupt, we need to set the - * handler address directly. Do that below. For a - * slow interrupt, we don't need to know more details, - * so do it here because it's tidier. - */ - if ((flags & INTR_FAST) == 0) { - /* - * Only create a kernel thread if we don't already - * have one. - */ - if (ithd->it_proc == NULL) { - errcode = kthread_create(ithd_loop, NULL, &p, - RFSTOPPED | RFHIGHPID, "irq%d: %s", irq, - name); - if (errcode) - panic("inthand_add: Can't create " - "interrupt thread"); - p->p_intr_nesting_level = 1; - p->p_rtprio.type = RTP_PRIO_ITHREAD; - p->p_stat = SWAIT; /* we're idle */ - - /* Put in linkages. */ - ithd->it_proc = p; - p->p_ithd = ithd; - } else - snprintf(ithd->it_proc->p_comm, MAXCOMLEN, - "irq%d: %s", irq, name); - p->p_rtprio.prio = pri; - - /* - * The interrupt process must be in place, but - * not necessarily schedulable, before we - * initialize the ICU, since it may cause an - * immediate interrupt. - */ - if (icu_setup(irq, &sched_ithd, arg, flags) != 0) - panic("inthand_add: Can't initialize ICU"); - } - } else if ((flags & INTR_EXCL) != 0 - || (ithd->it_ih->ih_flags & INTR_EXCL) != 0) { - /* - * We can't append the new handler if either - * list ithd or new handler do not allow - * interrupts to be shared. + * The interrupt process must be in place, but + * not necessarily schedulable, before we + * initialize the ICU, since it may cause an + * immediate interrupt. */ - if (bootverbose) - printf("\tdevice combination %s and %s " - "doesn't support shared irq%d\n", - ithd->it_ih->ih_name, name, irq); - return(NULL); - } else if (flags & INTR_FAST) { - /* We can only have one fast interrupt by itself. */ - if (bootverbose) - printf("\tCan't add fast interrupt %s" - " to normal interrupt %s on irq%d", - name, ithd->it_ih->ih_name, irq); - return (NULL); - } else { /* update p_comm */ - p = ithd->it_proc; - if (strlen(p->p_comm) + strlen(name) < MAXCOMLEN) { - strcat(p->p_comm, " "); - strcat(p->p_comm, name); - } else if (strlen(p->p_comm) == MAXCOMLEN) - p->p_comm[MAXCOMLEN - 1] = '+'; - else - strcat(p->p_comm, "+"); - } - idesc = malloc(sizeof (struct intrhand), M_DEVBUF, M_WAITOK | M_ZERO); - if (idesc == NULL) - return (NULL); - - idesc->ih_handler = handler; - idesc->ih_argument = arg; - idesc->ih_flags = flags; - idesc->ih_ithd = ithd; - - idesc->ih_name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); - if (idesc->ih_name == NULL) { - free(idesc, M_DEVBUF); - return (NULL); - } - strcpy(idesc->ih_name, name); + if (icu_setup(irq, &sched_ithd, arg, flags) != 0) + panic("inthand_add: Can't initialize ICU"); - /* Slow interrupts got set up above. */ - if ((flags & INTR_FAST) - && (icu_setup(irq, idesc->ih_handler, idesc->ih_argument, - idesc->ih_flags) != 0) ) { - if (bootverbose) + if (errcode) + return (errcode); + + if (flags & INTR_FAST) { + errcode = icu_setup(irq, handler, arg, flags); + if (errcode && bootverbose) printf("\tinthand_add(irq%d) failed, result=%d\n", irq, errcode); - free(idesc->ih_name, M_DEVBUF); - free(idesc, M_DEVBUF); - return NULL; + if (errcode) + return (errcode); } - head = ithd->it_ih; /* look at chain of handlers */ - if (head) { - while (head->ih_next != NULL) - head = head->ih_next; /* find the end */ - head->ih_next = idesc; /* hook it in there */ - } else - ithd->it_ih = idesc; /* put it up front */ - update_intrname(irq, idesc->ih_name); - return (idesc); + + update_intrname(irq, name); + return (0); } /* @@ -673,50 +637,9 @@ inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, * structure to the system. First ensure the handler is not actively * in use. */ - int -inthand_remove(struct intrhand *idesc) +inthand_remove(void *cookie) { - struct ithd *ithd; /* descriptor for the IRQ */ - struct intrhand *ih; /* chain of handlers */ - if (idesc == NULL) - return (-1); - ithd = idesc->ih_ithd; - ih = ithd->it_ih; - - if (ih == idesc) /* first in the chain */ - ithd->it_ih = idesc->ih_next; /* unhook it */ - else { - while ((ih != NULL) - && (ih->ih_next != idesc) ) - ih = ih->ih_next; - if (ih->ih_next != idesc) - return (-1); - ih->ih_next = ih->ih_next->ih_next; - } - - if (ithd->it_ih == NULL) { /* no handlers left, */ - icu_unset(ithd->irq, idesc->ih_handler); - ithds[ithd->irq] = NULL; - - if ((idesc->ih_flags & INTR_FAST) == 0) { - mtx_lock_spin(&sched_lock); - if (ithd->it_proc->p_stat == SWAIT) { - ithd->it_proc->p_intr_nesting_level = 0; - ithd->it_proc->p_stat = SRUN; - setrunqueue(ithd->it_proc); - /* - * We don't do an ast here because we really - * don't care when it runs next. - * - * XXX: should we lower the threads priority? - */ - } - mtx_unlock_spin(&sched_lock); - } - } - free(idesc->ih_name, M_DEVBUF); - free(idesc, M_DEVBUF); - return (0); + return (ithread_remove_handler(cookie)); } diff --git a/sys/i386/i386/legacy.c b/sys/i386/i386/legacy.c index 76c41ae..ce2c9cd 100644 --- a/sys/i386/i386/legacy.c +++ b/sys/i386/i386/legacy.c @@ -560,23 +560,17 @@ nexus_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, void (*ihand)(void *), void *arg, void **cookiep) { driver_t *driver; - int error, icflags; - int pri; /* interrupt thread priority */ + int error; /* somebody tried to setup an irq that failed to allocate! */ if (irq == NULL) panic("nexus_setup_intr: NULL irq resource!"); *cookiep = 0; - if (irq->r_flags & RF_SHAREABLE) - icflags = 0; - else - icflags = INTR_EXCL; + if ((irq->r_flags & RF_SHAREABLE) == 0) + flags |= INTR_EXCL; driver = device_get_driver(child); - pri = ithread_priority(flags); - if (flags & INTR_FAST) - icflags |= INTR_FAST; /* * We depend here on rman_activate_resource() being idempotent. @@ -585,10 +579,8 @@ nexus_setup_intr(device_t bus, device_t child, struct resource *irq, if (error) return (error); - *cookiep = inthand_add(device_get_nameunit(child), irq->r_start, - ihand, arg, pri, icflags); - if (*cookiep == NULL) - error = EINVAL; /* XXX ??? */ + error = inthand_add(device_get_nameunit(child), irq->r_start, + ihand, arg, flags, cookiep); return (error); } diff --git a/sys/i386/i386/nexus.c b/sys/i386/i386/nexus.c index 76c41ae..ce2c9cd 100644 --- a/sys/i386/i386/nexus.c +++ b/sys/i386/i386/nexus.c @@ -560,23 +560,17 @@ nexus_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, void (*ihand)(void *), void *arg, void **cookiep) { driver_t *driver; - int error, icflags; - int pri; /* interrupt thread priority */ + int error; /* somebody tried to setup an irq that failed to allocate! */ if (irq == NULL) panic("nexus_setup_intr: NULL irq resource!"); *cookiep = 0; - if (irq->r_flags & RF_SHAREABLE) - icflags = 0; - else - icflags = INTR_EXCL; + if ((irq->r_flags & RF_SHAREABLE) == 0) + flags |= INTR_EXCL; driver = device_get_driver(child); - pri = ithread_priority(flags); - if (flags & INTR_FAST) - icflags |= INTR_FAST; /* * We depend here on rman_activate_resource() being idempotent. @@ -585,10 +579,8 @@ nexus_setup_intr(device_t bus, device_t child, struct resource *irq, if (error) return (error); - *cookiep = inthand_add(device_get_nameunit(child), irq->r_start, - ihand, arg, pri, icflags); - if (*cookiep == NULL) - error = EINVAL; /* XXX ??? */ + error = inthand_add(device_get_nameunit(child), irq->r_start, + ihand, arg, flags, cookiep); return (error); } diff --git a/sys/i386/isa/intr_machdep.c b/sys/i386/isa/intr_machdep.c index 70b9378..2dd96d7 100644 --- a/sys/i386/isa/intr_machdep.c +++ b/sys/i386/isa/intr_machdep.c @@ -93,10 +93,12 @@ * Per-interrupt data. */ u_long *intr_countp[ICU_LEN]; /* pointers to interrupt counters */ -driver_intr_t *intr_handler[ICU_LEN]; /* first level interrupt handler */ -struct ithd *ithds[ICU_LEN]; /* real interrupt handler */ +driver_intr_t *intr_handler[ICU_LEN]; /* first level interrupt handler */ +struct ithd *ithds[ICU_LEN]; /* real interrupt handler */ void *intr_unit[ICU_LEN]; +static struct mtx ithds_table_lock; /* protect the ithds table */ + static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), @@ -133,6 +135,10 @@ static inthand_t *slowintr[ICU_LEN] = { static driver_intr_t isa_strayintr; +static void ithds_init(void *dummy); +static void ithread_enable(int vector); +static void ithread_disable(int vector); + #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 @@ -388,7 +394,7 @@ isa_irq_pending() * vmstat(8) and the like. */ static void -update_intrname(int intr, char *name) +update_intrname(int intr, const char *name) { char buf[32]; char *cp; @@ -536,132 +542,90 @@ icu_unset(intr, handler) return (0); } -struct intrhand * +static void +ithds_init(void *dummy) +{ + + mtx_init(&ithds_table_lock, "ithread table lock", MTX_SPIN); +} +SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL); + +static void +ithread_enable(int vector) +{ + + INTREN(1 << vector); +} + +static void +ithread_disable(int vector) +{ + + INTRDIS(1 << vector); +} + +int inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, - int pri, int flags) + enum intr_type flags, void **cookiep) { - struct ithd *ithd = ithds[irq]; /* descriptor for the IRQ */ - struct intrhand *head; /* chain of handlers for IRQ */ - struct intrhand *idesc; /* descriptor for this handler */ - struct proc *p; /* interrupt thread */ + struct ithd *ithd; /* descriptor for the IRQ */ int errcode = 0; + int created_ithd = 0; - if (name == NULL) /* no name? */ - panic ("anonymous interrupt"); - if (ithd == NULL || ithd->it_ih == NULL) { - /* first handler for this irq. */ - if (ithd == NULL) { - ithd = malloc(sizeof (struct ithd), M_DEVBUF, - M_WAITOK | M_ZERO); - if (ithd == NULL) - return (NULL); - ithd->irq = irq; + /* + * Work around a race where more than one CPU may be registering + * handlers on the same IRQ at the same time. + */ + mtx_lock_spin(&ithds_table_lock); + ithd = ithds[irq]; + mtx_unlock_spin(&ithds_table_lock); + if (ithd == NULL) { + errcode = ithread_create(&ithd, irq, 0, ithread_disable, + ithread_enable, "irq%d:", irq); + if (errcode) + return (errcode); + mtx_lock_spin(&ithds_table_lock); + if (ithds[irq] == NULL) { ithds[irq] = ithd; + created_ithd++; + mtx_unlock_spin(&ithds_table_lock); + } else { + struct ithd *orphan; + + orphan = ithd; + ithd = ithds[irq]; + mtx_unlock_spin(&ithds_table_lock); + ithread_destroy(orphan); } + } + + errcode = ithread_add_handler(ithd, name, handler, arg, + ithread_priority(flags), flags, cookiep); + + if ((flags & INTR_FAST) == 0 || errcode) /* - * If we have a fast interrupt, we need to set the - * handler address directly. Do that below. For a - * slow interrupt, we don't need to know more details, - * so do it here because it's tidier. - */ - if ((flags & INTR_FAST) == 0) { - /* - * Only create a kernel thread if we don't already - * have one. - */ - if (ithd->it_proc == NULL) { - errcode = kthread_create(ithd_loop, NULL, &p, - RFSTOPPED | RFHIGHPID, "irq%d: %s", irq, - name); - if (errcode) - panic("inthand_add: Can't create " - "interrupt thread"); - p->p_intr_nesting_level = 1; - p->p_rtprio.type = RTP_PRIO_ITHREAD; - p->p_stat = SWAIT; /* we're idle */ - - /* Put in linkages. */ - ithd->it_proc = p; - p->p_ithd = ithd; - } else - snprintf(ithd->it_proc->p_comm, MAXCOMLEN, - "irq%d: %s", irq, name); - p->p_rtprio.prio = pri; - - /* - * The interrupt process must be in place, but - * not necessarily schedulable, before we - * initialize the ICU, since it may cause an - * immediate interrupt. - */ - if (icu_setup(irq, &sched_ithd, arg, flags) != 0) - panic("inthand_add: Can't initialize ICU"); - } - } else if ((flags & INTR_EXCL) != 0 - || (ithd->it_ih->ih_flags & INTR_EXCL) != 0) { - /* - * We can't append the new handler if either - * list ithd or new handler do not allow - * interrupts to be shared. + * The interrupt process must be in place, but + * not necessarily schedulable, before we + * initialize the ICU, since it may cause an + * immediate interrupt. */ - if (bootverbose) - printf("\tdevice combination %s and %s " - "doesn't support shared irq%d\n", - ithd->it_ih->ih_name, name, irq); - return(NULL); - } else if (flags & INTR_FAST) { - /* We can only have one fast interrupt by itself. */ - if (bootverbose) - printf("\tCan't add fast interrupt %s" - " to normal interrupt %s on irq%d", - name, ithd->it_ih->ih_name, irq); - return (NULL); - } else { /* update p_comm */ - p = ithd->it_proc; - if (strlen(p->p_comm) + strlen(name) < MAXCOMLEN) { - strcat(p->p_comm, " "); - strcat(p->p_comm, name); - } else if (strlen(p->p_comm) == MAXCOMLEN) - p->p_comm[MAXCOMLEN - 1] = '+'; - else - strcat(p->p_comm, "+"); - } - idesc = malloc(sizeof (struct intrhand), M_DEVBUF, M_WAITOK | M_ZERO); - if (idesc == NULL) - return (NULL); - - idesc->ih_handler = handler; - idesc->ih_argument = arg; - idesc->ih_flags = flags; - idesc->ih_ithd = ithd; - - idesc->ih_name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); - if (idesc->ih_name == NULL) { - free(idesc, M_DEVBUF); - return (NULL); - } - strcpy(idesc->ih_name, name); + if (icu_setup(irq, &sched_ithd, arg, flags) != 0) + panic("inthand_add: Can't initialize ICU"); - /* Slow interrupts got set up above. */ - if ((flags & INTR_FAST) - && (icu_setup(irq, idesc->ih_handler, idesc->ih_argument, - idesc->ih_flags) != 0) ) { - if (bootverbose) + if (errcode) + return (errcode); + + if (flags & INTR_FAST) { + errcode = icu_setup(irq, handler, arg, flags); + if (errcode && bootverbose) printf("\tinthand_add(irq%d) failed, result=%d\n", irq, errcode); - free(idesc->ih_name, M_DEVBUF); - free(idesc, M_DEVBUF); - return NULL; + if (errcode) + return (errcode); } - head = ithd->it_ih; /* look at chain of handlers */ - if (head) { - while (head->ih_next != NULL) - head = head->ih_next; /* find the end */ - head->ih_next = idesc; /* hook it in there */ - } else - ithd->it_ih = idesc; /* put it up front */ - update_intrname(irq, idesc->ih_name); - return (idesc); + + update_intrname(irq, name); + return (0); } /* @@ -673,50 +637,9 @@ inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, * structure to the system. First ensure the handler is not actively * in use. */ - int -inthand_remove(struct intrhand *idesc) +inthand_remove(void *cookie) { - struct ithd *ithd; /* descriptor for the IRQ */ - struct intrhand *ih; /* chain of handlers */ - if (idesc == NULL) - return (-1); - ithd = idesc->ih_ithd; - ih = ithd->it_ih; - - if (ih == idesc) /* first in the chain */ - ithd->it_ih = idesc->ih_next; /* unhook it */ - else { - while ((ih != NULL) - && (ih->ih_next != idesc) ) - ih = ih->ih_next; - if (ih->ih_next != idesc) - return (-1); - ih->ih_next = ih->ih_next->ih_next; - } - - if (ithd->it_ih == NULL) { /* no handlers left, */ - icu_unset(ithd->irq, idesc->ih_handler); - ithds[ithd->irq] = NULL; - - if ((idesc->ih_flags & INTR_FAST) == 0) { - mtx_lock_spin(&sched_lock); - if (ithd->it_proc->p_stat == SWAIT) { - ithd->it_proc->p_intr_nesting_level = 0; - ithd->it_proc->p_stat = SRUN; - setrunqueue(ithd->it_proc); - /* - * We don't do an ast here because we really - * don't care when it runs next. - * - * XXX: should we lower the threads priority? - */ - } - mtx_unlock_spin(&sched_lock); - } - } - free(idesc->ih_name, M_DEVBUF); - free(idesc, M_DEVBUF); - return (0); + return (ithread_remove_handler(cookie)); } diff --git a/sys/i386/isa/intr_machdep.h b/sys/i386/isa/intr_machdep.h index 53d782f..b42c348 100644 --- a/sys/i386/isa/intr_machdep.h +++ b/sys/i386/isa/intr_machdep.h @@ -220,13 +220,10 @@ int icu_unset __P((int intr, driver_intr_t *handler)); * WARNING: These are internal functions and not to be used by device drivers! * They are subject to change without notice. */ -struct intrhand *inthand_add(const char *name, int irq, driver_intr_t handler, - void *arg, int pri, int flags); -int inthand_remove(struct intrhand *idesc); -void sched_ithd(void *); -void ithd_loop(void *); -void start_softintr(void *); -void intr_soft(void *); +int inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, + enum intr_type flags, void **cookiep); +int inthand_remove(void *cookie); +void sched_ithd(void *dummy); #endif /* LOCORE */ diff --git a/sys/i386/isa/ithread.c b/sys/i386/isa/ithread.c index 99a1abf..8fb1924 100644 --- a/sys/i386/isa/ithread.c +++ b/sys/i386/isa/ithread.c @@ -51,6 +51,8 @@ #include <sys/unistd.h> #include <sys/errno.h> #include <sys/interrupt.h> +#include <sys/random.h> +#include <sys/time.h> #include <machine/md_var.h> #include <machine/segments.h> @@ -64,6 +66,11 @@ #include <sys/ktr.h> #include <machine/cpu.h> +struct int_entropy { + struct proc *p; + int irq; +}; + static u_int straycount[NHWI]; #define MAX_STRAY_LOG 5 @@ -87,6 +94,19 @@ sched_ithd(void *cookie) */ atomic_add_long(intr_countp[irq], 1); /* one more for this IRQ */ atomic_add_int(&cnt.v_intr, 1); /* one more global interrupt */ + + /* + * If this interrupt is marked as being a source of entropy, use + * the current timestamp to feed entropy to the PRNG. + */ + if (ir != NULL && (ir->it_flags & IT_ENTROPY)) { + struct int_entropy entropy; + + entropy.irq = irq; + entropy.p = curproc; + random_harvest(&entropy, sizeof(entropy), 2, 0, + RANDOM_INTERRUPT); + } /* * If we don't have an interrupt resource or an interrupt thread for @@ -121,102 +141,13 @@ sched_ithd(void *cookie) /* membar_lock(); */ ir->it_proc->p_stat = SRUN; setrunqueue(ir->it_proc); - if (!cold) { - if (curproc != PCPU_GET(idleproc)) - setrunqueue(curproc); - mi_switch(); - } + need_resched(); } else { CTR3(KTR_INTR, "sched_ithd %d: it_need %d, state %d", ir->it_proc->p_pid, ir->it_need, ir->it_proc->p_stat ); - need_resched(); } mtx_unlock_spin(&sched_lock); } - -/* - * This is the main code for all interrupt threads. It gets put on - * whichkqs by setrunqueue above. - */ -void -ithd_loop(void *dummy) -{ - struct ithd *me; /* our thread context */ - struct intrhand *ih; /* and our interrupt handler chain */ - - me = curproc->p_ithd; /* point to myself */ - - /* - * As long as we have interrupts outstanding, go through the - * list of handlers, giving each one a go at it. - */ - for (;;) { - /* - * If we don't have any handlers, then we are an orphaned - * thread and just need to die. - */ - if (me->it_ih == NULL) { - CTR2(KTR_INTR, "ithd_loop pid %d(%s) exiting", - me->it_proc->p_pid, me->it_proc->p_comm); - curproc->p_ithd = NULL; - free(me, M_DEVBUF); - mtx_lock(&Giant); - kthread_exit(0); - } - - CTR3(KTR_INTR, "ithd_loop pid %d(%s) need=%d", - me->it_proc->p_pid, me->it_proc->p_comm, me->it_need); - while (me->it_need) { - /* - * Service interrupts. If another interrupt - * arrives while we are running, they will set - * it_need to denote that we should make - * another pass. - */ - me->it_need = 0; -#if 0 - membar_unlock(); /* push out "it_need=0" */ -#endif - for (ih = me->it_ih; ih != NULL; ih = ih->ih_next) { - CTR5(KTR_INTR, - "ithd_loop pid %d ih=%p: %p(%p) flg=%x", - me->it_proc->p_pid, (void *)ih, - (void *)ih->ih_handler, ih->ih_argument, - ih->ih_flags); - - if ((ih->ih_flags & INTR_MPSAFE) == 0) - mtx_lock(&Giant); - ih->ih_handler(ih->ih_argument); - if ((ih->ih_flags & INTR_MPSAFE) == 0) - mtx_unlock(&Giant); - } - } - - /* - * Processed all our interrupts. Now get the sched - * lock. This may take a while and it_need may get - * set again, so we have to check it again. - */ - mtx_assert(&Giant, MA_NOTOWNED); - mtx_lock_spin(&sched_lock); - if (!me->it_need) { - - INTREN (1 << me->irq); /* reset the mask bit */ - me->it_proc->p_stat = SWAIT; /* we're idle */ -#ifdef APIC_IO - CTR2(KTR_INTR, "ithd_loop pid %d: done, apic_imen=%x", - me->it_proc->p_pid, apic_imen); -#else - CTR2(KTR_INTR, "ithd_loop pid %d: done, imen=%x", - me->it_proc->p_pid, imen); -#endif - mi_switch(); - CTR1(KTR_INTR, "ithd_loop pid %d: resumed", - me->it_proc->p_pid); - } - mtx_unlock_spin(&sched_lock); - } -} diff --git a/sys/i386/isa/nmi.c b/sys/i386/isa/nmi.c index 70b9378..2dd96d7 100644 --- a/sys/i386/isa/nmi.c +++ b/sys/i386/isa/nmi.c @@ -93,10 +93,12 @@ * Per-interrupt data. */ u_long *intr_countp[ICU_LEN]; /* pointers to interrupt counters */ -driver_intr_t *intr_handler[ICU_LEN]; /* first level interrupt handler */ -struct ithd *ithds[ICU_LEN]; /* real interrupt handler */ +driver_intr_t *intr_handler[ICU_LEN]; /* first level interrupt handler */ +struct ithd *ithds[ICU_LEN]; /* real interrupt handler */ void *intr_unit[ICU_LEN]; +static struct mtx ithds_table_lock; /* protect the ithds table */ + static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), @@ -133,6 +135,10 @@ static inthand_t *slowintr[ICU_LEN] = { static driver_intr_t isa_strayintr; +static void ithds_init(void *dummy); +static void ithread_enable(int vector); +static void ithread_disable(int vector); + #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 @@ -388,7 +394,7 @@ isa_irq_pending() * vmstat(8) and the like. */ static void -update_intrname(int intr, char *name) +update_intrname(int intr, const char *name) { char buf[32]; char *cp; @@ -536,132 +542,90 @@ icu_unset(intr, handler) return (0); } -struct intrhand * +static void +ithds_init(void *dummy) +{ + + mtx_init(&ithds_table_lock, "ithread table lock", MTX_SPIN); +} +SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL); + +static void +ithread_enable(int vector) +{ + + INTREN(1 << vector); +} + +static void +ithread_disable(int vector) +{ + + INTRDIS(1 << vector); +} + +int inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, - int pri, int flags) + enum intr_type flags, void **cookiep) { - struct ithd *ithd = ithds[irq]; /* descriptor for the IRQ */ - struct intrhand *head; /* chain of handlers for IRQ */ - struct intrhand *idesc; /* descriptor for this handler */ - struct proc *p; /* interrupt thread */ + struct ithd *ithd; /* descriptor for the IRQ */ int errcode = 0; + int created_ithd = 0; - if (name == NULL) /* no name? */ - panic ("anonymous interrupt"); - if (ithd == NULL || ithd->it_ih == NULL) { - /* first handler for this irq. */ - if (ithd == NULL) { - ithd = malloc(sizeof (struct ithd), M_DEVBUF, - M_WAITOK | M_ZERO); - if (ithd == NULL) - return (NULL); - ithd->irq = irq; + /* + * Work around a race where more than one CPU may be registering + * handlers on the same IRQ at the same time. + */ + mtx_lock_spin(&ithds_table_lock); + ithd = ithds[irq]; + mtx_unlock_spin(&ithds_table_lock); + if (ithd == NULL) { + errcode = ithread_create(&ithd, irq, 0, ithread_disable, + ithread_enable, "irq%d:", irq); + if (errcode) + return (errcode); + mtx_lock_spin(&ithds_table_lock); + if (ithds[irq] == NULL) { ithds[irq] = ithd; + created_ithd++; + mtx_unlock_spin(&ithds_table_lock); + } else { + struct ithd *orphan; + + orphan = ithd; + ithd = ithds[irq]; + mtx_unlock_spin(&ithds_table_lock); + ithread_destroy(orphan); } + } + + errcode = ithread_add_handler(ithd, name, handler, arg, + ithread_priority(flags), flags, cookiep); + + if ((flags & INTR_FAST) == 0 || errcode) /* - * If we have a fast interrupt, we need to set the - * handler address directly. Do that below. For a - * slow interrupt, we don't need to know more details, - * so do it here because it's tidier. - */ - if ((flags & INTR_FAST) == 0) { - /* - * Only create a kernel thread if we don't already - * have one. - */ - if (ithd->it_proc == NULL) { - errcode = kthread_create(ithd_loop, NULL, &p, - RFSTOPPED | RFHIGHPID, "irq%d: %s", irq, - name); - if (errcode) - panic("inthand_add: Can't create " - "interrupt thread"); - p->p_intr_nesting_level = 1; - p->p_rtprio.type = RTP_PRIO_ITHREAD; - p->p_stat = SWAIT; /* we're idle */ - - /* Put in linkages. */ - ithd->it_proc = p; - p->p_ithd = ithd; - } else - snprintf(ithd->it_proc->p_comm, MAXCOMLEN, - "irq%d: %s", irq, name); - p->p_rtprio.prio = pri; - - /* - * The interrupt process must be in place, but - * not necessarily schedulable, before we - * initialize the ICU, since it may cause an - * immediate interrupt. - */ - if (icu_setup(irq, &sched_ithd, arg, flags) != 0) - panic("inthand_add: Can't initialize ICU"); - } - } else if ((flags & INTR_EXCL) != 0 - || (ithd->it_ih->ih_flags & INTR_EXCL) != 0) { - /* - * We can't append the new handler if either - * list ithd or new handler do not allow - * interrupts to be shared. + * The interrupt process must be in place, but + * not necessarily schedulable, before we + * initialize the ICU, since it may cause an + * immediate interrupt. */ - if (bootverbose) - printf("\tdevice combination %s and %s " - "doesn't support shared irq%d\n", - ithd->it_ih->ih_name, name, irq); - return(NULL); - } else if (flags & INTR_FAST) { - /* We can only have one fast interrupt by itself. */ - if (bootverbose) - printf("\tCan't add fast interrupt %s" - " to normal interrupt %s on irq%d", - name, ithd->it_ih->ih_name, irq); - return (NULL); - } else { /* update p_comm */ - p = ithd->it_proc; - if (strlen(p->p_comm) + strlen(name) < MAXCOMLEN) { - strcat(p->p_comm, " "); - strcat(p->p_comm, name); - } else if (strlen(p->p_comm) == MAXCOMLEN) - p->p_comm[MAXCOMLEN - 1] = '+'; - else - strcat(p->p_comm, "+"); - } - idesc = malloc(sizeof (struct intrhand), M_DEVBUF, M_WAITOK | M_ZERO); - if (idesc == NULL) - return (NULL); - - idesc->ih_handler = handler; - idesc->ih_argument = arg; - idesc->ih_flags = flags; - idesc->ih_ithd = ithd; - - idesc->ih_name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); - if (idesc->ih_name == NULL) { - free(idesc, M_DEVBUF); - return (NULL); - } - strcpy(idesc->ih_name, name); + if (icu_setup(irq, &sched_ithd, arg, flags) != 0) + panic("inthand_add: Can't initialize ICU"); - /* Slow interrupts got set up above. */ - if ((flags & INTR_FAST) - && (icu_setup(irq, idesc->ih_handler, idesc->ih_argument, - idesc->ih_flags) != 0) ) { - if (bootverbose) + if (errcode) + return (errcode); + + if (flags & INTR_FAST) { + errcode = icu_setup(irq, handler, arg, flags); + if (errcode && bootverbose) printf("\tinthand_add(irq%d) failed, result=%d\n", irq, errcode); - free(idesc->ih_name, M_DEVBUF); - free(idesc, M_DEVBUF); - return NULL; + if (errcode) + return (errcode); } - head = ithd->it_ih; /* look at chain of handlers */ - if (head) { - while (head->ih_next != NULL) - head = head->ih_next; /* find the end */ - head->ih_next = idesc; /* hook it in there */ - } else - ithd->it_ih = idesc; /* put it up front */ - update_intrname(irq, idesc->ih_name); - return (idesc); + + update_intrname(irq, name); + return (0); } /* @@ -673,50 +637,9 @@ inthand_add(const char *name, int irq, driver_intr_t handler, void *arg, * structure to the system. First ensure the handler is not actively * in use. */ - int -inthand_remove(struct intrhand *idesc) +inthand_remove(void *cookie) { - struct ithd *ithd; /* descriptor for the IRQ */ - struct intrhand *ih; /* chain of handlers */ - if (idesc == NULL) - return (-1); - ithd = idesc->ih_ithd; - ih = ithd->it_ih; - - if (ih == idesc) /* first in the chain */ - ithd->it_ih = idesc->ih_next; /* unhook it */ - else { - while ((ih != NULL) - && (ih->ih_next != idesc) ) - ih = ih->ih_next; - if (ih->ih_next != idesc) - return (-1); - ih->ih_next = ih->ih_next->ih_next; - } - - if (ithd->it_ih == NULL) { /* no handlers left, */ - icu_unset(ithd->irq, idesc->ih_handler); - ithds[ithd->irq] = NULL; - - if ((idesc->ih_flags & INTR_FAST) == 0) { - mtx_lock_spin(&sched_lock); - if (ithd->it_proc->p_stat == SWAIT) { - ithd->it_proc->p_intr_nesting_level = 0; - ithd->it_proc->p_stat = SRUN; - setrunqueue(ithd->it_proc); - /* - * We don't do an ast here because we really - * don't care when it runs next. - * - * XXX: should we lower the threads priority? - */ - } - mtx_unlock_spin(&sched_lock); - } - } - free(idesc->ih_name, M_DEVBUF); - free(idesc, M_DEVBUF); - return (0); + return (ithread_remove_handler(cookie)); } |