Cleanup P_CURPROC and P_CP_RELEASED handling. P_CP_RELEASED prevents the
authorMatthew Dillon <dillon@dragonflybsd.org>
Fri, 17 Oct 2003 07:30:43 +0000 (07:30 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Fri, 17 Oct 2003 07:30:43 +0000 (07:30 +0000)
userland scheduler from messing with a process and must be set (in a lazy
fashion) when a thread that came in from userland is about to block in
the kernel.  P_CURPROC is used to manage which threads belonging to
processes controlled by the userland scheduler are scheduled by LWKT.
The userland scheduler has also been streamlined considerably.

p_priority now has a rollup priority that can be compared directly,
regardless of the realtime/idle/whatever queue a process is on.
p_priority has been changed from an unsigned char to a signed short.

schedclock is now distributed, so 'top' shows the correct values.

A huge amount of cpu affinity code has been added to the scheduler.  It is
not working 100% yet, but the framework is there (see kern/kern_switch.c).
globaldata now has gd_upri to support remote reschedule requests issued
by the affinity code.  There are two goals to the new scheduling code for
MP boxes: (1) so niced/idleprio cpu bound programs do not interfere
*at* *all* with other things (like, say, a buildworld).  (2) to reduce
the flip-flopping of processes between cpus.

lwkt_gettoken() now increments the generation number unconditionally so
kernel code can detect when other processes have used a token.

lwkt_send_ipiq_mask() removes stopped cpus from the mask automatically.

sys/i386/i386/trap.c
sys/kern/kern_clock.c
sys/kern/kern_exit.c
sys/kern/kern_switch.c
sys/kern/kern_synch.c
sys/kern/lwkt_thread.c
sys/platform/pc32/i386/trap.c
sys/sys/globaldata.h
sys/sys/param.h
sys/sys/proc.h

index 4ca3bf8..2f7e01f 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)trap.c        7.4 (Berkeley) 5/13/91
  * $FreeBSD: src/sys/i386/i386/trap.c,v 1.147.2.11 2003/02/27 19:09:59 luoqi Exp $
- * $DragonFly: src/sys/i386/i386/Attic/trap.c,v 1.35 2003/10/16 22:26:35 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/trap.c,v 1.36 2003/10/17 07:30:43 dillon Exp $
  */
 
 /*
@@ -175,19 +175,25 @@ MALLOC_DEFINE(M_SYSMSG, "sysmsg", "sysmsg structure");
  * point of view of the userland scheduler unless we actually have to
  * switch.
  *
- * usertdsw is called from within a critical section and the BGL will still
- * be held.  This function is NOT called for preemptions, only for switchouts.
+ * passive_release is called from within a critical section and the BGL will
+ * still be held.  This function is NOT called for preemptions, only for
+ * switchouts.  We only actually release our P_CURPROC designation when we
+ * are going to sleep, otherwise another process might be assigned P_CURPROC
+ * unnecessarily.
  */
 static void
 passive_release(struct thread *td)
 {
        struct proc *p = td->td_proc;
 
-       lwkt_setpri_self(TDPRI_KERN_USER);
-       if (p->p_flag & P_CURPROC)
-               release_curproc(p, (td->td_flags & TDF_RUNQ) == 0);
-       if ((p->p_flag & P_CURPROC) == 0)
+       if ((p->p_flag & P_CP_RELEASED) == 0) {
+               p->p_flag |= P_CP_RELEASED;
+               lwkt_setpri_self(TDPRI_KERN_USER);
+       }
+       if ((p->p_flag & P_CURPROC) && (td->td_flags & TDF_RUNQ) == 0) {
                td->td_release = NULL;
+               release_curproc(p);
+       }
 }
 
 /*
@@ -210,19 +216,20 @@ userexit(struct proc *p)
        /*
         * Reacquire our P_CURPROC status and adjust the LWKT priority
         * for our return to userland.  We can fast path the case where
-        * td_release was not called and P_CURPROC is still set, otherwise
-        * do it the slow way.
+        * td_release was not called by checking particular proc flags.
+        * Otherwise we do it the slow way.
         *
         * Lowering our priority may make other higher priority threads
         * runnable. lwkt_setpri_self() does not switch away, so call
         * lwkt_maybe_switch() to deal with it.
         */
-       if (td->td_release && (p->p_flag & P_CURPROC)) {
+       td->td_release = NULL;
+       if ((p->p_flag & (P_CP_RELEASED|P_CURPROC)) == P_CURPROC) {
                ++fast_release;
-               td->td_release = NULL;
        } else {
                ++slow_release;
                acquire_curproc(p);
+
                switch(p->p_rtprio.type) {
                case RTP_PRIO_IDLE:
                        lwkt_setpri_self(TDPRI_USER_IDLE);
@@ -235,8 +242,8 @@ userexit(struct proc *p)
                        lwkt_setpri_self(TDPRI_USER_NORM);
                        break;
                }
-               lwkt_maybe_switch();
        }
+       lwkt_maybe_switch();
 }
 
 
