X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/blobdiff_plain/984263bcb83ad82313113c6ac840d99124d8f90c..a72187e94edafbd6b8a53afd63f7001850806f5e:/sys/kern/kern_intr.c diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c index fcdc7b24ae..e045c6e2f5 100644 --- a/sys/kern/kern_intr.c +++ b/sys/kern/kern_intr.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, Stefan Esser - * All rights reserved. + * Copyright (c) 2003, Matthew Dillon All rights reserved. + * Copyright (c) 1997, Stefan Esser All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,110 +24,220 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/kern/kern_intr.c,v 1.24.2.1 2001/10/14 20:05:50 luigi Exp $ + * $DragonFly: src/sys/kern/kern_intr.c,v 1.10 2003/07/25 05:26:50 dillon Exp $ * */ - #include #include #include #include #include +#include +#include +#include +#include #include #include -struct swilist { - swihand_t *sl_handler; - struct swilist *sl_next; -}; +typedef struct intrec { + struct intrec *next; + inthand2_t *handler; + void *argument; + const char *name; + int intr; +} intrec_t; + +static intrec_t *intlists[NHWI+NSWI]; +static thread_t ithreads[NHWI+NSWI]; +static struct thread ithread_ary[NHWI+NSWI]; +static struct random_softc irandom_ary[NHWI+NSWI]; +static int irunning[NHWI+NSWI]; + +static void ithread_handler(void *arg); + +thread_t +register_swi(int intr, inthand2_t *handler, void *arg, const char *name) +{ + if (intr < NHWI || intr >= NHWI + NSWI) + panic("register_swi: bad intr %d", intr); + return(register_int(intr, handler, arg, name)); +} + +thread_t +register_int(int intr, inthand2_t *handler, void *arg, const char *name) +{ + intrec_t **list; + intrec_t *rec; + thread_t td; + + if (intr < 0 || intr > NHWI + NSWI) + panic("register_int: bad intr %d", intr); + + rec = malloc(sizeof(intrec_t), M_DEVBUF, M_NOWAIT); + if (rec == NULL) + panic("register_swi: malloc failed"); + rec->handler = handler; + rec->argument = arg; + rec->name = name; + rec->intr = intr; + rec->next = NULL; + + list = &intlists[intr]; -static struct swilist swilists[NSWI]; + /* + * Create an interrupt thread if necessary, leave it in an unscheduled + * state. The kthread restore function exits a critical section before + * starting the function so we need *TWO* critical sections in order + * for the handler to begin running in one. + */ + if ((td = ithreads[intr]) == NULL) { + lwkt_create((void *)ithread_handler, (void *)intr, &ithreads[intr], + &ithread_ary[intr], TDF_STOPREQ, "ithread %d", intr); + td = ithreads[intr]; + if (intr >= NHWI && intr < NHWI + NSWI) + lwkt_setpri(td, TDPRI_SOFT_NORM + TDPRI_CRIT * 2); + else + lwkt_setpri(td, TDPRI_INT_MED + TDPRI_CRIT * 2); + } + + /* + * Add the record to the interrupt list + */ + crit_enter(); /* token */ + while (*list != NULL) + list = &(*list)->next; + *list = rec; + crit_exit(); + return(td); +} + +void +unregister_swi(int intr, inthand2_t *handler) +{ + if (intr < NHWI || intr >= NHWI + NSWI) + panic("register_swi: bad intr %d", intr); + unregister_int(intr, handler); +} void -register_swi(intr, handler) - int intr; - swihand_t *handler; +unregister_int(int intr, inthand2_t handler) { - struct swilist *slp, *slq; - int s; - - if (intr < NHWI || intr >= NHWI + NSWI) - panic("register_swi: bad intr %d", intr); - if (handler == swi_generic || handler == swi_null) - panic("register_swi: bad handler %p", (void *)handler); - slp = &swilists[intr - NHWI]; - s = splhigh(); - if (ihandlers[intr] == swi_null) - ihandlers[intr] = handler; - else { - if (slp->sl_next == NULL) { - slp->sl_handler = ihandlers[intr]; - ihandlers[intr] = swi_generic; - } - slq = malloc(sizeof(*slq), M_DEVBUF, M_NOWAIT); - if (slq == NULL) - panic("register_swi: malloc failed"); - slq->sl_handler = handler; - slq->sl_next = NULL; - while (slp->sl_next != NULL) - slp = slp->sl_next; - slp->sl_next = slq; + intrec_t **list; + intrec_t *rec; + + if (intr < 0 || intr > NHWI + NSWI) + panic("register_int: bad intr %d", intr); + list = &intlists[intr]; + crit_enter(); + while ((rec = *list) != NULL) { + if (rec->handler == (void *)handler) { + *list = rec->next; + break; } - splx(s); + list = &rec->next; + } + crit_exit(); + if (rec != NULL) { + free(rec, M_DEVBUF); + } else { + printf("warning: unregister_int: int %d handler %p not found\n", + intr, handler); + } } void -swi_dispatcher(intr) - int intr; +swi_setpriority(int intr, int pri) { - struct swilist *slp; + struct thread *td; - slp = &swilists[intr - NHWI]; - do { - (*slp->sl_handler)(); - slp = slp->sl_next; - } while (slp != NULL); + if (intr < NHWI || intr >= NHWI + NSWI) + panic("register_swi: bad intr %d", intr); + if ((td = ithreads[intr]) != NULL) + lwkt_setpri(td, pri); } void -unregister_swi(intr, handler) - int intr; - swihand_t *handler; +register_randintr(int intr) { - struct swilist *slfoundpred, *slp, *slq; - int s; - - if (intr < NHWI || intr >= NHWI + NSWI) - panic("unregister_swi: bad intr %d", intr); - if (handler == swi_generic || handler == swi_null) - panic("unregister_swi: bad handler %p", (void *)handler); - slp = &swilists[intr - NHWI]; - s = splhigh(); - if (ihandlers[intr] == handler) - ihandlers[intr] = swi_null; - else if (slp->sl_next != NULL) { - slfoundpred = NULL; - for (slq = slp->sl_next; slq != NULL; - slp = slq, slq = slp->sl_next) - if (slq->sl_handler == handler) - slfoundpred = slp; - slp = &swilists[intr - NHWI]; - if (slfoundpred != NULL) { - slq = slfoundpred->sl_next; - slfoundpred->sl_next = slq->sl_next; - free(slq, M_DEVBUF); - } else if (slp->sl_handler == handler) { - slq = slp->sl_next; - slp->sl_next = slq->sl_next; - slp->sl_handler = slq->sl_handler; - free(slq, M_DEVBUF); - } - if (slp->sl_next == NULL) - ihandlers[intr] = slp->sl_handler; + struct random_softc *sc = &irandom_ary[intr]; + sc->sc_intr = intr; + sc->sc_enabled = 1; +} + +void +unregister_randintr(int intr) +{ + struct random_softc *sc = &irandom_ary[intr]; + sc->sc_enabled = 0; +} + +/* + * Dispatch an interrupt. If there's nothing to do we have a stray + * interrupt and can just return, leaving the interrupt masked. + * + * We need to schedule the interrupt and set its irunning[] bit. If + * we are not on the interrupt thread's cpu we have to send a message + * to the correct cpu that will issue the desired action (interlocking + * with the interrupt thread's critical section). + * + * We are NOT in a critical section, which will allow the scheduled + * interrupt to preempt us. + */ +static void +sched_ithd_remote(void *arg) +{ + sched_ithd((int)arg); +} + +void +sched_ithd(int intr) +{ + thread_t td; + + if ((td = ithreads[intr]) != NULL) { + if (intlists[intr] == NULL) { + printf("sched_ithd: stray interrupt %d\n", intr); + } else { + if (td->td_gd == mycpu) { + irunning[intr] = 1; + lwkt_schedule(td); /* preemption handled internally */ + } else { + lwkt_send_ipiq(td->td_gd->gd_cpuid, sched_ithd_remote, (void *)intr); + } + } + } else { + printf("sched_ithd: stray interrupt %d\n", intr); + } +} + +/* + * Interrupt threads run this as their main loop. The handler should be + * in a critical section on entry. + */ +static void +ithread_handler(void *arg) +{ + int intr = (int)arg; + intrec_t **list = &intlists[intr]; + intrec_t *rec; + intrec_t *nrec; + struct random_softc *sc = &irandom_ary[intr]; + + KKASSERT(curthread->td_pri >= TDPRI_CRIT); + for (;;) { + irunning[intr] = 0; + for (rec = *list; rec; rec = nrec) { + nrec = rec->next; + rec->handler(rec->argument); } - splx(s); + if (sc->sc_enabled) + add_interrupt_randomness(intr); + if (irunning[intr] == 0) + ithread_done(intr); + } } /*