Fix a minor bug in lwkt_init_thread() (the thread was being added to the
[dragonfly.git] / sys / kern / kern_switch.c
index 36535a8..6ad37fc 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/kern/kern_switch.c,v 1.3.2.1 2000/05/16 06:58:12 dillon Exp $
- * $DragonFly: src/sys/kern/Attic/kern_switch.c,v 1.6 2003/07/10 18:23:24 dillon Exp $
+ * $DragonFly: src/sys/kern/Attic/kern_switch.c,v 1.9 2003/07/25 05:26:50 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -36,6 +36,7 @@
 #include <sys/rtprio.h>
 #include <sys/thread2.h>
 #include <sys/uio.h>
+#include <sys/sysctl.h>
 #include <machine/ipl.h>
 #include <machine/cpu.h>
 
@@ -63,10 +64,20 @@ static struct rq idqueues[NQS];
 static u_int32_t queuebits;
 static u_int32_t rtqueuebits;
 static u_int32_t idqueuebits;
-static u_int32_t curprocmask = -1;
-static u_int32_t rdyprocmask = 0;
+static u_int32_t curprocmask = -1;     /* currently running a user process */
+static u_int32_t rdyprocmask;          /* ready to accept a user process */
 static int      runqcount;
 
+SYSCTL_INT(_debug, OID_AUTO, runqcount, CTLFLAG_RD, &runqcount, 0, "");
+static int usched_steal;
+SYSCTL_INT(_debug, OID_AUTO, usched_steal, CTLFLAG_RW,
+        &usched_steal, 0, "Passive Release was nonoptimal");
+static int usched_optimal;
+SYSCTL_INT(_debug, OID_AUTO, usched_optimal, CTLFLAG_RW,
+        &usched_optimal, 0, "Passive Release was nonoptimal");
+
+#define USCHED_COUNTER(td)     ((td->td_gd == mycpu) ? ++usched_optimal : ++usched_steal)
+
 /*
  * Initialize the run queues at boot time.
  */
@@ -135,6 +146,7 @@ chooseproc(void)
        p = TAILQ_FIRST(q);
        KASSERT(p, ("chooseproc: no proc on busy queue"));
        TAILQ_REMOVE(q, p, p_procq);
+       --runqcount;
        if (TAILQ_EMPTY(q))
                *which &= ~(1 << pri);
        KASSERT((p->p_flag & P_ONRUNQ) != 0, ("not on runq6!"));
@@ -158,6 +170,18 @@ chooseproc(void)
  * signal other cpus in the system that may need to be woken up to service
  * the new 'user' process.
  *
+ * If P_PASSIVE_ACQ is set setrunqueue() will not wakeup potential target
+ * cpus in an attempt to keep the process on the current cpu at least for
+ * a little while to take advantage of locality of reference (e.g. fork/exec
+ * or short fork/exit).
+ *
+ * WARNING! a thread can be acquired by another cpu the moment it is put
+ * on the user scheduler's run queue AND we release the MP lock.  Since we
+ * release the MP lock before switching out another cpu may begin stealing
+ * our current thread before we are completely switched out!  The 
+ * lwkt_acquire() function will stall until TDF_RUNNING is cleared on the
+ * thread before stealing it.
+ *
  * The associated thread must NOT be scheduled.  
  * The process must be runnable.
  * This must be called at splhigh().
@@ -186,6 +210,21 @@ setrunqueue(struct proc *p)
                return;
        }
 
+       /*
+        * If our cpu is available to run a user process we short cut and
+        * just do it.
+        */
+       cpuid = mycpu->gd_cpuid;
+       if ((curprocmask & (1 << cpuid)) == 0) {
+               curprocmask |= 1 << cpuid;
+               p->p_flag |= P_CURPROC;
+               USCHED_COUNTER(p->p_thread);
+               lwkt_acquire(p->p_thread);
+               lwkt_schedule(p->p_thread);
+               crit_exit();
+               return;
+       }
+
        /*
         * Otherwise place this process on the userland scheduler's run
         * queue for action.
@@ -216,9 +255,15 @@ setrunqueue(struct proc *p)
         * Wakeup other cpus to schedule the newly available thread.
         * XXX doesn't really have to be in a critical section.
         * We own giant after all.
+        *
+        * We use rdyprocmask to avoid unnecessarily waking up the scheduler
+        * thread when it is already running.
         */