@@ -253,18 +260,22 @@ userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
        }
 
        /*
-        * If a reschedule has been requested then the easiest solution
-        * is to run our passive release function which will possibly
-        * shift our P_CURPROC designation to another user process. 
-        *
-        * A reschedule can also occur due to a higher priority LWKT thread
-        * becoming runable, we have to call lwkt_maybe_switch() to deal
-        * with it.
+        * If a reschedule has been requested then we release the current
+        * process in order to shift our P_CURPROC designation to another
+        * user process.  userexit() will reacquire P_CURPROC and block
+        * there.
         */
        if (resched_wanted()) {
-               if (curthread->td_release)
-                       passive_release(curthread);
-               lwkt_maybe_switch();
+               p->p_thread->td_release = NULL;
+               if ((p->p_flag & P_CP_RELEASED) == 0) {
+                       p->p_flag |= P_CP_RELEASED;
+                       lwkt_setpri_self(TDPRI_KERN_USER);
+               }
+               if (p->p_flag & P_CURPROC) {
+                       release_curproc(p);
+               } else {
+                       clear_resched();
+               }
        }
 
        /*
index 96939ef..3823153 100644 (file)
@@ -38,7 +38,7 @@
  *
  *     @(#)kern_clock.c        8.5 (Berkeley) 1/21/94
  * $FreeBSD: src/sys/kern/kern_clock.c,v 1.105.2.10 2002/10/17 13:19:40 maxim Exp $
- * $DragonFly: src/sys/kern/kern_clock.c,v 1.11 2003/08/26 21:09:02 rob Exp $
+ * $DragonFly: src/sys/kern/kern_clock.c,v 1.12 2003/10/17 07:30:42 dillon Exp $
  */
 
 #include "opt_ntp.h"
@@ -494,9 +494,17 @@ statclock(frame)
        mycpu->gd_psdiv = psdiv;
        mycpu->gd_psticks = psticks + psdiv;
 
