Fix a minor bug in lwkt_init_thread() (the thread was being added to the
[dragonfly.git] / sys / kern / kern_intr.c
index 929d37a..e045c6e 100644 (file)
@@ -24,7 +24,7 @@
  * 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.5 2003/06/29 07:37:06 dillon Exp $
+ * $DragonFly: src/sys/kern/kern_intr.c,v 1.10 2003/07/25 05:26:50 dillon Exp $
  *
  */
 
@@ -36,6 +36,7 @@
 #include <sys/thread.h>
 #include <sys/proc.h>
 #include <sys/thread2.h>
+#include <sys/random.h>
 
 #include <machine/ipl.h>
 
@@ -52,18 +53,20 @@ typedef struct intrec {
 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);
 
-void
+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);
-    register_int(intr, handler, arg, name);
+    return(register_int(intr, handler, arg, name));
 }
 
-void
+thread_t
 register_int(int intr, inthand2_t *handler, void *arg, const char *name)
 {
     intrec_t **list;
@@ -86,16 +89,18 @@ register_int(int intr, inthand2_t *handler, void *arg, const char *name)
 
     /*
      * Create an interrupt thread if necessary, leave it in an unscheduled
-     * state.
+     * 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);
+           lwkt_setpri(td, TDPRI_SOFT_NORM + TDPRI_CRIT * 2);
        else
-           lwkt_setpri(td, TDPRI_INT_MED);
+           lwkt_setpri(td, TDPRI_INT_MED + TDPRI_CRIT * 2);
     }
 
     /*
@@ -106,6 +111,7 @@ register_int(int intr, inthand2_t *handler, void *arg, const char *name)
        list = &(*list)->next;
     *list = rec;
     crit_exit();
+    return(td);
 }
 
 void
@@ -153,10 +159,39 @@ swi_setpriority(int intr, int pri)
        lwkt_setpri(td, pri);
 }
 
+void
+register_randintr(int intr)
+{
+    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)
 {
@@ -166,8 +201,12 @@ sched_ithd(int intr)
        if (intlists[intr] == NULL) {
            printf("sched_ithd: stray interrupt %d\n", intr);
        } else {
-           lwkt_schedule(td);
-           lwkt_preempt(td, intr);
+           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);
@@ -175,7 +214,8 @@ sched_ithd(int intr)
 }
 
 /*
- * Interrupt threads run this as their main loop.
+ * Interrupt threads run this as their main loop.  The handler should be
+ * in a critical section on entry.
  */
 static void
 ithread_handler(void *arg)
@@ -184,28 +224,22 @@ ithread_handler(void *arg)
     intrec_t **list = &intlists[intr];
     intrec_t *rec;
     intrec_t *nrec;
-    int xpri = (curthread->td_pri & TDPRI_MASK) + TDPRI_CRIT; /* DEBUG YYY */
+    struct random_softc *sc = &irandom_ary[intr];
 
-    crit_enter();      /* replaces SPLs */
+    KKASSERT(curthread->td_pri >= TDPRI_CRIT);
     for (;;) {
+       irunning[intr] = 0;
        for (rec = *list; rec; rec = nrec) {
            nrec = rec->next;
            rec->handler(rec->argument);
        }
-       ithread_done(intr);
-
-       /*
-        * temporary sanity check.  If we preempted another thread we
-        * are placed in another critical section by that thread which
-        * will be released when we block and resume the original thread.
-        */
-       KKASSERT(curthread->td_pri == xpri ||
-       (curthread->td_preempted && curthread->td_pri == xpri + TDPRI_CRIT));
+       if (sc->sc_enabled)
+           add_interrupt_randomness(intr);
+       if (irunning[intr] == 0)
+           ithread_done(intr);
     }
-    crit_exit();       /* not reached */
 }
 
-
 /* 
  * Sysctls used by systat and others: hw.intrnames and hw.intrcnt.
  * The data for this machine dependent, and the declarations are in machine