-       if ((mask = ~curprocmask & rdyprocmask & mycpu->gd_other_cpus) != 0) {
+       if ((mask = ~curprocmask & rdyprocmask & mycpu->gd_other_cpus) != 0 &&
+           (p->p_flag & P_PASSIVE_ACQ) == 0) {
                int count = runqcount;
+               if (!mask)
+                       printf("PROC %d nocpu to schedule it on\n", p->p_pid);
                while (mask && count) {
                        cpuid = bsfl(mask);
                        KKASSERT((curprocmask & (1 << cpuid)) == 0);
@@ -296,11 +341,11 @@ release_curproc(struct proc *p)
        struct proc *np;
 
 #ifdef ONLY_ONE_USER_CPU
-       KKASSERT(mycpu->gd_cpuid == 0 && p->p_thread->td_cpu == 0);
+       KKASSERT(mycpu->gd_cpuid == 0 && p->p_thread->td_gd == mycpu);
 #endif
        crit_enter();
        clear_resched();
-       cpuid = p->p_thread->td_cpu;
+       cpuid = p->p_thread->td_gd->gd_cpuid;
        p->p_flag |= P_CP_RELEASED;
        if (p->p_flag & P_CURPROC) {
                p->p_flag &= ~P_CURPROC;
@@ -308,6 +353,7 @@ release_curproc(struct proc *p)
                        KKASSERT(curprocmask & (1 << cpuid));
                        if ((np = chooseproc()) != NULL) {
                                np->p_flag |= P_CURPROC;
+                               USCHED_COUNTER(np->p_thread);
                                lwkt_acquire(np->p_thread);
                                lwkt_schedule(np->p_thread);
                        } else {
@@ -315,6 +361,7 @@ release_curproc(struct proc *p)
                        }
                        rel_mplock();
                } else {
+                       KKASSERT(0);
                        curprocmask &= ~(1 << cpuid);
                        if (rdyprocmask & (1 << cpuid))
                                lwkt_schedule(&globaldata_find(cpuid)->gd_schedthread);
@@ -358,18 +405,19 @@ acquire_curproc(struct proc *p)
         * will mess w/ this proc?  Or will it?  What about curprocmask?
         */
 #ifdef ONLY_ONE_USER_CPU
-       KKASSERT(mycpu->gd_cpuid == 0 && p->p_thread->td_cpu == 0);
+       KKASSERT(mycpu->gd_cpuid == 0 && p->p_thread->td_gd == mycpu);
 #endif
        crit_enter();
        p->p_flag &= ~P_CP_RELEASED;
        while ((p->p_flag & P_CURPROC) == 0) {
-               cpuid = p->p_thread->td_cpu;    /* load/reload cpuid */
+               cpuid = p->p_thread->td_gd->gd_cpuid;   /* load/reload cpuid */
                if ((curprocmask & (1 << cpuid)) == 0) {
                        curprocmask |= 1 << cpuid;
                        if ((np = chooseproc()) != NULL) {
                                KKASSERT((np->p_flag & P_CP_RELEASED) == 0);
                                if (test_resched(p, np)) {
                                        np->p_flag |= P_CURPROC;
+                                       USCHED_COUNTER(np->p_thread);
                                        lwkt_acquire(np->p_thread);
                                        lwkt_schedule(np->p_thread);
                                } else {
@@ -411,8 +459,10 @@ uio_yield(void)
 
        lwkt_switch();
        if (p) {
+               p->p_flag |= P_PASSIVE_ACQ;
                acquire_curproc(p);
                release_curproc(p);
+               p->p_flag &= ~P_PASSIVE_ACQ;
        }
 }
 
@@ -442,12 +492,13 @@ sched_thread(void *dummy)
     for (;;) {
        struct proc *np;
 
-       rdyprocmask |= cpumask;
        lwkt_deschedule_self();         /* interlock */
+       rdyprocmask |= cpumask;
        crit_enter();
        if ((curprocmask & cpumask) == 0 && (np = chooseproc()) != NULL) {
            curprocmask |= cpumask;
            np->p_flag |= P_CURPROC;
+           USCHED_COUNTER(np->p_thread);
            lwkt_acquire(np->p_thread);
            lwkt_schedule(np->p_thread);
        }