* 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.4 2003/06/30 19:50:31 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>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/queue.h>
#include <sys/proc.h>
#include <sys/rtprio.h>
-#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/sysctl.h>
+#include <machine/ipl.h>
+#include <machine/cpu.h>
+
+/*
+ * debugging only YYY Remove me!   define to schedule user processes only
+ * on the BSP.  Interrupts can still be taken on the APs.
+ */
+#undef ONLY_ONE_USER_CPU

/*
* We have NQS (32) run queues per scheduling class.  For the normal
* the state of all 32 queues and then a ffs() to find the first busy
* queue.
*/
-struct rq queues[NQS];
-struct rq rtqueues[NQS];
-struct rq idqueues[NQS];
-u_int32_t queuebits;
-u_int32_t rtqueuebits;
-u_int32_t idqueuebits;
+static struct rq queues[NQS];
+static struct rq rtqueues[NQS];
+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;     /* currently running 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.
@@ -66,15 +91,98 @@ rqinit(void *dummy)
TAILQ_INIT(&rtqueues[i]);
TAILQ_INIT(&idqueues[i]);
}
+#ifdef SMP
+#else
+#endif
}
SYSINIT(runqueue, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, rqinit, NULL)

+static int
+test_resched(struct proc *curp, struct proc *newp)
+{
+       if (newp->p_rtprio.type < curp->p_rtprio.type)
+               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);
+}
+
/*
- * setrunqueue() examines a process priority and class and inserts it on
- * the tail of it's appropriate run queue (based on class and priority).
- * This sets the queue busy bit.  If no user processes have been scheduled
- * on the LWKT subsystem we schedule this one.
+ * chooseproc() is called when a cpu needs a user process to LWKT schedule.
+ * chooseproc() will select a user process and return it.
+ */
+static
+struct proc *
+chooseproc(void)
+{
+       struct proc *p;
+       struct rq *q;
+       u_int32_t *which;
+       u_int32_t pri;
+
+       if (rtqueuebits) {
+               pri = bsfl(rtqueuebits);
+               q = &rtqueues[pri];
+               which = &rtqueuebits;
+       } else if (queuebits) {
+               pri = bsfl(queuebits);
+               q = &queues[pri];
+               which = &queuebits;
+       } else if (idqueuebits) {
+               pri = bsfl(idqueuebits);
+               q = &idqueues[pri];
+               which = &idqueuebits;
+       } else {
+               return NULL;
+       }
+       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!"));
+       p->p_flag &= ~P_ONRUNQ;
+       return p;
+}
+
+
+/*
+ * setrunqueue() 'wakes up' a 'user' process, which can mean several things.
+ *
+ * If P_CP_RELEASED is set the user process is under the control of the
+ * LWKT subsystem and we simply wake the thread up.  This is ALWAYS the
+ * case when setrunqueue() is called from wakeup() and, in fact wakeup()
+ * asserts that P_CP_RELEASED is set.
+ *
+ * Note that acquire_curproc() already optimizes making the current process
+ * P_CURPROC, so setrunqueue() does not need to.
+ *
+ * If P_CP_RELEASED is not set we place the process on the run queue and we
+ * 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().
*/
@@ -82,59 +190,90 @@ void
setrunqueue(struct proc *p)
{
struct rq *q;
-       u_int8_t pri;
-       struct globaldata *gd;
+       int pri;
+       int cpuid;

+       crit_enter();
KASSERT(p->p_stat == SRUN, ("setrunqueue: proc not SRUN"));
+       KASSERT((p->p_flag & (P_ONRUNQ|P_CURPROC)) == 0,
+           ("process %d already on runq! flag %08x", p->p_pid, p->p_flag));
+       KKASSERT((p->p_thread->td_flags & TDF_RUNQ) == 0);

/*
-        * If we are already the designated current process just
+        * If we have been released from the userland scheduler we
+        * directly schedule its thread.
*/
-       if (p->p_flag & P_CURPROC) {
-               KASSERT((p->p_flag & P_ONRUNQ) == 0, ("already on runq!"));
+       if (p->p_flag & P_CP_RELEASED) {
+               crit_exit();
return;
}

/*
-        * If the process's cpu is not running any userland processes
-        * then schedule this one's thread.
+        * If our cpu is available to run a user process we short cut and
+        * just do it.
*/
-       if (gd->gd_uprocscheduled == 0) {
-               gd->gd_uprocscheduled = 1;
+       cpuid = mycpu->gd_cpuid;
+       if ((curprocmask & (1 << cpuid)) == 0) {
+               curprocmask |= 1 << cpuid;
p->p_flag |= P_CURPROC;
-               KASSERT((p->p_flag & P_ONRUNQ) == 0, ("already on runq2!"));
+               crit_exit();
return;
}

-       KASSERT((p->p_flag & P_ONRUNQ) == 0, ("already on runq3!"));
-       p->p_flag |= P_ONRUNQ;
/*
* Otherwise place this process on the userland scheduler's run
* queue for action.
*/
-
+       ++runqcount;
+       p->p_flag |= P_ONRUNQ;
if (p->p_rtprio.type == RTP_PRIO_NORMAL) {
pri = p->p_priority >> 2;
q = &queues[pri];
queuebits |= 1 << pri;
} else if (p->p_rtprio.type == RTP_PRIO_REALTIME ||
p->p_rtprio.type == RTP_PRIO_FIFO) {
-               pri = p->p_rtprio.prio;
+               pri = (u_int8_t)p->p_rtprio.prio;
q = &rtqueues[pri];
rtqueuebits |= 1 << pri;
} else if (p->p_rtprio.type == RTP_PRIO_IDLE) {
-               pri = p->p_rtprio.prio;
+               pri = (u_int8_t)p->p_rtprio.prio;
q = &idqueues[pri];
idqueuebits |= 1 << pri;
} else {
panic("setrunqueue: invalid rtprio type");
}
+       KKASSERT(pri < 32);
p->p_rqindex = pri;             /* remember the queue index */
TAILQ_INSERT_TAIL(q, p, p_procq);
+
+       /*
+        * 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
+        */
+           (p->p_flag & P_PASSIVE_ACQ) == 0) {
+               int count = runqcount;
+                       printf("PROC %d nocpu to schedule it on\n", p->p_pid);
+               while (mask && count) {
+                       KKASSERT((curprocmask & (1 << cpuid)) == 0);
+                       rdyprocmask &= ~(1 << cpuid);
+                       --count;
+                       mask &= ~(1 << cpuid);
+               }
+       }
+       crit_exit();
}

/*
@@ -155,8 +294,11 @@ remrunqueue(struct proc *p)
u_int32_t *which;
u_int8_t pri;

+       crit_enter();
KASSERT((p->p_flag & P_ONRUNQ) != 0, ("not on runq4!"));
p->p_flag &= ~P_ONRUNQ;
+       --runqcount;
+       KKASSERT(runqcount >= 0);
pri = p->p_rqindex;
if (p->p_rtprio.type == RTP_PRIO_NORMAL) {
q = &queues[pri];
@@ -177,42 +319,208 @@ remrunqueue(struct proc *p)
("remrunqueue: remove from empty queue"));
*which &= ~(1 << pri);
}
+       crit_exit();
}

/*
- * chooseproc() is called when a cpu needs a user process to LWKT schedule.
- * chooseproc() will select a user process and return it.
+ * 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.
+ *
+ * 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
+ * from a passive release during a lwkt_switch().   try_mplock() will deal
+ * with this for us but you should be aware that td_mpcount may not be
+ * useable.
*/
-struct proc *
-chooseproc(void)
+void
+release_curproc(struct proc *p)
{
-       struct proc *p;
-       struct rq *q;
-       u_int32_t *which;
-       u_int32_t pri;
+       int cpuid;
+       struct proc *np;

-       if (rtqueuebits) {
-               pri = ffs(rtqueuebits) - 1;
-               q = &rtqueues[pri];
-               which = &rtqueuebits;
-       } else if (queuebits) {
-               pri = ffs(queuebits) - 1;
-               q = &queues[pri];
-               which = &queuebits;
-       } else if (idqueuebits) {
-               pri = ffs(idqueuebits) - 1;
-               q = &idqueues[pri];
-               which = &idqueuebits;
-       } else {
-               return NULL;
+#ifdef ONLY_ONE_USER_CPU
+       KKASSERT(mycpu->gd_cpuid == 0 && p->p_thread->td_gd == mycpu);
+#endif
+       crit_enter();
+       clear_resched();
+       p->p_flag |= P_CP_RELEASED;
+       if (p->p_flag & P_CURPROC) {
+               p->p_flag &= ~P_CURPROC;
+               if (try_mplock()) {
+                       KKASSERT(curprocmask & (1 << cpuid));
+                       if ((np = chooseproc()) != NULL) {
+                               np->p_flag |= P_CURPROC;
+                       } else {
+                               curprocmask &= ~(1 << cpuid);
+                       }
+                       rel_mplock();
+               } else {
+                       KKASSERT(0);
+                       curprocmask &= ~(1 << cpuid);
+                       if (rdyprocmask & (1 << cpuid))
+               }
}
-       p = TAILQ_FIRST(q);
-       KASSERT(p, ("chooseproc: no proc on busy queue"));
-       TAILQ_REMOVE(q, p, p_procq);
-       if (TAILQ_EMPTY(q))
-               *which &= ~(1 << pri);
-       KASSERT((p->p_flag & P_ONRUNQ) != 0, ("not on runq6!"));
-       p->p_flag &= ~P_ONRUNQ;
-       return p;
+       crit_exit();
+}
+
+/*
+ * Acquire the P_CURPROC designation on the CURRENT process only.  This
+ * function is called prior to returning to userland.  If the system
+ * call or trap did not block and if no reschedule was requested it is
+ * highly likely that the P_CURPROC flag is still set in the proc, and
+ * we do almost nothing here.
+ */
+void
+acquire_curproc(struct proc *p)
+{
+       int cpuid;
+       struct proc *np;
+
+       /*
+        * Short cut, we've already acquired the designation or we never
+        * lost it in the first place.
+        */
+       if ((p->p_flag & P_CURPROC) != 0)
+               return;
+
+       /*
+        * Long cut.  This pulls in a bit of the userland scheduler as
+        * an optimization.  If our cpu has not scheduled a userland
+        * process we gladly fill the slot, otherwise we choose the best
+        * candidate from the run queue and compare it against ourselves,
+        * scheduling either us or him depending.
+        *
+        * If our cpu's slot isn't free we put ourselves on the userland
+        * run queue and switch away.  We should have P_CURPROC when we
+        * come back.  Note that a cpu change can occur when we come back.
+        *
+        * YYY don't need critical section, we hold giant and no interrupt
+        * 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_gd == mycpu);
+#endif
+       crit_enter();
+       p->p_flag &= ~P_CP_RELEASED;
+       while ((p->p_flag & P_CURPROC) == 0) {
+               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;
+                               } else {
+                                       p->p_flag |= P_CURPROC;
+                                       setrunqueue(np);
+                               }
+                       } else {
+                               p->p_flag |= P_CURPROC;
+                       }
+               }
+               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);
+               }
+       }
+       crit_exit();
}

+/*
+ * Yield / synchronous reschedule.  This is a bit tricky because the trap
+ * code might have set a lazy release on the switch function.  The first
+ * thing we do is call lwkt_switch() to resolve the lazy release (if any).
+ * Then, if we are a process, we want to allow another process to run.
+ *
+ * The only way to do that is to acquire and then release P_CURPROC.  We
+ * have to release it because the kernel expects it to be released as a
+ * sanity check when it goes to sleep.
+ *
+ * XXX we need a way to ensure that we wake up eventually from a yield,
+ * even if we are an idprio process.
+ */
+void
+uio_yield(void)
+{
+       struct proc *p = td->td_proc;
+
+       lwkt_switch();
+       if (p) {
+               p->p_flag |= P_PASSIVE_ACQ;
+               acquire_curproc(p);
+               release_curproc(p);
+               p->p_flag &= ~P_PASSIVE_ACQ;
+       }
+}
+
+
+/*
+ * For SMP systems a user scheduler helper thread is created for each
+ * cpu and is used to allow one cpu to wakeup another for the purposes of
+ * scheduling userland threads from setrunqueue().  UP systems do not
+ * need the helper since there is only one cpu.  We can't use the idle
+ * thread for this because we need to hold the MP lock.  Additionally,
+ * doing things this way allows us to HLT idle cpus on MP systems.
+ */
+
+#ifdef SMP
+
+static void
+{
+    int cpuid = mycpu->gd_cpuid;       /* doesn't change */
+    u_int32_t cpumask = 1 << cpuid;    /* doesn't change */
+
+#ifdef ONLY_ONE_USER_CPU
+    KKASSERT(cpuid == 0);
+#endif
+
+    get_mplock();                      /* hold the MP lock */
+    for (;;) {
+       struct proc *np;
+
+       lwkt_deschedule_self();         /* interlock */
+       crit_enter();
+       if ((curprocmask & cpumask) == 0 && (np = chooseproc()) != NULL) {
+           np->p_flag |= P_CURPROC;
+       }
+       crit_exit();
+       lwkt_switch();
+    }
+}
+
+void
+{
+    int cpuid = mycpu->gd_cpuid;
+
{"ft_lang_label":"__label__en","ft_lang_prob":0.6027429,"math_prob":0.7927398,"size":14784,"snap":"2019-51-2020-05","text_gpt3_token_len":4713,"char_repetition_ratio":0.14384302,"word_repetition_ratio":0.115676105,"special_character_ratio":0.3589015,"punctuation_ratio":0.1687448,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.96868825,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2020-01-25T23:03:11Z\",\"WARC-Record-ID\":\"<urn:uuid:b8bc6244-3870-4031-bf6a-b7b900ed4147>\",\"Content-Length\":\"70885\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:b9bc663c-410f-41e8-88f2-35a9b1e78081>\",\"WARC-Concurrent-To\":\"<urn:uuid:990a9b98-a650-4203-a0f3-76db8ddbafb7>\",\"WARC-IP-Address\":\"199.233.90.68\",\"WARC-Target-URI\":\"http://gitweb.dragonflybsd.org/dragonfly.git/blobdiff/26a0694ba4ad6b2eea86882e4436d151c7eab4cd..a72187e94edafbd6b8a53afd63f7001850806f5e:/sys/kern/kern_switch.c\",\"WARC-Payload-Digest\":\"sha1:WCCQCRFBGK2WA7VZ3EPUC423H4C2OSFS\",\"WARC-Block-Digest\":\"sha1:WD2RDVEKDMYU4ZUKI25H34MN5WAQP4R7\",\"WARC-Identified-Payload-Type\":\"application/xhtml+xml\",\"warc_filename\":\"/cc_download/warc_2020/CC-MAIN-2020-05/CC-MAIN-2020-05_segments_1579251681625.83_warc_CC-MAIN-20200125222506-20200126012506-00526.warc.gz\"}"}