-       if (p != NULL) {
-               schedclock(p);
+       /*
+        * XXX YYY DragonFly... need to rewrite all of this,
+        * only schedclock is distributed at the moment
+        */
+       schedclock(NULL);
+#ifdef SMP
+       if (smp_started && invltlb_ok && !cold && !panicstr) /* YYY */
+               lwkt_send_ipiq_mask(mycpu->gd_other_cpus, schedclock, NULL);
+#endif
 
+       if (p != NULL) {
                /* Update resource usage integrals and maximums. */
                if ((pstats = p->p_stats) != NULL &&
                    (ru = &pstats->p_ru) != NULL &&
index 0e97c25..fd9ebf4 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)kern_exit.c 8.7 (Berkeley) 2/12/94
  * $FreeBSD: src/sys/kern/kern_exit.c,v 1.92.2.11 2003/01/13 22:51:16 dillon Exp $
- * $DragonFly: src/sys/kern/kern_exit.c,v 1.24 2003/10/16 22:26:37 dillon Exp $
+ * $DragonFly: src/sys/kern/kern_exit.c,v 1.25 2003/10/17 07:30:42 dillon Exp $
  */
 
 #include "opt_compat.h"
@@ -367,7 +367,7 @@ exit1(int rv)
         * Release the P_CURPROC designation on the process so the userland
         * scheduler can work in someone else.
         */
-       release_curproc(p, 1);
+       release_curproc(p);
 
        /*
         * Finally, call machine-dependent code to release the remaining
index 7601009..4f3f4dd 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.10 2003/10/16 22:26:37 dillon Exp $
+ * $DragonFly: src/sys/kern/Attic/kern_switch.c,v 1.11 2003/10/17 07:30:42 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -67,6 +67,9 @@ static u_int32_t idqueuebits;
 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;
+#ifdef SMP
+static int      scancpu;
+#endif
 
 SYSCTL_INT(_debug, OID_AUTO, runqcount, CTLFLAG_RD, &runqcount, 0, "");
 static int usched_steal;
@@ -75,6 +78,20 @@ SYSCTL_INT(_debug, OID_AUTO, usched_steal, CTLFLAG_RW,
 static int usched_optimal;
 SYSCTL_INT(_debug, OID_AUTO, usched_optimal, CTLFLAG_RW,
         &usched_optimal, 0, "Passive Release was nonoptimal");
+#ifdef SMP
+static int remote_resched = 1;
+static int remote_resched_nonaffinity;
+static int remote_resched_affinity;
+static int choose_affinity;
+SYSCTL_INT(_debug, OID_AUTO, remote_resched, CTLFLAG_RW,
+        &remote_resched, 0, "Resched to another cpu");
+SYSCTL_INT(_debug, OID_AUTO, remote_resched_nonaffinity, CTLFLAG_RD,
+        &remote_resched_nonaffinity, 0, "Number of remote rescheds");
+SYSCTL_INT(_debug, OID_AUTO, remote_resched_affinity, CTLFLAG_RD,
+        &remote_resched_affinity, 0, "Number of remote rescheds");
+SYSCTL_INT(_debug, OID_AUTO, choose_affinity, CTLFLAG_RD,
+        &choose_affinity, 0, "chooseproc() was smart");
+#endif
 
 #define USCHED_COUNTER(td)     ((td->td_gd == mycpu) ? ++usched_optimal : ++usched_steal)
 
@@ -99,19 +116,12 @@ rqinit(void *dummy)
 }
 SYSINIT(runqueue, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, rqinit, NULL)
 
-static int
+static __inline
+int
 test_resched(struct proc *curp, struct proc *newp)
 {
-       if (newp->p_rtprio.type < curp->p_rtprio.type)
+       if (newp->p_priority / PPQ <= curp->p_priority / PPQ)
                return(1);
-       if (newp->p_rtprio.type == curp->p_rtprio.type) {
-               if (newp->p_rtprio.type == RTP_PRIO_NORMAL) {
-                       if (newp->p_priority / PPQ <= curp->p_priority / PPQ)
-                               return(1);
-               } else if (newp->p_rtprio.prio < curp->p_rtprio.prio) {
-                       return(1);
-               }
-       }
        return(0);
 }
 
@@ -121,7 +131,7 @@ test_resched(struct proc *curp, struct proc *newp)
  */
 static
 struct proc *
-chooseproc(void)
+chooseproc(struct proc *chkp)
 {
        struct proc *p;
        struct rq *q;
@@ -145,6 +155,31 @@ chooseproc(void)
        }
        p = TAILQ_FIRST(q);
        KASSERT(p, ("chooseproc: no proc on busy queue"));
+
+       /*
+        * If the chosen process is not at a higher priority then chkp
+        * then return NULL without dequeueing a new process.
+        */
+       if (chkp && !test_resched(chkp, p))
+               return(NULL);
+
+#ifdef SMP
+       /*
+        * If the chosen process does not reside on this cpu spend a few
+        * cycles looking for a better candidate at the same priority level.
+        * This is a fallback check, setrunqueue() tries to wakeup the
+        * correct cpu and is our front-line affinity.
+        */
+       if (p->p_thread->td_gd != mycpu &&
+           (chkp = TAILQ_NEXT(p, p_procq)) != NULL
+       ) {
+               if (chkp->p_thread->td_gd == mycpu) {
+                       ++choose_affinity;
+                       p = chkp;
+               }
+       }
+#endif
+
        TAILQ_REMOVE(q, p, p_procq);
        --runqcount;
        if (TAILQ_EMPTY(q))
@@ -154,6 +189,18 @@ chooseproc(void)
        return p;
 }
 
+#ifdef SMP
+/*
+ * called via an ipi message to reschedule on another cpu.
+ */
+static
+void
+need_resched_remote(void *dummy)
+{
+       need_resched();
+}
+
+#endif
 
 /*
  * setrunqueue() 'wakes up' a 'user' process, which can mean several things.
@@ -175,6 +222,12 @@ chooseproc(void)
  * a little while to take advantage of locality of reference (e.g. fork/exec
  * or short fork/exit).
  *
+ * CPU AFFINITY: cpu affinity is handled by attempting to either schedule
+ * or (user level) preempt on the same cpu that a process was previously
+ * scheduled to.  If we cannot do this but we are at enough of a higher
+ * priority then the processes running on other cpus, we will allow the
+ * process to be stolen by another cpu.
+ *
  * 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
@@ -190,9 +243,13 @@ void
 setrunqueue(struct proc *p)
 {
        struct rq *q;
+       struct globaldata *gd;
        int pri;
        int cpuid;
+#ifdef SMP
+       int count;
        u_int32_t mask;
+#endif
 
        crit_enter();
        KASSERT(p->p_stat == SRUN, ("setrunqueue: proc not SRUN"));
@@ -211,28 +268,40 @@ setrunqueue(struct proc *p)
        }
 
        /*
-        * If our cpu is available to run a user process we short cut and
-        * just do it.
+        * Check cpu affinity.  The associated thread is stable at the
+        * moment.  Note that we may be checking another cpu here so we
+        * have to be careful.  Note that gd_upri only counts when the
+        * curprocmask bit is set for the cpu in question, and since it is
+        * only a hint we can modify it on another cpu's globaldata structure.
+        * We use it to prevent unnecessary IPIs (hence the - PPQ).
         */
-       cpuid = mycpu->gd_cpuid;
+       gd = p->p_thread->td_gd;
+       cpuid = gd->gd_cpuid;
+
        if ((curprocmask & (1 << cpuid)) == 0) {
                curprocmask |= 1 << cpuid;
                p->p_flag |= P_CURPROC;
+               gd->gd_upri = p->p_priority;
                USCHED_COUNTER(p->p_thread);
-               lwkt_acquire(p->p_thread);
                lwkt_schedule(p->p_thread);
+               /* CANNOT TOUCH PROC OR TD AFTER SCHEDULE CALL TO REMOTE CPU */
                crit_exit();
+#ifdef SMP
+               if (gd != mycpu)
+                       ++remote_resched_affinity;
+#endif
                return;
        }
 
        /*
-        * Otherwise place this process on the userland scheduler's run
-        * queue for action.
+        * gd and cpuid may still 'hint' at another cpu.  Even so we have
+        * to place this process on the userland scheduler's run queue for
+        * action by the target cpu.
         */
        ++runqcount;
        p->p_flag |= P_ONRUNQ;
        if (p->p_rtprio.type == RTP_PRIO_NORMAL) {
-               pri = p->p_priority >> 2;
+               pri = (p->p_priority & PRIMASK) >> 2;
                q = &queues[pri];
                queuebits |= 1 << pri;
        } else if (p->p_rtprio.type == RTP_PRIO_REALTIME ||
@@ -251,17 +320,42 @@ setrunqueue(struct proc *p)
        p->p_rqindex = pri;             /* remember the queue index */
        TAILQ_INSERT_TAIL(q, p, p_procq);
 
+#ifdef SMP
        /*
-        * 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.
+        * Either wakeup other cpus user thread scheduler or request 
+        * preemption on other cpus (which will also wakeup a HLT).
         *
-        * We use rdyprocmask to avoid unnecessarily waking up the scheduler
-        * thread when it is already running.
+        * NOTE!  gd and cpuid may still be our 'hint', not our current
+        * cpu info.
+        */
+
+       count = runqcount;
+
+       /*
+        * Check cpu affinity for user preemption (when the curprocmask bit
+        * is set)
+        */
+       if (gd == mycpu) {
+               if (p->p_priority / PPQ < gd->gd_upri / PPQ) {
+                       need_resched();
+                       --count;
+               }
+       } else if (remote_resched) {
+               if (p->p_priority / PPQ < gd->gd_upri / PPQ) {
+                       gd->gd_upri = p->p_priority;
+                       lwkt_send_ipiq(cpuid, need_resched_remote, NULL);
+                       --count;
+                       ++remote_resched_affinity;
+               }
+       }
+
+       /*
+        * No affinity, first schedule to any cpus that do not have a current
+        * process.  If there is a free cpu we always schedule to it.
         */
-       if ((mask = ~curprocmask & rdyprocmask & mycpu->gd_other_cpus) != 0 &&
+       if (count &&
+           (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) {
@@ -273,6 +367,35 @@ setrunqueue(struct proc *p)
                        mask &= ~(1 << cpuid);
                }
        }
+
+       /*
+        * If there are still runnable processes try to wakeup a random
+        * cpu that is running a much lower priority process in order to
+        * preempt on it.  Note that gd_upri is only a hint, so we can
+        * overwrite it from the wrong cpu.   If we can't find one, we
+        * are SOL.
+        *
+        * We depress the priority check so multiple cpu bound programs
+        * do not bounce between cpus.  Remember that the clock interrupt
+        * will also cause all cpus to reschedule.
+        */
+       if (count && remote_resched && ncpus > 1) {
+               cpuid = scancpu;
+               do {
+                       if (++cpuid == ncpus)
+                               cpuid = 0;
+               } while (cpuid == mycpu->gd_cpuid);
+               scancpu = cpuid;
+
+               gd = globaldata_find(cpuid);
+
+               if (p->p_priority / PPQ < gd->gd_upri / PPQ - 2) {
+                       gd->gd_upri = p->p_priority;
+                       lwkt_send_ipiq(cpuid, need_resched_remote, NULL);
+                       ++remote_resched_nonaffinity;
+               }
+       }
+#endif
        crit_exit();
 }
 
@@ -323,10 +446,11 @@ remrunqueue(struct proc *p)
 }
 
 /*
- * Release the P_CURPROC designation on the CURRENT process only.  This
- * will allow another userland process to be scheduled.  If we do not
- * have or cannot get the MP lock we just wakeup the scheduler thread for
- * this cpu.
+ * Release the P_CURPROC designation on the current process for this cpu
+ * and attempt to assign a new current process from the run queue.
+ *
+ * If we do not have or cannot get the MP lock we just wakeup the userland
+ * helper scheduler thread for this cpu.
  *
  * WARNING!  The MP lock may be in an unsynchronized state due to the
  * way get_mplock() works and the fact that this function may be called
@@ -335,7 +459,7 @@ remrunqueue(struct proc *p)
  * useable.
  */
 void
-release_curproc(struct proc *p, int force)
+release_curproc(struct proc *p)
 {
        int cpuid;
        struct proc *np;
@@ -346,31 +470,38 @@ release_curproc(struct proc *p, int force)
        crit_enter();
        clear_resched();
        cpuid = p->p_thread->td_gd->gd_cpuid;
-       p->p_flag |= P_CP_RELEASED;
+       if ((p->p_flag & P_CP_RELEASED) == 0) {
+               p->p_flag |= P_CP_RELEASED;
+               lwkt_setpri_self(TDPRI_KERN_USER);
+       }
        if (p->p_flag & P_CURPROC) {
                p->p_flag &= ~P_CURPROC;
+               curprocmask &= ~(1 << cpuid);
                if (try_mplock()) {
-                       KKASSERT(curprocmask & (1 << cpuid));
-                       if ((np = chooseproc()) != NULL) {
-                               if (force || 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 {
-                                       p->p_flag &= ~P_CP_RELEASED;
-                                       p->p_flag |= P_CURPROC;
-                                       setrunqueue(np);
-                               }
-                       } else {
-                               curprocmask &= ~(1 << cpuid);
+                       /*
+                        * Choose the next process to assign P_CURPROC to.
+                        * Note that we cannot schedule gd_schedthread
+                        * if runqcount is 0 without creating a scheduling
+                        * loop.
+                        */
+                       if ((np = chooseproc(NULL)) != NULL) {
+                               curprocmask |= 1 << cpuid;
+                               np->p_flag |= P_CURPROC;
+                               mycpu->gd_upri = np->p_priority;
+                               USCHED_COUNTER(np->p_thread);
+                               lwkt_acquire(np->p_thread);
+                               lwkt_schedule(np->p_thread);
+                       } else if (runqcount && (rdyprocmask & (1 << cpuid))) {
+                               rdyprocmask &= ~(1 << cpuid);
+                               lwkt_schedule(&mycpu->gd_schedthread);
                        }
                        rel_mplock();
                } else {
-                       KKASSERT(0);
-                       curprocmask &= ~(1 << cpuid);
-                       if (rdyprocmask & (1 << cpuid))
-                               lwkt_schedule(&globaldata_find(cpuid)->gd_schedthread);
+                       KKASSERT(0);    /* MP LOCK ALWAYS HELD AT THE MOMENT */
+                       if (runqcount && (rdyprocmask & (1 << cpuid))) {
+                               rdyprocmask &= ~(1 << cpuid);
+                               lwkt_schedule(&mycpu->gd_schedthread);
+                       }
                }
        }
        crit_exit();
@@ -391,10 +522,15 @@ acquire_curproc(struct proc *p)
 
        /*
         * Short cut, we've already acquired the designation or we never
-        * lost it in the first place.
+        * lost it in the first place.  P_CP_RELEASED is cleared, meaning
+        * that the process is again under the control of the userland 
+        * scheduler.  We do not have to fiddle with the LWKT priority,
+        * the trap code (userret/userexit) will do that for us.
         */
-       if ((p->p_flag & P_CURPROC) != 0)
+       if ((p->p_flag & P_CURPROC) != 0) {
+               p->p_flag &= ~P_CP_RELEASED;
                return;
+       }
 
        /*
         * Long cut.  This pulls in a bit of the userland scheduler as 
@@ -414,32 +550,42 @@ acquire_curproc(struct proc *p)
        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_gd->gd_cpuid;   /* load/reload cpuid */
+               /*
+                * reload the cpuid
+                */
+               cpuid = p->p_thread->td_gd->gd_cpuid;
+
+               /*
+                * (broken out from setrunqueue() as an optimization that
+                * allows us to avoid descheduling and rescheduling ourself)
+                *
+                * Interlock against the helper scheduler thread by setting
+                * curprocmask while we choose a new process.  Check our
+                * process against the new process to shortcut setrunqueue()
+                * and remrunqueue() operations.
+                */
                if ((curprocmask & (1 << cpuid)) == 0) {
                        curprocmask |= 1 << cpuid;
-                       if ((np = chooseproc()) != NULL) {
+
+                       if ((np = chooseproc(p)) != 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 {
-                                       p->p_flag |= P_CURPROC;
-                                       setrunqueue(np);
-                               }
+                               np->p_flag |= P_CURPROC;
+                               mycpu->gd_upri = np->p_priority;
+                               USCHED_COUNTER(np->p_thread);
+                               lwkt_acquire(np->p_thread);
+                               lwkt_schedule(np->p_thread);
                        } else {
                                p->p_flag |= P_CURPROC;
                        }
+                       break;
                }
-               if ((p->p_flag & P_CURPROC) == 0) {
-                       lwkt_deschedule_self();
-                       setrunqueue(p);
-                       lwkt_switch();
-                       KKASSERT((p->p_flag & (P_ONRUNQ|P_CURPROC|P_CP_RELEASED)) == P_CURPROC);
-               }
+               lwkt_deschedule_self();
+               p->p_flag &= ~P_CP_RELEASED;
+               setrunqueue(p);
+               lwkt_switch();  /* CPU CAN CHANGE DUE TO SETRUNQUEUE() */
+               KASSERT((p->p_flag & (P_ONRUNQ|P_CURPROC|P_CP_RELEASED)) == P_CURPROC, ("unexpected p_flag %08x acquiring P_CURPROC\n", p->p_flag));
        }
        crit_exit();
 }
@@ -467,7 +613,7 @@ uio_yield(void)
        if (p) {
                p->p_flag |= P_PASSIVE_ACQ;
                acquire_curproc(p);
-               release_curproc(p, 1);
+               release_curproc(p);
                p->p_flag &= ~P_PASSIVE_ACQ;
        }
 }
@@ -501,9 +647,10 @@ sched_thread(void *dummy)
        lwkt_deschedule_self();         /* interlock */
        rdyprocmask |= cpumask;
        crit_enter();
-       if ((curprocmask & cpumask) == 0 && (np = chooseproc()) != NULL) {
+       if ((curprocmask & cpumask) == 0 && (np = chooseproc(NULL)) != NULL) {
            curprocmask |= cpumask;
            np->p_flag |= P_CURPROC;
+           mycpu->gd_upri = np->p_priority;
            USCHED_COUNTER(np->p_thread);
            lwkt_acquire(np->p_thread);
            lwkt_schedule(np->p_thread);
index af83d44..be3d2be 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)kern_synch.c        8.9 (Berkeley) 5/19/95
  * $FreeBSD: src/sys/kern/kern_synch.c,v 1.87.2.6 2002/10/13 07:29:53 kbyanc Exp $
- * $DragonFly: src/sys/kern/kern_synch.c,v 1.24 2003/10/16 23:59:15 dillon Exp $
+ * $DragonFly: src/sys/kern/kern_synch.c,v 1.25 2003/10/17 07:30:42 dillon Exp $
  */
 
 #include "opt_ktrace.h"
@@ -85,7 +85,6 @@ static fixpt_t cexp[3] = {
 
 static void    endtsleep (void *);
 static void    loadav (void *arg);
-static void    maybe_resched (struct proc *chk);
 static void    roundrobin (void *arg);
 static void    schedcpu (void *arg);
 static void    updatepri (struct proc *p);
@@ -110,38 +109,6 @@ sysctl_kern_quantum(SYSCTL_HANDLER_ARGS)
 SYSCTL_PROC(_kern, OID_AUTO, quantum, CTLTYPE_INT|CTLFLAG_RW,
        0, sizeof sched_quantum, sysctl_kern_quantum, "I", "");
 
-/*
- * Arrange to reschedule if necessary by checking to see if the current
- * process is on the highest priority user scheduling queue.  This may
- * be run from an interrupt so we have to follow any preemption chains
- * back to the original process.
- */
-static void
-maybe_resched(struct proc *chk)
-{
-       struct proc *cur = lwkt_preempted_proc();
-
-       if (cur == NULL)
-               return;
-
-       /*
-        * Check the user queue (realtime, normal, idle).  Lower numbers
-        * indicate higher priority queues.  Lower numbers are also better
-        * for p_priority.
-        */
-       if (chk->p_rtprio.type < cur->p_rtprio.type) {
-               need_resched();
-       } else if (chk->p_rtprio.type == cur->p_rtprio.type) {
-               if (chk->p_rtprio.type == RTP_PRIO_NORMAL) {
-                       if (chk->p_priority / PPQ < cur->p_priority / PPQ)
-                               need_resched();
-               } else {
-                       if (chk->p_rtprio.prio < cur->p_rtprio.prio)
-                               need_resched();
-               }
-       }
-}
-
 int 
 roundrobin_interval(void)
 {
@@ -589,7 +556,6 @@ xwakeup(struct xwait *w)
                        p->p_stat = SRUN;
                        if (p->p_flag & P_INMEM) {
                                setrunqueue(p);
-                               maybe_resched(p);
                        } else {
                                p->p_flag |= P_SWAPINREQ;
                                wakeup((caddr_t)&proc0);
@@ -629,8 +595,6 @@ restart:
                                p->p_stat = SRUN;
                                if (p->p_flag & P_INMEM) {
                                        setrunqueue(p);
-                                       if (p->p_flag & P_CURPROC)
-                                           maybe_resched(p);
                                } else {
                                        p->p_flag |= P_SWAPINREQ;
                                        wakeup((caddr_t)&proc0);
@@ -762,8 +726,6 @@ setrunnable(struct proc *p)
        if ((p->p_flag & P_INMEM) == 0) {
                p->p_flag |= P_SWAPINREQ;
                wakeup((caddr_t)&proc0);
-       } else {
-               maybe_resched(p);
        }
 }
 
@@ -794,8 +756,6 @@ clrrunnable(struct proc *p, int stat)
  * Compute the priority of a process when running in user mode.
  * Arrange to reschedule if the resulting priority is better
  * than that of the current process.
- *
- * YYY real time / idle procs do not use p_priority XXX
  */
 void
 resetpriority(struct proc *p)
@@ -804,20 +764,39 @@ resetpriority(struct proc *p)
        int opq;
        int npq;
 
-       if (p->p_rtprio.type != RTP_PRIO_NORMAL)
+       /*
+        * Set p_priority for general process comparisons
+        */
+       switch(p->p_rtprio.type) {
+       case RTP_PRIO_REALTIME:
+               p->p_priority = PRIBASE_REALTIME + p->p_rtprio.prio;
+               return;
+       case RTP_PRIO_NORMAL:
+               break;
+       case RTP_PRIO_IDLE:
+               p->p_priority = PRIBASE_IDLE + p->p_rtprio.prio;
                return;
+       case RTP_PRIO_THREAD:
+               p->p_priority = PRIBASE_THREAD + p->p_rtprio.prio;
+               return;
+       }
+
+       /*
+        * NORMAL priorities fall through.  These are based on niceness
+        * and cpu use.
+        */
        newpriority = NICE_ADJUST(p->p_nice - PRIO_MIN) +
                        p->p_estcpu / ESTCPURAMP;
        newpriority = min(newpriority, MAXPRI);
        npq = newpriority / PPQ;
        crit_enter();
-       opq = p->p_priority / PPQ;
+       opq = (p->p_priority & PRIMASK) / PPQ;
        if (p->p_stat == SRUN && (p->p_flag & P_ONRUNQ) && opq != npq) {
                /*
                 * We have to move the process to another queue
                 */
                remrunqueue(p);
-               p->p_priority = newpriority;
+               p->p_priority = PRIBASE_NORMAL + newpriority;
                setrunqueue(p);
        } else {
                /*
@@ -825,10 +804,9 @@ resetpriority(struct proc *p)
                 * up later.
                 */
                KKASSERT(opq == npq || (p->p_flag & P_ONRUNQ) == 0);
-               p->p_priority = newpriority;
+               p->p_priority = PRIBASE_NORMAL + newpriority;
        }
        crit_exit();
-       maybe_resched(p);
 }
 
 /*
@@ -891,14 +869,24 @@ sched_setup(dummy)
  * is that the system will 90% forget that the process used a lot of CPU
  * time in 5 * loadav seconds.  This causes the system to favor processes
  * which haven't run much recently, and to round-robin among other processes.
+ *
+ * WARNING!
  */
 void
-schedclock(struct proc *p)
+schedclock(void *dummy)
 {
-       p->p_cpticks++;
-       p->p_estcpu = ESTCPULIM(p->p_estcpu + 1);
-       if ((p->p_estcpu % PPQ) == 0)
-               resetpriority(p);
+       struct thread *td;
+       struct proc *p;
+
+       td = curthread;
+       if ((p = td->td_proc) != NULL) {
+               p->p_cpticks++;
+               p->p_estcpu = ESTCPULIM(p->p_estcpu + 1);
+               if ((p->p_estcpu % PPQ) == 0 && try_mplock()) {
+                       resetpriority(p);
+                       rel_mplock();
+               }
+       }
 }
 
 static
index ceb6cac..6af6ba0 100644 (file)
@@ -28,7 +28,7 @@
  *     to use a critical section to avoid problems.  Foreign thread 
  *     scheduling is queued via (async) IPIs.
  *
- * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.36 2003/10/15 23:27:06 dillon Exp $
+ * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.37 2003/10/17 07:30:42 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -1111,8 +1111,10 @@ lwkt_gentoken(lwkt_token_t tok, int *gen)
 }
 
 /*
- * Re-acquire a token that might have been lost.  Returns the generation 
- * number of the token.
+ * Re-acquire a token that might have been lost.   The generation number
+ * is bumped and returned regardless of whether the token had been lost
+ * or not (because we only have cpu granularity we have to bump the token
+ * either way).
  */
 int
 lwkt_regettoken(lwkt_token_t tok)
@@ -1141,8 +1143,8 @@ lwkt_regettoken(lwkt_token_t tok)
 #endif
        }
 #endif
-       ++tok->t_gen;
     }
+    ++tok->t_gen;
     return(tok->t_gen);
 }
 
@@ -1340,12 +1342,14 @@ lwkt_send_ipiq(int dcpu, ipifunc_t func, void *arg)
 
 /*
  * Send a message to several target cpus.  Typically used for scheduling.
+ * The message will not be sent to stopped cpus.
  */
 void
 lwkt_send_ipiq_mask(u_int32_t mask, ipifunc_t func, void *arg)
 {
     int cpuid;
 
+    mask &= ~stopped_cpus;
     while (mask) {
            cpuid = bsfl(mask);
            lwkt_send_ipiq(cpuid, func, arg);
index 5103899..b7defdd 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)trap.c        7.4 (Berkeley) 5/13/91
  * $FreeBSD: src/sys/i386/i386/trap.c,v 1.147.2.11 2003/02/27 19:09:59 luoqi Exp $
- * $DragonFly: src/sys/platform/pc32/i386/trap.c,v 1.35 2003/10/16 22:26:35 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/trap.c,v 1.36 2003/10/17 07:30:43 dillon Exp $
  */
 
 /*
@@ -175,19 +175,25 @@ MALLOC_DEFINE(M_SYSMSG, "sysmsg", "sysmsg structure");
  * point of view of the userland scheduler unless we actually have to
  * switch.
  *
- * usertdsw is called from within a critical section and the BGL will still
- * be held.  This function is NOT called for preemptions, only for switchouts.
+ * passive_release is called from within a critical section and the BGL will
+ * still be held.  This function is NOT called for preemptions, only for
+ * switchouts.  We only actually release our P_CURPROC designation when we
+ * are going to sleep, otherwise another process might be assigned P_CURPROC
+ * unnecessarily.
  */
 static void
 passive_release(struct thread *td)
 {
        struct proc *p = td->td_proc;
 
-       lwkt_setpri_self(TDPRI_KERN_USER);
-       if (p->p_flag & P_CURPROC)
-               release_curproc(p, (td->td_flags & TDF_RUNQ) == 0);
-       if ((p->p_flag & P_CURPROC) == 0)
+       if ((p->p_flag & P_CP_RELEASED) == 0) {
+               p->p_flag |= P_CP_RELEASED;
+               lwkt_setpri_self(TDPRI_KERN_USER);
+       }
+       if ((p->p_flag & P_CURPROC) && (td->td_flags & TDF_RUNQ) == 0) {
                td->td_release = NULL;
+               release_curproc(p);
+       }
 }
 
 /*
@@ -210,19 +216,20 @@ userexit(struct proc *p)
        /*
         * Reacquire our P_CURPROC status and adjust the LWKT priority
         * for our return to userland.  We can fast path the case where
-        * td_release was not called and P_CURPROC is still set, otherwise
-        * do it the slow way.
+        * td_release was not called by checking particular proc flags.
+        * Otherwise we do it the slow way.
         *
         * Lowering our priority may make other higher priority threads
         * runnable. lwkt_setpri_self() does not switch away, so call
         * lwkt_maybe_switch() to deal with it.
         */
-       if (td->td_release && (p->p_flag & P_CURPROC)) {
+       td->td_release = NULL;
+       if ((p->p_flag & (P_CP_RELEASED|P_CURPROC)) == P_CURPROC) {
                ++fast_release;
-               td->td_release = NULL;
        } else {
                ++slow_release;
                acquire_curproc(p);
+
                switch(p->p_rtprio.type) {
                case RTP_PRIO_IDLE:
                        lwkt_setpri_self(TDPRI_USER_IDLE);
@@ -235,8 +242,8 @@ userexit(struct proc *p)
                        lwkt_setpri_self(TDPRI_USER_NORM);
                        break;
                }
-               lwkt_maybe_switch();
        }
+       lwkt_maybe_switch();
 }
 
 
@@ -253,18 +260,22 @@ userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
        }
 
        /*
-        * If a reschedule has been requested then the easiest solution
-        * is to run our passive release function which will possibly
-        * shift our P_CURPROC designation to another user process. 
-        *
-        * A reschedule can also occur due to a higher priority LWKT thread
-        * becoming runable, we have to call lwkt_maybe_switch() to deal
-        * with it.
+        * If a reschedule has been requested then we release the current
+        * process in order to shift our P_CURPROC designation to another
+        * user process.  userexit() will reacquire P_CURPROC and block
+        * there.
         */
        if (resched_wanted()) {
-               if (curthread->td_release)
-                       passive_release(curthread);
-               lwkt_maybe_switch();
+               p->p_thread->td_release = NULL;
+               if ((p->p_flag & P_CP_RELEASED) == 0) {
+                       p->p_flag |= P_CP_RELEASED;
+                       lwkt_setpri_self(TDPRI_KERN_USER);
+               }
+               if (p->p_flag & P_CURPROC) {
+                       release_curproc(p);
+               } else {
+                       clear_resched();
+               }
        }
 
        /*
index 706181c..21ce786 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/include/globaldata.h,v 1.11.2.1 2000/05/16 06:58:10 dillon Exp $
- * $DragonFly: src/sys/sys/globaldata.h,v 1.17 2003/10/02 22:27:00 dillon Exp $
+ * $DragonFly: src/sys/sys/globaldata.h,v 1.18 2003/10/17 07:30:40 dillon Exp $
  */
 
 #ifndef _SYS_GLOBALDATA_H_
@@ -94,7 +94,9 @@ struct globaldata {
        int             gd_psdiv;               /* profile kern/kern_clock.c */
        struct vmmeter  gd_cnt;
        struct lwkt_ipiq *gd_ipiq;
-       struct thread   gd_schedthread;
+       short           gd_upri;                /* userland scheduler helper */
+       short           gd_unused01;
+       struct thread   gd_schedthread;         /* userland scheduler helper */
        struct thread   gd_idlethread;
        SLGlobalData    gd_slab;                /* slab allocator */
        int             gd_vme_kdeficit;        /* vm_map_entry reservation */
index 905c6ca..bcd1b20 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)param.h     8.3 (Berkeley) 4/4/95
  * $FreeBSD: src/sys/sys/param.h,v 1.61.2.38 2003/05/22 17:12:01 fjoe Exp $
- * $DragonFly: src/sys/sys/param.h,v 1.7 2003/10/16 23:59:13 dillon Exp $
+ * $DragonFly: src/sys/sys/param.h,v 1.8 2003/10/17 07:30:40 dillon Exp $
  */
 
 #ifndef _SYS_PARAM_H_
  * insignificant.
  */
 #define        MAXPRI  127             /* Priorities range from 0 through MAXPRI. */
+#define PRIMASK        127
+#define PRIBASE_REALTIME       0
+#define PRIBASE_NORMAL         128
+#define PRIBASE_IDLE           256
+#define PRIBASE_THREAD         384     /* huh? */
 
 #define        PCATCH          0x0100  /* OR'd with pri for tsleep to check signals */
 #define PUSRFLAG1      0x0200  /* Subsystem specific flag */
index b659bae..0f36e70 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)proc.h      8.15 (Berkeley) 5/19/95
  * $FreeBSD: src/sys/sys/proc.h,v 1.99.2.9 2003/06/06 20:21:32 tegge Exp $
