PERFORCE change 60246 for review
Julian Elischer
julian at FreeBSD.org
Sun Aug 22 01:14:08 PDT 2004
http://perforce.freebsd.org/chv.cgi?CH=60246
Change 60246 by julian at julian_ref on 2004/08/22 08:13:44
put "e.diff" into nsched branch of p4
Affected files ...
.. //depot/projects/nsched/sys/alpha/alpha/machdep.c#4 edit
.. //depot/projects/nsched/sys/amd64/amd64/machdep.c#7 edit
.. //depot/projects/nsched/sys/arm/sa11x0/assabet_machdep.c#3 edit
.. //depot/projects/nsched/sys/ddb/db_ps.c#5 edit
.. //depot/projects/nsched/sys/i386/i386/machdep.c#13 edit
.. //depot/projects/nsched/sys/ia64/ia64/machdep.c#5 edit
.. //depot/projects/nsched/sys/kern/init_main.c#11 edit
.. //depot/projects/nsched/sys/kern/kern_exec.c#10 edit
.. //depot/projects/nsched/sys/kern/kern_exit.c#17 edit
.. //depot/projects/nsched/sys/kern/kern_fork.c#9 edit
.. //depot/projects/nsched/sys/kern/kern_kse.c#23 edit
.. //depot/projects/nsched/sys/kern/kern_proc.c#11 edit
.. //depot/projects/nsched/sys/kern/kern_switch.c#5 edit
.. //depot/projects/nsched/sys/kern/kern_synch.c#9 edit
.. //depot/projects/nsched/sys/kern/kern_thr.c#12 edit
.. //depot/projects/nsched/sys/kern/kern_thread.c#32 edit
.. //depot/projects/nsched/sys/kern/sched_4bsd.c#31 edit
.. //depot/projects/nsched/sys/kern/sched_ule.c#19 edit
.. //depot/projects/nsched/sys/pc98/i386/machdep.c#8 edit
.. //depot/projects/nsched/sys/powerpc/powerpc/machdep.c#4 edit
.. //depot/projects/nsched/sys/sparc64/sparc64/machdep.c#5 edit
.. //depot/projects/nsched/sys/sys/proc.h#22 edit
.. //depot/projects/nsched/sys/sys/sched.h#11 edit
Differences ...
==== //depot/projects/nsched/sys/alpha/alpha/machdep.c#4 (text+ko) ====
@@ -846,7 +846,7 @@
}
- proc_linkup(&proc0, &ksegrp0, &kse0, &thread0);
+ proc_linkup(&proc0, &ksegrp0, &thread0);
/*
* Init mapping for u page(s) for proc 0
*/
==== //depot/projects/nsched/sys/amd64/amd64/machdep.c#7 (text+ko) ====
@@ -1112,7 +1112,7 @@
* This may be done better later if it gets more high level
* components in it. If so just link td->td_proc here.
*/
- proc_linkup(&proc0, &ksegrp0, &kse0, &thread0);
+ proc_linkup(&proc0, &ksegrp0, &thread0);
preload_metadata = (caddr_t)(uintptr_t)(modulep + KERNBASE);
preload_bootstrap_relocate(KERNBASE);
==== //depot/projects/nsched/sys/arm/sa11x0/assabet_machdep.c#3 (text+ko) ====
@@ -370,7 +370,7 @@
/* Set stack for exception handlers */
- proc_linkup(&proc0, &ksegrp0, &kse0, &thread0);
+ proc_linkup(&proc0, &ksegrp0, &thread0);
proc0.p_uarea = (struct user *) proc0_uarea.pv_va;
thread0.td_kstack = kernelstack.pv_va;
thread0.td_pcb = (struct pcb *)
==== //depot/projects/nsched/sys/ddb/db_ps.c#5 (text+ko) ====
==== //depot/projects/nsched/sys/i386/i386/machdep.c#13 (text+ko) ====
@@ -1943,17 +1943,15 @@
int gsel_tss, metadata_missing, off, x;
struct pcpu *pc;
- /*
- * Set up things that proc0 would have associated with it already
- * if it were taken from the process allocation cache.
- * This includes a ksegrp, a thread, and a stack and uarea to go
- * with the thread. The pcb is deliniated ready for use.
- * Note that the stack for proc0 has no guard page.
- */
proc0.p_uarea = proc0uarea;
thread0.td_kstack = proc0kstack;
thread0.td_pcb = (struct pcb *)
(thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1;
+
+ /*
+ * This may be done better later if it gets more high level
+ * components in it. If so just link td->td_proc here.
+ */
proc_linkup(&proc0, &ksegrp0, &thread0);
metadata_missing = 0;
==== //depot/projects/nsched/sys/ia64/ia64/machdep.c#5 (text+ko) ====
@@ -724,7 +724,7 @@
msgbufp = (struct msgbuf *)pmap_steal_memory(MSGBUF_SIZE);
msgbufinit(msgbufp, MSGBUF_SIZE);
- proc_linkup(&proc0, &ksegrp0, &kse0, &thread0);
+ proc_linkup(&proc0, &ksegrp0, &thread0);
/*
* Init mapping for u page(s) for proc 0
*/
==== //depot/projects/nsched/sys/kern/init_main.c#11 (text+ko) ====
@@ -338,11 +338,11 @@
kseinit(); /* set up kse specific stuff e.g. upcall zone*/
/*
- * initialise scheduler resources.
+ * Initialise scheduler resources.
* Add scheduler specific parts to proc, ksegrp, thread as needed.
*/
schedinit(); /* scheduler gets its house in order */
-
+ schedinit2(); /* temporary */
/*
* Initialize sleep queue hash table
*/
@@ -370,8 +370,6 @@
session0.s_leader = p;
p->p_sysent = &null_sysvec;
-
-
p->p_flag = P_SYSTEM;
p->p_sflag = PS_INMEM;
p->p_state = PRS_NORMAL;
==== //depot/projects/nsched/sys/kern/kern_exec.c#10 (text+ko) ====
@@ -53,7 +53,6 @@
#include <sys/namei.h>
#include <sys/sf_buf.h>
#include <sys/sysent.h>
-#include <sys/sched.h>
#include <sys/shm.h>
#include <sys/sysctl.h>
#include <sys/user.h>
@@ -255,7 +254,7 @@
PROC_LOCK(p);
KASSERT((p->p_flag & P_INEXEC) == 0,
("%s(): process already has P_INEXEC flag", __func__));
- if (p->p_flag & P_SA || p->p_numthreads > 1) {
+ if (p->p_flag & P_HADTHREADS) {
if (thread_single(SINGLE_EXIT)) {
PROC_UNLOCK(p);
mtx_unlock(&Giant);
@@ -263,18 +262,8 @@
}
/*
* If we get here all other threads are dead,
- * so unset the associated flags and lose KSE mode.
- * This meams that we must get rid of any extra
- * upcalls and kses we may have picked up along the way.
+ * and threading mode has been turned off
*/
- mtx_lock_spin(&sched_lock);
- sched_set_concurrency(td->td_ksegrp, 1);
- upcall_remove(td);
- mtx_unlock_spin(&sched_lock);
- p->p_flag &= ~(P_SA|P_HADTHREADS);
- td->td_mailbox = NULL;
- td->td_pflags &= ~TDP_SA;
- thread_single_end();
}
p->p_flag |= P_INEXEC;
PROC_UNLOCK(p);
==== //depot/projects/nsched/sys/kern/kern_exit.c#17 (text+ko) ====
@@ -134,7 +134,7 @@
* MUST abort all other threads before proceeding past here.
*/
PROC_LOCK(p);
- if (p->p_flag & P_SA || p->p_numthreads > 1) {
+ if (p->p_flag & P_HADTHREADS) {
retry:
/*
* First check if some other thread got here before us..
@@ -164,18 +164,8 @@
goto retry;
/*
* All other activity in this process is now stopped.
- * Remove excess KSEs and KSEGRPS. XXXKSE (when we have them)
- * ...
- * Turn off threading support.
+ * Threading support has been turned off.
*/
- mtx_lock_spin(&sched_lock);
- sched_set_concurrency(td->td_ksegrp, 1);
- upcall_remove(td);
- mtx_unlock_spin(&sched_lock);
- p->p_flag &= ~(P_SA|P_HADTHREADS);
- td->td_mailbox = NULL;
- td->td_pflags &= ~TDP_SA;
- thread_single_end();
}
p->p_flag |= P_WEXIT;
@@ -488,7 +478,7 @@
* Finally, call machine-dependent code to release the remaining
* resources including address space.
* The address space is released by "vmspace_exitfree(p)" in
- * vm_waitproc(). To be called BEFORE taking the final schedlock.
+ * vm_waitproc().
*/
cpu_exit(td);
@@ -533,7 +523,8 @@
knlist_destroy(&p->p_klist);
/*
- * Make sure the system takes this thread out of its tables etc.
+ * Make sure the scheduler takes this thread out of its tables etc.
+ * This will also release this thread's reference to the ucred.
* Other thread parts to release include pcb bits and such.
*/
thread_exit();
@@ -681,8 +672,6 @@
/*
* do any thread-system specific cleanups
- * like freeing the thread's ucred,
- * and any spare threads, etc.
*/
thread_wait(p);
@@ -695,6 +684,8 @@
#ifdef MAC
mac_destroy_proc(p);
#endif
+ KASSERT(FIRST_THREAD_IN_PROC(p),
+ ("kern_wait: no residual thread!"));
uma_zfree(proc_zone, p);
sx_xlock(&allproc_lock);
nprocs--;
==== //depot/projects/nsched/sys/kern/kern_fork.c#9 (text+ko) ====
@@ -508,7 +508,7 @@
* Allow the scheduler to adjust the priority of the child and
* parent while we hold the sched_lock.
*/
- sched_fork(td, p2);
+ sched_fork(td, td2);
mtx_unlock_spin(&sched_lock);
p2->p_ucred = crhold(td->td_ucred);
==== //depot/projects/nsched/sys/kern/kern_kse.c#23 (text+ko) ====
@@ -325,9 +325,8 @@
psignal(p, SIGSEGV);
mtx_lock_spin(&sched_lock);
upcall_remove(td);
-
if (p->p_numthreads != 1) {
- /*
+ /*
* If we are not the last thread, but we are the last
* thread in this ksegrp, then by definition this is not
* the last group and we need to clean it up as well.
@@ -337,21 +336,21 @@
thread_exit();
/* NOTREACHED */
}
- /*
+ /*
* This is the last thread. Just return to the user.
* We know that there is only one ksegrp too, as any others
* would have been discarded in previous calls to thread_exit().
* Effectively we have left threading mode..
- * The only real thing left to do is ensure that the
- * scheduler sets out concurrency back to 1 as that may be a
+ * The only real thing left to do is ensure that the
+ * scheduler sets out concurrency back to 1 as that may be a
* resource leak otherwise.
* This is an A[PB]I issue.. what SHOULD we do?
* One possibility is to return to the user. It may not cope well.
* The other possibility would be to let the process exit.
*/
- p->p_flag &= ~P_SA;
+ p->p_flag &= ~(P_SA|P_HADTHREADS);
+ sched_reset_concurrency(td->td_ksegrp);
mtx_unlock_spin(&sched_lock);
- sched_set_concurrency(td->td_ksegrp, 1);
PROC_UNLOCK(p);
#if 1
return (0);
@@ -501,6 +500,10 @@
/*
* No new KSEG: first call: use current KSE, don't schedule an upcall
* All other situations, do allocate max new KSEs and schedule an upcall.
+ *
+ * XXX should be changed so that 'first' behaviour lasts for as long
+ * as you have not made a kse in this ksegrp. i.e. as long as we do not have
+ * a mailbox..
*/
/* struct kse_create_args {
struct kse_mailbox *mbx;
@@ -521,6 +524,13 @@
if ((err = copyin(uap->mbx, &mbx, sizeof(mbx))))
return (err);
+ /*
+ * Processes using the other threading model can't
+ * suddenly start calling this one
+ */
+ if ((p->p_flag & (P_SA|P_HADTHREADS)) == P_HADTHREADS)
+ return (EINVAL);
+
ncpus = mp_ncpus;
if (virtual_cpu != 0)
ncpus = virtual_cpu;
@@ -605,15 +615,17 @@
* Initialize KSE group with the appropriate
* concurrency.
*
- * For multiplxed group, set concurrency equal to physical
- * cpus. This increases concurrent even if userland
- * is not MP safe and can only run on single CPU.
- * In ideal world, every physical cpu should execute a thread.
- * If there is enough KSEs, threads in kernel can be
- * executed parallel on different cpus with full speed,
- * Concurrent in kernel shouldn't be restricted by number of
- * upcalls userland provides. Adding more upcall structures
- * only increases concurrent in userland.
+ * For a multiplexed group, create as as much concurrency
+ * as the number of physical cpus.
+ * This increases concurrency in the kernel even if the
+ * userland is not MP safe and can only run on a single CPU.
+ * In an ideal world, every physical cpu should execute a
+ * thread. If there is enough concurrency, threads in the
+ * kernel can be executed parallel on different cpus at
+ * full speed without being restricted by the number of
+ * upcalls the userland provides.
+ * Adding more upcall structures only increases concurrency
+ * in userland.
*
* For a bound thread group, because there is only one thread
* in the group, we only set the concurrency for the group
@@ -767,7 +779,6 @@
NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
}
-
/*
* Stash an embarasingly extra upcall into the zombie upcall queue.
*/
@@ -1002,7 +1013,8 @@
/*
* This function is intended to be used to initialize a spare thread
* for upcall. Initialize thread's large data area outside sched_lock
- * for thread_schedule_upcall().
+ * for thread_schedule_upcall(). The crhold is also here to get it out
+ * from the schedlock as it has a mutex op itself.
*/
void
thread_alloc_spare(struct thread *td)
@@ -1011,7 +1023,8 @@
if (td->td_standin)
return;
- td->td_standin = spare = thread_alloc();
+ spare = thread_alloc();
+ td->td_standin = spare;
bzero(&spare->td_startzero,
(unsigned) RANGEOF(struct thread, td_startzero, td_endzero));
spare->td_proc = td->td_proc;
@@ -1056,11 +1069,13 @@
ku->ku_owner = td2;
td2->td_upcall = ku;
td2->td_flags = 0;
- td2->td_pflags = (TDP_SA | TDP_UPCALLING);
+ td2->td_pflags = TDP_SA|TDP_UPCALLING;
+ td2->td_kse = NULL;
td2->td_state = TDS_CAN_RUN;
td2->td_inhibitors = 0;
SIGFILLSET(td2->td_sigmask);
SIG_CANTMASK(td2->td_sigmask);
+ sched_fork_thread(td, td2);
return (td2); /* bogus.. should be a void function */
}
@@ -1181,8 +1196,6 @@
(ku->ku_mflags & KMF_NOUPCALL)) {
td->td_mailbox = NULL;
} else {
- if (td->td_standin == NULL)
- thread_alloc_spare(td);
flags = fuword32(&tmbx->tm_flags);
/*
* On some architectures, TP register points to thread
==== //depot/projects/nsched/sys/kern/kern_proc.c#11 (text+ko) ====
@@ -100,8 +100,6 @@
SYSCTL_INT(_kern, OID_AUTO, kstack_pages, CTLFLAG_RD, &kstack_pages, 0, "");
SYSCTL_INT(_kern, OID_AUTO, uarea_pages, CTLFLAG_RD, &uarea_pages, 0, "");
-#define RANGEOF(type, start, end) (offsetof(type, end) - offsetof(type, start))
-
CTASSERT(sizeof(struct kinfo_proc) == KINFO_PROC_SIZE);
/*
@@ -127,7 +125,6 @@
/*
* Prepare a proc for use.
- * cache->used
*/
static int
proc_ctor(void *mem, int size, void *arg, int flags)
@@ -140,7 +137,6 @@
/*
* Reclaim a proc after use.
- * used -> cache
*/
static void
proc_dtor(void *mem, int size, void *arg)
@@ -151,7 +147,7 @@
/* INVARIANTS checks go here */
p = (struct proc *)mem;
- td = FIRST_THREAD_IN_PROC(p);
+ td = FIRST_THREAD_IN_PROC(p);
#ifdef INVARIANTS
KASSERT((p->p_numthreads == 1),
("bad number of threads in exiting process"));
@@ -171,7 +167,6 @@
/*
* Initialize type-stable parts of a proc (when newly created).
- * raw memory -> cache
*/
static int
proc_init(void *mem, int size, int flags)
@@ -185,15 +180,15 @@
vm_proc_new(p);
td = thread_alloc();
kg = ksegrp_alloc();
+ bzero(&p->p_mtx, sizeof(struct mtx));
mtx_init(&p->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK);
proc_linkup(p, kg, td);
- sched_newproc(p, kg, td); /* err? */
+ sched_newproc(p, kg, td);
return (0);
}
/*
* Tear down type-stable parts of a proc (just before being discarded)
- * cache -> free memeory
*/
static void
proc_fini(void *mem, int size)
@@ -765,8 +760,8 @@
kp->ki_kstack = (void *)td->td_kstack;
kp->ki_pctcpu = sched_pctcpu(td);
+ /* Things in the kse */
#if 0
- /* Things in the kse */
if (ke)
kp->ki_rqindex = ke->ke_rqindex;
else
==== //depot/projects/nsched/sys/kern/kern_switch.c#5 (text+ko) ====
@@ -90,6 +90,7 @@
#include "opt_full_preemption.h"
+#ifndef KERN_SWITCH_INCLUDE
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kdb.h>
@@ -100,6 +101,7 @@
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/sched.h>
+#else /* KERN_SWITCH_INCLUDE */
#if defined(SMP) && (defined(__i386__) || defined(__amd64__))
#include <sys/smp.h>
#endif
@@ -223,6 +225,7 @@
TAILQ_INSERT_TAIL(&kg->kg_iq, ke, ke_kgrlist);
kg->kg_idle_kses++;
CTR1(KTR_RUNQ, "kse_reassign: ke%p on idle queue", ke);
+ sched_check_concurrency(kg); /* could implement directly */
return;
}
@@ -837,3 +840,348 @@
}
#endif
+/****** functions that are temporarily here ***********/
+#include <vm/uma.h>
+#define RANGEOF(type, start, end) (offsetof(type, end) - offsetof(type, start))
+static uma_zone_t kse_zone;
+TAILQ_HEAD(, kse) zombie_kses = TAILQ_HEAD_INITIALIZER(zombie_kses);
+extern struct mtx kse_zombie_lock;
+
+/*
+ * Initialize type-stable parts of a kse (when newly created).
+ */
+static int
+kse_init(void *mem, int size, int flags)
+{
+ struct kse *ke;
+
+ ke = (struct kse *)mem;
+ ke->ke_sched = (struct ke_sched *)&ke[1];
+ return (0);
+}
+
+/*
+ * Allocate a kse.
+ */
+static struct kse *
+kse_alloc(void)
+{
+ return (uma_zalloc(kse_zone, M_WAITOK));
+}
+
+/*
+ * Deallocate a kse.
+ */
+void
+kse_free(struct kse *td)
+{
+ uma_zfree(kse_zone, td);
+}
+
+/*
+ * KSE is linked into kse group.
+ * If we know the thread at this time attach to it,
+ * otherwise put it on the idle kse queue.
+ * Called from:
+ * sched_init_concurrency() schedlock
+ * sched_set_concurrency() schedlock
+ * schedinit2() NO schedlock (too early)
+ */
+static void
+kse_link(struct kse *ke, struct ksegrp *kg, struct thread *td)
+{
+ struct proc *p = kg->kg_proc;
+
+ TAILQ_INSERT_HEAD(&kg->kg_kseq, ke, ke_kglist);
+ kg->kg_kses++;
+ ke->ke_proc = p;
+ ke->ke_ksegrp = kg;
+ ke->ke_oncpu = NOCPU;
+ ke->ke_flags = 0;
+ if (td) {
+ ke->ke_state = KES_THREAD;
+ td->td_kse = ke;
+ ke->ke_thread = td;
+ } else {
+ TAILQ_INSERT_TAIL(&kg->kg_iq, ke, ke_kgrlist);
+ kg->kg_idle_kses++;
+ ke->ke_state = KES_IDLE;
+ ke->ke_thread = NULL;
+ }
+}
+
+/*
+ * Stash an embarasingly extra kse into the zombie kse queue.
+ */
+static void
+kse_stash(struct kse *ke)
+{
+ mtx_lock_spin(&kse_zombie_lock);
+ TAILQ_INSERT_HEAD(&zombie_kses, ke, ke_procq);
+ mtx_unlock_spin(&kse_zombie_lock);
+}
+
+/*
+ * Called from:
+ * sched_set_concurrency()
+ * sched_reset_concurrency()
+ * sched_check_concurrency()
+ */
+static void
+kse_unlink(struct kse *ke)
+{
+ struct ksegrp *kg;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ kg = ke->ke_ksegrp;
+ TAILQ_REMOVE(&kg->kg_kseq, ke, ke_kglist);
+ if (ke->ke_state == KES_IDLE) {
+ TAILQ_REMOVE(&kg->kg_iq, ke, ke_kgrlist);
+ kg->kg_idle_kses--;
+ }
+ --kg->kg_kses;
+ /*
+ * Aggregate stats from the KSE
+ */
+ kse_stash(ke);
+}
+
+
+/*
+ * Concurrency is implemented using the number of KSEs.
+ * This will be re-implmented using another method, so
+ * isolate the details with a simple API.
+ * Once the API has been implemented, we can switch out the
+ * underlying implementation.
+ */
+
+/*
+ * Allocate scheduler specific per-process resources.
+ * The thread and ksegrp have already been linked in.
+ * Called from:
+ * proc_init() (UMA init method)
+ */
+void
+sched_newproc(struct proc *p, struct ksegrp *kg, struct thread *td)
+{
+
+ sched_init_concurrency(kg, td);
+}
+
+/*
+ * Called by the uma process fini routine..
+ * undo anything we may have done in the uma_init method.
+ * Panic if it's not all 1:1:1:1
+ * Called from:
+ * proc_fini() (UMA method)
+ */
+void
+sched_destroyproc(struct proc *p)
+{
+ struct ksegrp *kg;
+
+ KASSERT((p->p_numthreads == 1), ("Cached proc with > 1 thread "));
+ KASSERT((p->p_numksegrps == 1), ("Cached proc with > 1 ksegrp "));
+ kg = FIRST_KSEGRP_IN_PROC(p);
+ KASSERT((kg->kg_kses == 1), ("Cached proc with > 1 kse "));
+ kse_free(TAILQ_FIRST(&kg->kg_kseq));
+}
+
+/*
+ * thread is being either created or recycled.
+ * Fix up the per-scheduler resources associated with it.
+ * Called from:
+ * thread_dtor()
+ * thread_init()
+ */
+void
+sched_newthread(struct thread *td)
+{
+ td->td_last_kse = NULL;
+ td->td_kse = NULL;
+}
+
+/*
+ * (Re) assign resources to allow the ksegrp to implement
+ * the requested concurrency. At this time it means allocating
+ * or freeing KSE structures.
+ * We may not remove all the KSEs if there are enough threads in the
+ * ksegrp to justify them. They will eventually go away as they are added
+ * to the free kse queue and threads exit.
+ */
+
+/*
+ * set up an initial concurrency of 1
+ * and set the given thread (if given) to be using that
+ * concurrency slot.
+ * May be used "offline"..before the ksegrp is attached to the world
+ * and thus wouldn't need schedlock in that case.
+ * Called from:
+ * thr_create()
+ * proc_init() (UMA) via sched_newproc()
+ */
+void
+sched_init_concurrency(struct ksegrp *kg, struct thread *td)
+{
+ struct kse *newke;
+
+ newke = kse_alloc();
+ if (newke == NULL)
+ panic("sched_init_concurrency: no kse allocated");
+ kg->kg_concurrency = 1;
+ mtx_lock_spin(&sched_lock);
+ kse_link(newke, kg , td);
+ mtx_unlock_spin(&sched_lock);
+}
+
+/*
+ * Change the concurrency of an existing ksegrp to N
+ * Called from:
+ * kse_create()
+ */
+void
+sched_set_concurrency(struct ksegrp *kg, int concurrency)
+{
+ struct kse *newke;
+
+ kg->kg_concurrency = concurrency;
+ /* Handle the case for a declining concurrency */
+ while ((kg->kg_concurrency < kg->kg_kses) &&
+ (kg->kg_idle_kses > 0) &&
+ (kg->kg_kses > kg->kg_numthreads)) {
+ kse_unlink(TAILQ_FIRST(&kg->kg_iq));
+ }
+ while (kg->kg_kses < kg->kg_concurrency) {
+ newke = kse_alloc();
+ if (newke == NULL)
+ panic("sched_set_concurrency: no kse allocated");
+ bzero(&newke->ke_startzero, RANGEOF(struct kse,
+ ke_startzero, ke_endzero));
+ mtx_lock_spin(&sched_lock);
+ kse_link(newke, kg , NULL);
+ sched_fork_kse(curthread, newke);
+ mtx_unlock_spin(&sched_lock);
+ }
+}
+
+/*
+ * This is a temporary function to remove the KSE(s) from a ksegrp when the
+ * ksegrp is being destroyed, so we need not keep it consistent.
+ * Called from:
+ * thread_exit()
+ * Not called from sched_destroyproc() which does it itself.
+ */
+void
+sched_remove_concurrency(struct ksegrp *kg)
+{
+ struct kse *ke, *nextke;
+ ke = TAILQ_FIRST(&kg->kg_kseq);
+ while (ke != NULL) {
+ nextke = TAILQ_NEXT(ke, ke_kglist);
+ kse_stash(ke);
+ ke = nextke;
+ }
+ /* set these down just so later kasserts don't trigger */
+ kg->kg_kses = 0;
+ kg->kg_idle_kses = 0;
+ kg->kg_concurrency = 0;
+}
+
+/*
+ * Whenever we have idle KSEs and there are too many for the concurrency,
+ * then free as many as we can. Don't free too many if we have threads
+ * to run/kill.
+ * Called from:
+ * kse_reassign().
+ */
+void
+sched_check_concurrency(struct ksegrp * kg) {
+ while ((kg->kg_concurrency < kg->kg_kses) &&
+ (kg->kg_idle_kses > 0) &&
+ (kg->kg_kses > kg->kg_numthreads)) {
+ kse_unlink(TAILQ_FIRST(&kg->kg_iq));
+ }
+}
+
+/*
+ * Reset the Concurrency back to 1, whatever that means to this scheduler.
+ * In this case, free all but one KSE.
+ * Called from:
+ * thread_single(SINGLE_EXIT) (e.g. in execve and exit)
+ * kse_exit()
+ */
+void
+sched_reset_concurrency(struct ksegrp *kg)
+{
+ KASSERT((kg->kg_numthreads == 1),
+ ("sched_reset_concurrency: Nthread!= 1"));
+ mtx_assert(&sched_lock, MA_OWNED);
+ kg->kg_concurrency = 1;
+ while ((kg->kg_concurrency < kg->kg_kses) &&
+ (kg->kg_idle_kses > 0) &&
+ (kg->kg_kses > kg->kg_numthreads)) {
+ kse_unlink(TAILQ_FIRST(&kg->kg_iq));
+ }
+}
+
+/*
+ * Very early in the boot some setup of scheduler-specific
+ * parts of proc0 and of some scheduler resources needs to be done.
+ * This is temporary, it will merge with schedinit() in each scheduler
+ * soon.
+ * Called from:
+ * proc0_init()
+ */
+void
+schedinit2(void)
+{
+ /*
+ * Put our per-scheduler struct on the ksegrp/thread
+ * this puts it on the idle queue.
+ */
+ kse_link(&kse0, &ksegrp0, &thread0);
+
+ kse_zone = uma_zcreate("KSE", sched_sizeof_kse(),
+ NULL, NULL, kse_init, NULL, UMA_ALIGN_CACHE, 0);
+}
+
+void
+sched_thread_exit(struct thread *td)
+{
+ struct kse *ke;
+
+ ke = td->td_kse;
+
+ if ((td->td_proc->p_flag & P_SA) && ke != NULL) {
+ ke->ke_thread = NULL;
+ td->td_kse = NULL;
+ kse_reassign(ke);
+ }
+}
+
+/*
+ * Reap zombie kse resource.
+ */
+void
+sched_GC(void)
+{
+ struct kse *ke_first, *ke_next;
+
+ /*
+ * Don't even bother to lock if none at this instant,
+ * we really don't care about the next instant..
+ */
+ if (!TAILQ_EMPTY(&zombie_kses)) {
+ mtx_lock_spin(&kse_zombie_lock);
+ ke_first = TAILQ_FIRST(&zombie_kses);
+ if (ke_first)
+ TAILQ_INIT(&zombie_kses);
+ mtx_unlock_spin(&kse_zombie_lock);
+ while (ke_first) {
+ ke_next = TAILQ_NEXT(ke_first, ke_procq);
+ kse_free(ke_first);
+ ke_first = ke_next;
+ }
+ }
+}
+#endif /* KERN_SWITCH_INCLUDE */
==== //depot/projects/nsched/sys/kern/kern_synch.c#9 (text+ko) ====
==== //depot/projects/nsched/sys/kern/kern_thr.c#12 (text+ko) ====
@@ -44,69 +44,13 @@
#include <machine/frame.h>
-extern int virtual_cpu;
+extern int max_threads_per_proc;
+extern int max_groups_per_proc;
+
/*
* Back end support functions.
*/
-void
-thr_exit1(void)
-{
- struct ksegrp *kg;
- struct thread *td;
- struct proc *p;
-
- td = curthread;
- p = td->td_proc;
- kg = td->td_ksegrp;
-
- mtx_assert(&sched_lock, MA_OWNED);
- PROC_LOCK_ASSERT(p, MA_OWNED);
- KASSERT(!mtx_owned(&Giant), ("dying thread owns giant"));
-
- /*
- * Shutting down last thread in the proc. This will actually
- * call exit() in the trampoline when it returns.
- */
- if (p->p_numthreads == 1) {
- PROC_UNLOCK(p);
- return;
- }
-
- /*
- * XXX Undelivered process wide signals should be reposted to the
- * proc.
- */
-
- /* Clean up cpu resources. */
- cpu_thread_exit(td);
-
- /* let the scheduler know we are dying.. */
- /* Lots in common with sched_thread_exit.. merge one day */
- sched_thr_exit(td);
-
- /* Unlink the thread from the process and kseg. */
- thread_unlink(td);
-
- /*
- * If we were stopped while waiting for all threads to exit and this
- * is the last thread wakeup the exiting thread.
- */
- if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE)
- if (p->p_numthreads == 1)
- thread_unsuspend_one(p->p_singlethread);
-
- PROC_UNLOCK(p);
- td->td_state = TDS_INACTIVE;
-#if 0
- td->td_proc = NULL;
-#endif
- td->td_ksegrp = NULL;
- thread_stash(td);
-
- cpu_throw(td, choosethread(SW_VOL));
-}
-
#define RANGEOF(type, start, end) (offsetof(type, end) - offsetof(type, start))
/*
@@ -116,57 +60,82 @@
thr_create(struct thread *td, struct thr_create_args *uap)
/* ucontext_t *ctx, long *id, int flags */
{
- struct thread *td0;
+ struct thread *newtd;
ucontext_t ctx;
long id;
int error;
- int ncpus;
+ struct ksegrp *kg, *newkg;
+ struct proc *p;
+ p = td->td_proc;
+ kg = td->td_ksegrp;
if ((error = copyin(uap->ctx, &ctx, sizeof(ctx))))
return (error);
- /* Initialize our td. */
- td0 = thread_alloc();
-
+ /* Have race condition but it is cheap */
+ if ((p->p_numksegrps >= max_groups_per_proc) ||
+ (p->p_numthreads >= max_threads_per_proc)) {
+ return (EPROCLIM);
+ }
+ /* Initialize our td and new ksegrp.. */
+ newtd = thread_alloc();
+ newkg = ksegrp_alloc();
/*
* Try the copyout as soon as we allocate the td so we don't have to
* tear things down in a failure case below.
*/
- id = td0->td_tid;
+ id = newtd->td_tid;
if ((error = copyout(&id, uap->id, sizeof(long)))) {
- thread_free(td0);
+ ksegrp_free(newkg);
+ thread_free(newtd);
return (error);
}
- bzero(&td0->td_startzero,
- (unsigned)RANGEOF(struct thread, td_startzero, td_endzero));
- bcopy(&td->td_startcopy, &td0->td_startcopy,
+ bzero(&newtd->td_startzero,
+ (unsigned) RANGEOF(struct thread, td_startzero, td_endzero));
+ bcopy(&td->td_startcopy, &newtd->td_startcopy,
(unsigned) RANGEOF(struct thread, td_startcopy, td_endcopy));
- td0->td_proc = td->td_proc;
- PROC_LOCK(td->td_proc);
- td0->td_sigmask = td->td_sigmask;
- /* First time through? */
- if ((td->td_proc->p_flag & P_HADTHREADS) == 0) {
- ncpus = mp_ncpus;
- if (virtual_cpu != 0)
- ncpus = virtual_cpu;
- sched_set_concurrency(td->td_ksegrp, ncpus);
- td->td_proc->p_flag |= P_HADTHREADS;
- }
- PROC_UNLOCK(td->td_proc);
- td0->td_ucred = crhold(td->td_ucred);
+ bzero(&newkg->kg_startzero,
+ (unsigned) RANGEOF(struct ksegrp, kg_startzero, kg_endzero));
+ bcopy(&kg->kg_startcopy, &newkg->kg_startcopy,
+ (unsigned) RANGEOF(struct ksegrp, kg_startcopy, kg_endcopy));
+
+ newtd->td_proc = td->td_proc;
+ newtd->td_ucred = crhold(td->td_ucred);
/* Set up our machine context. */
>>> TRUNCATED FOR MAIL (1000 lines) <<<
More information about the p4-projects
mailing list