Fix a minor bug in lwkt_init_thread() (the thread was being added to the
[dragonfly.git] / sys / kern / kern_intr.c
index fcdc7b2..e045c6e 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * All rights reserved.
+ * Copyright (c) 2003, Matthew Dillon <dillon@backplane.com> All rights reserved.
+ * Copyright (c) 1997, Stefan Esser <se@freebsd.org> All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * 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 <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
 #include <sys/sysctl.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/thread2.h>
+#include <sys/random.h>
 
 #include <machine/ipl.h>
 
 #include <sys/interrupt.h>
 
-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);
+    }
 }
 
 /*