- * $DragonFly: src/sys/sys/proc.h,v 1.33 2003/10/16 23:59:13 dillon Exp $
+ * $DragonFly: src/sys/sys/proc.h,v 1.34 2003/10/17 07:30:40 dillon Exp $
  */
 
 #ifndef _SYS_PROC_H_
@@ -179,8 +179,7 @@ struct      proc {
        struct  vnode *p_textvp;        /* Vnode of executable. */
 
        char    p_lock;                 /* Process lock (prevent swap) count. */
-       u_char  p_unused01;             /* Which cpu we are on */
-       u_char  p_unused02;             /* Last cpu we were on */
+       short   p_priority;             /* overall process priority */
        char    p_rqindex;              /* Run queue index */
 
        unsigned int    p_stops;        /* procfs event bitmask */
@@ -203,7 +202,7 @@ struct      proc {
 
        sigset_t p_sigmask;     /* Current signal mask. */
        stack_t p_sigstk;       /* sp & on stack state variable */
-       u_char  p_priority;     /* Tracks user sched queue */
+       u_char  p_unused00;     /* (used to be p_priority) */
        char    p_nice;         /* Process "nice" value. */
 
        struct  pgrp *p_pgrp;   /* Pointer to process group. */
@@ -419,7 +418,7 @@ int p_trespass (struct ucred *cr1, struct ucred *cr2);
 void   resetpriority (struct proc *);
 int    roundrobin_interval (void);
 void   resched_cpus(u_int32_t mask);
-void   schedclock (struct proc *);
+void   schedclock (void *dummy);
 void   setrunnable (struct proc *);
 void   clrrunnable (struct proc *, int stat);
 void   setrunqueue (struct proc *);
@@ -428,7 +427,7 @@ int suser (struct thread *td);
 int    suser_proc (struct proc *p);
 int    suser_cred (struct ucred *cred, int flag);
 void   remrunqueue (struct proc *);
-void   release_curproc (struct proc *curp, int force);
+void   release_curproc (struct proc *curp);
 void   acquire_curproc (struct proc *curp);
 void   cpu_heavy_switch (struct thread *);
 void   cpu_lwkt_switch (struct thread *);