summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>2002-12-10 02:33:45 +0000
committerjulian <julian@FreeBSD.org>2002-12-10 02:33:45 +0000
commit9868d96f1fab28e828ad309a400539d072e1da2d (patch)
treeb7b4d7891168c36af86fd5540478865532fe8977 /sys
parent9349f701144f7b1af93f420305e9cbf99727d7ed (diff)
downloadFreeBSD-src-9868d96f1fab28e828ad309a400539d072e1da2d.zip
FreeBSD-src-9868d96f1fab28e828ad309a400539d072e1da2d.tar.gz
Unbreak the KSE code. Keep track of zobie threads using the Per-CPU storage
during the context switch. Rearrange thread cleanups to avoid problems with Giant. Clean threads when freed or when recycled. Approved by: re (jhb)
Diffstat (limited to 'sys')
-rw-r--r--sys/alpha/alpha/vm_machdep.c2
-rw-r--r--sys/amd64/amd64/vm_machdep.c13
-rw-r--r--sys/i386/i386/vm_machdep.c13
-rw-r--r--sys/ia64/ia64/vm_machdep.c2
-rw-r--r--sys/kern/kern_exit.c30
-rw-r--r--sys/kern/kern_fork.c10
-rw-r--r--sys/kern/kern_kse.c195
-rw-r--r--sys/kern/kern_synch.c8
-rw-r--r--sys/kern/kern_thread.c195
-rw-r--r--sys/powerpc/aim/vm_machdep.c2
-rw-r--r--sys/powerpc/powerpc/vm_machdep.c2
-rw-r--r--sys/sparc64/sparc64/vm_machdep.c2
-rw-r--r--sys/sys/pcpu.h1
-rw-r--r--sys/sys/proc.h3
14 files changed, 198 insertions, 280 deletions
diff --git a/sys/alpha/alpha/vm_machdep.c b/sys/alpha/alpha/vm_machdep.c
index 09158d8..8fdc15b 100644
--- a/sys/alpha/alpha/vm_machdep.c
+++ b/sys/alpha/alpha/vm_machdep.c
@@ -261,7 +261,7 @@ cpu_thread_exit(struct thread *td)
}
void
-cpu_thread_dtor(struct thread *td)
+cpu_thread_clean(struct thread *td)
{
}
diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c
index 1f1e400..4f1734a 100644
--- a/sys/amd64/amd64/vm_machdep.c
+++ b/sys/amd64/amd64/vm_machdep.c
@@ -282,7 +282,7 @@ cpu_thread_exit(struct thread *td)
}
void
-cpu_thread_dtor(struct thread *td)
+cpu_thread_clean(struct thread *td)
{
struct pcb *pcb;
@@ -294,8 +294,10 @@ cpu_thread_dtor(struct thread *td)
* XXX do we need to move the TSS off the allocated pages
* before freeing them? (not done here)
*/
+ mtx_lock(&Giant);
kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ext,
ctob(IOPAGES + 1));
+ mtx_unlock(&Giant);
pcb->pcb_ext = 0;
}
}
@@ -388,6 +390,15 @@ void
cpu_set_upcall_kse(struct thread *td, struct kse *ke)
{
+ /*
+ * Do any extra cleaning that needs to be done.
+ * The thread may have optional components
+ * that are not present in a fresh thread.
+ * This may be a recycled thread so make it look
+ * as though it's newly allocated.
+ */
+ cpu_thread_clean(td);
+
/*
* Set the trap frame to point at the beginning of the uts
* function.
diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c
index 1f1e400..4f1734a 100644
--- a/sys/i386/i386/vm_machdep.c
+++ b/sys/i386/i386/vm_machdep.c
@@ -282,7 +282,7 @@ cpu_thread_exit(struct thread *td)
}
void
-cpu_thread_dtor(struct thread *td)
+cpu_thread_clean(struct thread *td)
{
struct pcb *pcb;
@@ -294,8 +294,10 @@ cpu_thread_dtor(struct thread *td)
* XXX do we need to move the TSS off the allocated pages
* before freeing them? (not done here)
*/
+ mtx_lock(&Giant);
kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ext,
ctob(IOPAGES + 1));
+ mtx_unlock(&Giant);
pcb->pcb_ext = 0;
}
}
@@ -388,6 +390,15 @@ void
cpu_set_upcall_kse(struct thread *td, struct kse *ke)
{
+ /*
+ * Do any extra cleaning that needs to be done.
+ * The thread may have optional components
+ * that are not present in a fresh thread.
+ * This may be a recycled thread so make it look
+ * as though it's newly allocated.
+ */
+ cpu_thread_clean(td);
+
/*
* Set the trap frame to point at the beginning of the uts
* function.
diff --git a/sys/ia64/ia64/vm_machdep.c b/sys/ia64/ia64/vm_machdep.c
index d4a8c64..6c64f58 100644
--- a/sys/ia64/ia64/vm_machdep.c
+++ b/sys/ia64/ia64/vm_machdep.c
@@ -118,7 +118,7 @@ cpu_thread_exit(struct thread *td)
}
void
-cpu_thread_dtor(struct thread *td)
+cpu_thread_clean(struct thread *td)
{
}
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 0ee27b7..38d8092 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -483,7 +483,7 @@ exit1(td, rv)
/*
* Finally, call machine-dependent code to release the remaining
- * resources including address space, the kernel stack and pcb.
+ * resources including address space.
* The address space is released by "vmspace_exitfree(p)" in
* vm_waitproc().
*/
@@ -493,6 +493,7 @@ exit1(td, rv)
PROC_LOCK(p->p_pptr);
sx_xunlock(&proctree_lock);
mtx_lock_spin(&sched_lock);
+
while (mtx_owned(&Giant))
mtx_unlock(&Giant);
@@ -512,11 +513,11 @@ exit1(td, rv)
cpu_sched_exit(td); /* XXXKSE check if this should be in thread_exit */
/*
- * Make sure this thread is discarded from the zombie.
+ * 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();
- panic("exit1");
}
#ifdef COMPAT_43
@@ -570,9 +571,6 @@ wait1(td, uap, compat)
int nfound;
struct proc *p, *q, *t;
int status, error;
- struct thread *td2;
- struct kse *ke;
- struct ksegrp *kg;
q = td->td_proc;
if (uap->pid == 0) {
@@ -717,25 +715,9 @@ loop:
}
/*
- * There should only be one
- * but do it right anyhow.
+ * do any thread-system specific cleanups
*/
- FOREACH_KSEGRP_IN_PROC(p, kg) {
- FOREACH_KSE_IN_GROUP(kg, ke) {
- /* Free the KSE spare thread. */
- if (ke->ke_tdspare != NULL) {
- thread_free(ke->ke_tdspare);
- ke->ke_tdspare = NULL;
- }
- }
- }
- FOREACH_THREAD_IN_PROC(p, td2) {
- if (td2->td_standin != NULL) {
- thread_free(td2->td_standin);
- td2->td_standin = NULL;
- }
- }
- thread_reap(); /* check for zombie threads */
+ thread_wait(p);
/*
* Give vm and machine-dependent layer a chance
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index caefeff..c63a839 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -834,9 +834,15 @@ fork_exit(callout, arg, frame)
void *arg;
struct trapframe *frame;
{
- struct thread *td = curthread;
- struct proc *p = td->td_proc;
+ struct thread *td;
+ struct proc *p;
+ if ((td = PCPU_GET(deadthread))) {
+ PCPU_SET(deadthread, NULL);
+ thread_stash(td);
+ }
+ td = curthread;
+ p = td->td_proc;
td->td_kse->ke_oncpu = PCPU_GET(cpuid);
p->p_state = PRS_NORMAL;
/*
diff --git a/sys/kern/kern_kse.c b/sys/kern/kern_kse.c
index cf17c08..22f173e 100644
--- a/sys/kern/kern_kse.c
+++ b/sys/kern/kern_kse.c
@@ -66,13 +66,9 @@ static uma_zone_t thread_zone;
/* DEBUG ONLY */
SYSCTL_NODE(_kern, OID_AUTO, threads, CTLFLAG_RW, 0, "thread allocation");
-static int oiks_debug = 0; /* 0 disable, 1 printf, 2 enter debugger */
-SYSCTL_INT(_kern_threads, OID_AUTO, oiks, CTLFLAG_RW,
- &oiks_debug, 0, "OIKS thread debug");
-
-static int oiks_max_threads_per_proc = 10;
-SYSCTL_INT(_kern_threads, OID_AUTO, oiks_max_per_proc, CTLFLAG_RW,
- &oiks_max_threads_per_proc, 0, "Debug limit on threads per proc");
+static int thread_debug = 0;
+SYSCTL_INT(_kern_threads, OID_AUTO, debug, CTLFLAG_RW,
+ &thread_debug, 0, "thread debug");
static int max_threads_per_proc = 30;
SYSCTL_INT(_kern_threads, OID_AUTO, max_threads_per_proc, CTLFLAG_RW,
@@ -91,11 +87,10 @@ struct mtx zombie_thread_lock;
MTX_SYSINIT(zombie_thread_lock, &zombie_thread_lock,
"zombie_thread_lock", MTX_SPIN);
+static void kse_purge(struct proc *p, struct thread *td);
-
-void kse_purge(struct proc *p, struct thread *td);
/*
- * Pepare a thread for use.
+ * Prepare a thread for use.
*/
static void
thread_ctor(void *mem, int size, void *arg)
@@ -115,7 +110,6 @@ thread_dtor(void *mem, int size, void *arg)
{
struct thread *td;
- mtx_assert(&Giant, MA_OWNED);
td = (struct thread *)mem;
#ifdef INVARIANTS
@@ -138,8 +132,6 @@ thread_dtor(void *mem, int size, void *arg)
/* NOTREACHED */
}
#endif
-
- cpu_thread_dtor(td);
}
/*
@@ -346,12 +338,11 @@ kse_exit(struct thread *td, struct kse_exit_args *uap)
return (EDEADLK);
}
if ((p->p_numthreads == 1) && (p->p_numksegrps == 1)) {
+ /* XXXSKE what if >1 KSE? check.... */
p->p_flag &= ~P_KSES;
mtx_unlock_spin(&sched_lock);
PROC_UNLOCK(p);
} else {
- while (mtx_owned(&Giant))
- mtx_unlock(&Giant);
td->td_kse->ke_flags |= KEF_EXIT;
thread_exit();
/* NOTREACHED */
@@ -359,40 +350,50 @@ kse_exit(struct thread *td, struct kse_exit_args *uap)
return (0);
}
+/*
+ * Either returns as an upcall or exits
+ */
int
-kse_release(struct thread *td, struct kse_release_args *uap)
+kse_release(struct thread * td, struct kse_release_args * uap)
{
struct proc *p;
+ struct ksegrp *kg;
p = td->td_proc;
- /* KSE-enabled processes only */
- if (!(p->p_flag & P_KSES))
- return (EINVAL);
+ kg = td->td_ksegrp;
/*
* Must be a bound thread. And kse must have a mailbox ready,
- * if not, the kse would can not generate an upcall.
+ * if not, the kse can not generate an upcall.
*/
- if (!(td->td_flags & TDF_UNBOUND) && (td->td_kse->ke_mailbox != NULL)) {
- PROC_LOCK(p);
- mtx_lock_spin(&sched_lock);
- /* prevent last thread from exiting */
+ if (!(p->p_flag & P_KSES) ||
+ (td->td_flags & TDF_UNBOUND) ||
+ (td->td_kse->ke_mailbox == NULL))
+ return (EINVAL);
+ PROC_LOCK(p);
+ mtx_lock_spin(&sched_lock);
+ if (kg->kg_completed == NULL) {
+#if 1 /* temp until signals make new threads */
if (p->p_numthreads == 1) {
+ /* change OURSELF to become an upcall */
+ td->td_flags = TDF_UPCALLING;
mtx_unlock_spin(&sched_lock);
- if (td->td_standin == NULL) {
- PROC_UNLOCK(p);
- td->td_standin = thread_alloc();
- PROC_LOCK(p);
- }
- msleep(p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH,
- "pause", 0);
- mtx_lock_spin(&sched_lock);
- td->td_flags |= TDF_UNBOUND;
- thread_schedule_upcall(td, td->td_kse);
+ PROC_UNLOCK(p);
+ /*
+ * msleep will not call thread_sched_upcall
+ * because thread is not UNBOUND.
+ */
+ msleep(p->p_sigacts, NULL,
+ PPAUSE | PCATCH, "ksepause", 0);
+ return (0);
}
+#endif /* end temp */
thread_exit();
- /* NOTREACHED */
}
- return (EINVAL);
+ /* change OURSELF to become an upcall */
+ td->td_flags = TDF_UPCALLING;
+ mtx_unlock_spin(&sched_lock);
+ PROC_UNLOCK(p);
+ return (0);
}
/* struct kse_wakeup_args {
@@ -409,8 +410,6 @@ kse_wakeup(struct thread *td, struct kse_wakeup_args *uap)
/* KSE-enabled processes only, please. */
if (!(p->p_flag & P_KSES))
return EINVAL;
- if (td->td_standin == NULL)
- td->td_standin = thread_alloc();
ke = NULL;
mtx_lock_spin(&sched_lock);
if (uap->mbx) {
@@ -507,7 +506,7 @@ kse_create(struct thread *td, struct kse_create_args *uap)
* which is safe.
*/
if ((td->td_flags & TDF_UNBOUND) || td->td_kse->ke_mailbox) {
- if (oiks_debug == 0) {
+ if (thread_debug == 0) { /* if debugging, allow more */
#ifdef SMP
if (kg->kg_kses > mp_ncpus)
#endif
@@ -779,6 +778,8 @@ kse_free(struct kse *td)
void
thread_free(struct thread *td)
{
+
+ cpu_thread_clean(td);
uma_zfree(thread_zone, td);
}
@@ -966,10 +967,9 @@ bad:
* Discard the current thread and exit from its context.
*
* Because we can't free a thread while we're operating under its context,
- * push the current thread into our KSE's ke_tdspare slot, freeing the
- * thread that might be there currently. Because we know that only this
- * processor will run our KSE, we needn't worry about someone else grabbing
- * our context before we do a cpu_throw.
+ * push the current thread into our CPU's deadthread holder. This means
+ * we needn't worry about someone else grabbing our context before we
+ * do a cpu_throw().
*/
void
thread_exit(void)
@@ -992,10 +992,6 @@ thread_exit(void)
CTR1(KTR_PROC, "thread_exit: thread %p", td);
KASSERT(!mtx_owned(&Giant), ("dying thread owns giant"));
- if (ke->ke_tdspare != NULL) {
- thread_stash(ke->ke_tdspare);
- ke->ke_tdspare = NULL;
- }
if (td->td_standin != NULL) {
thread_stash(td->td_standin);
td->td_standin = NULL;
@@ -1039,87 +1035,47 @@ thread_exit(void)
("thread_exit: entered with ke_bound set"));
/*
- * The reason for all this hoopla is
- * an attempt to stop our thread stack from being freed
- * until AFTER we have stopped running on it.
- * Since we are under schedlock, almost any method where
- * it is eventually freed by someone else is probably ok.
- * (Especially if they do it under schedlock). We could
- * almost free it here if we could be certain that
- * the uma code wouldn't pull it apart immediatly,
- * but unfortunatly we can not guarantee that.
- *
- * For threads that are exiting and NOT killing their
- * KSEs we can just stash it in the KSE, however
- * in the case where the KSE is also being deallocated,
- * we need to store it somewhere else. It turns out that
- * we will never free the last KSE, so there is always one
- * other KSE available. We might as well just choose one
- * and stash it there. Being under schedlock should make that
- * safe.
- *
- * In borrower threads, we can stash it in the lender
- * Where it won't be needed until this thread is long gone.
- * Borrower threads can't kill their KSE anyhow, so even
- * the KSE would be a safe place for them. It is not
- * necessary to have a KSE (or KSEGRP) at all beyond this
- * point, while we are under the protection of schedlock.
- *
- * Either give the KSE to another thread to use (or make
- * it idle), or free it entirely, possibly along with its
- * ksegrp if it's the last one.
+ * decide what to do with the KSE attached to this thread.
*/
if (ke->ke_flags & KEF_EXIT) {
kse_unlink(ke);
- /*
- * Designate another KSE to hold our thread.
- * Safe as long as we abide by whatever lock
- * we control it with.. The other KSE will not
- * be able to run it until we release the schelock,
- * but we need to be careful about it deciding to
- * write to the stack before then. Luckily
- * I believe that while another thread's
- * standin thread can be used in this way, the
- * spare thread for the KSE cannot be used without
- * holding schedlock at least once.
- */
- ke = FIRST_KSE_IN_PROC(p);
} else {
kse_reassign(ke);
}
-#if 0
- if (ke->ke_bound) {
- /*
- * WE are a borrower..
- * stash our thread with the owner.
- */
- if (ke->ke_bound->td_standin) {
- thread_stash(ke->ke_bound->td_standin);
- }
- ke->ke_bound->td_standin = td;
- } else {
-#endif
- if (ke->ke_tdspare != NULL) {
- thread_stash(ke->ke_tdspare);
- ke->ke_tdspare = NULL;
- }
- ke->ke_tdspare = td;
-#if 0
- }
-#endif
PROC_UNLOCK(p);
td->td_state = TDS_INACTIVE;
td->td_proc = NULL;
td->td_ksegrp = NULL;
td->td_last_kse = NULL;
+ PCPU_SET(deadthread, td);
} else {
PROC_UNLOCK(p);
}
-
cpu_throw();
/* NOTREACHED */
}
+/*
+ * Do any thread specific cleanups that may be needed in wait()
+ * called with Giant held, proc and schedlock not held.
+ */
+void
+thread_wait(struct proc *p)
+{
+ struct thread *td;
+
+ KASSERT((p->p_numthreads == 1), ("Muliple threads in wait1()"));
+ KASSERT((p->p_numksegrps == 1), ("Muliple ksegrps in wait1()"));
+ FOREACH_THREAD_IN_PROC(p, td) {
+ if (td->td_standin != NULL) {
+ thread_free(td->td_standin);
+ td->td_standin = NULL;
+ }
+ cpu_thread_clean(td);
+ }
+ thread_reap(); /* check for zombie threads etc. */
+}
+
/*
* Link a thread to a process.
* set up anything that needs to be initialized for it to
@@ -1145,11 +1101,6 @@ thread_link(struct thread *td, struct ksegrp *kg)
TAILQ_INSERT_HEAD(&kg->kg_threads, td, td_kglist);
p->p_numthreads++;
kg->kg_numthreads++;
- if (oiks_debug && (p->p_numthreads > oiks_max_threads_per_proc)) {
- printf("OIKS %d\n", p->p_numthreads);
- if (oiks_debug > 1)
- Debugger("OIKS");
- }
td->td_kse = NULL;
}
@@ -1167,8 +1118,6 @@ kse_purge(struct proc *p, struct thread *td)
kg->kg_idle_kses--;
TAILQ_REMOVE(&kg->kg_kseq, ke, ke_kglist);
kg->kg_kses--;
- if (ke->ke_tdspare)
- thread_stash(ke->ke_tdspare);
kse_stash(ke);
}
TAILQ_REMOVE(&p->p_ksegrps, kg, kg_ksegrp);
@@ -1512,6 +1461,7 @@ thread_userret(struct thread *td, struct trapframe *frame)
TD_SET_LOAN(td);
ke->ke_bound = td;
ke->ke_thread = NULL;
+ p->p_stats->p_ru.ru_nvcsw++;
mi_switch(); /* kse_reassign() will (re)find td2 */
}
mtx_unlock_spin(&sched_lock);
@@ -1522,12 +1472,7 @@ thread_userret(struct thread *td, struct trapframe *frame)
* for when we re-enter the kernel.
*/
if (td->td_standin == NULL) {
- if (ke->ke_tdspare) {
- td->td_standin = ke->ke_tdspare;
- ke->ke_tdspare = NULL;
- } else {
- td->td_standin = thread_alloc();
- }
+ td->td_standin = thread_alloc();
}
thread_update_uticks();
@@ -1550,6 +1495,8 @@ thread_userret(struct thread *td, struct trapframe *frame)
/*
* Set user context to the UTS.
+ * Will use Giant in cpu_thread_clean() because it uses
+ * kmem_free(kernel_map, ...)
*/
cpu_set_upcall_kse(td, ke);
@@ -1619,6 +1566,7 @@ thread_single(int force_exit)
td = curthread;
p = td->td_proc;
+ mtx_assert(&Giant, MA_OWNED);
PROC_LOCK_ASSERT(p, MA_OWNED);
KASSERT((td != NULL), ("curthread is NULL"));
@@ -1677,6 +1625,7 @@ thread_single(int force_exit)
thread_suspend_one(td);
mtx_unlock(&Giant);
PROC_UNLOCK(p);
+ p->p_stats->p_ru.ru_nvcsw++;
mi_switch();
mtx_unlock_spin(&sched_lock);
mtx_lock(&Giant);
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
index 66b3411..3f5b01d 100644
--- a/sys/kern/kern_synch.c
+++ b/sys/kern/kern_synch.c
@@ -540,6 +540,14 @@ mi_switch(void)
*/
if (td->td_switchin)
td->td_switchin();
+
+ /*
+ * If the last thread was exiting, finish cleaning it up.
+ */
+ if ((td = PCPU_GET(deadthread))) {
+ PCPU_SET(deadthread, NULL);
+ thread_stash(td);
+ }
}
/*
diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c
index cf17c08..22f173e 100644
--- a/sys/kern/kern_thread.c
+++ b/sys/kern/kern_thread.c
@@ -66,13 +66,9 @@ static uma_zone_t thread_zone;
/* DEBUG ONLY */
SYSCTL_NODE(_kern, OID_AUTO, threads, CTLFLAG_RW, 0, "thread allocation");
-static int oiks_debug = 0; /* 0 disable, 1 printf, 2 enter debugger */
-SYSCTL_INT(_kern_threads, OID_AUTO, oiks, CTLFLAG_RW,
- &oiks_debug, 0, "OIKS thread debug");
-
-static int oiks_max_threads_per_proc = 10;
-SYSCTL_INT(_kern_threads, OID_AUTO, oiks_max_per_proc, CTLFLAG_RW,
- &oiks_max_threads_per_proc, 0, "Debug limit on threads per proc");
+static int thread_debug = 0;
+SYSCTL_INT(_kern_threads, OID_AUTO, debug, CTLFLAG_RW,
+ &thread_debug, 0, "thread debug");
static int max_threads_per_proc = 30;
SYSCTL_INT(_kern_threads, OID_AUTO, max_threads_per_proc, CTLFLAG_RW,
@@ -91,11 +87,10 @@ struct mtx zombie_thread_lock;
MTX_SYSINIT(zombie_thread_lock, &zombie_thread_lock,
"zombie_thread_lock", MTX_SPIN);
+static void kse_purge(struct proc *p, struct thread *td);
-
-void kse_purge(struct proc *p, struct thread *td);
/*
- * Pepare a thread for use.
+ * Prepare a thread for use.
*/
static void
thread_ctor(void *mem, int size, void *arg)
@@ -115,7 +110,6 @@ thread_dtor(void *mem, int size, void *arg)
{
struct thread *td;
- mtx_assert(&Giant, MA_OWNED);
td = (struct thread *)mem;
#ifdef INVARIANTS
@@ -138,8 +132,6 @@ thread_dtor(void *mem, int size, void *arg)
/* NOTREACHED */
}
#endif
-
- cpu_thread_dtor(td);
}
/*
@@ -346,12 +338,11 @@ kse_exit(struct thread *td, struct kse_exit_args *uap)
return (EDEADLK);
}
if ((p->p_numthreads == 1) && (p->p_numksegrps == 1)) {
+ /* XXXSKE what if >1 KSE? check.... */
p->p_flag &= ~P_KSES;
mtx_unlock_spin(&sched_lock);
PROC_UNLOCK(p);
} else {
- while (mtx_owned(&Giant))
- mtx_unlock(&Giant);
td->td_kse->ke_flags |= KEF_EXIT;
thread_exit();
/* NOTREACHED */
@@ -359,40 +350,50 @@ kse_exit(struct thread *td, struct kse_exit_args *uap)
return (0);
}
+/*
+ * Either returns as an upcall or exits
+ */
int
-kse_release(struct thread *td, struct kse_release_args *uap)
+kse_release(struct thread * td, struct kse_release_args * uap)
{
struct proc *p;
+ struct ksegrp *kg;
p = td->td_proc;
- /* KSE-enabled processes only */
- if (!(p->p_flag & P_KSES))
- return (EINVAL);
+ kg = td->td_ksegrp;
/*
* Must be a bound thread. And kse must have a mailbox ready,
- * if not, the kse would can not generate an upcall.
+ * if not, the kse can not generate an upcall.
*/
- if (!(td->td_flags & TDF_UNBOUND) && (td->td_kse->ke_mailbox != NULL)) {
- PROC_LOCK(p);
- mtx_lock_spin(&sched_lock);
- /* prevent last thread from exiting */
+ if (!(p->p_flag & P_KSES) ||
+ (td->td_flags & TDF_UNBOUND) ||
+ (td->td_kse->ke_mailbox == NULL))
+ return (EINVAL);
+ PROC_LOCK(p);
+ mtx_lock_spin(&sched_lock);
+ if (kg->kg_completed == NULL) {
+#if 1 /* temp until signals make new threads */
if (p->p_numthreads == 1) {
+ /* change OURSELF to become an upcall */
+ td->td_flags = TDF_UPCALLING;
mtx_unlock_spin(&sched_lock);
- if (td->td_standin == NULL) {
- PROC_UNLOCK(p);
- td->td_standin = thread_alloc();
- PROC_LOCK(p);
- }
- msleep(p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH,
- "pause", 0);
- mtx_lock_spin(&sched_lock);
- td->td_flags |= TDF_UNBOUND;
- thread_schedule_upcall(td, td->td_kse);
+ PROC_UNLOCK(p);
+ /*
+ * msleep will not call thread_sched_upcall
+ * because thread is not UNBOUND.
+ */
+ msleep(p->p_sigacts, NULL,
+ PPAUSE | PCATCH, "ksepause", 0);
+ return (0);
}
+#endif /* end temp */
thread_exit();
- /* NOTREACHED */
}
- return (EINVAL);
+ /* change OURSELF to become an upcall */
+ td->td_flags = TDF_UPCALLING;
+ mtx_unlock_spin(&sched_lock);
+ PROC_UNLOCK(p);
+ return (0);
}
/* struct kse_wakeup_args {
@@ -409,8 +410,6 @@ kse_wakeup(struct thread *td, struct kse_wakeup_args *uap)
/* KSE-enabled processes only, please. */
if (!(p->p_flag & P_KSES))
return EINVAL;
- if (td->td_standin == NULL)
- td->td_standin = thread_alloc();
ke = NULL;
mtx_lock_spin(&sched_lock);
if (uap->mbx) {
@@ -507,7 +506,7 @@ kse_create(struct thread *td, struct kse_create_args *uap)
* which is safe.
*/
if ((td->td_flags & TDF_UNBOUND) || td->td_kse->ke_mailbox) {
- if (oiks_debug == 0) {
+ if (thread_debug == 0) { /* if debugging, allow more */
#ifdef SMP
if (kg->kg_kses > mp_ncpus)
#endif
@@ -779,6 +778,8 @@ kse_free(struct kse *td)
void
thread_free(struct thread *td)
{
+
+ cpu_thread_clean(td);
uma_zfree(thread_zone, td);
}
@@ -966,10 +967,9 @@ bad:
* Discard the current thread and exit from its context.
*
* Because we can't free a thread while we're operating under its context,
- * push the current thread into our KSE's ke_tdspare slot, freeing the
- * thread that might be there currently. Because we know that only this
- * processor will run our KSE, we needn't worry about someone else grabbing
- * our context before we do a cpu_throw.
+ * push the current thread into our CPU's deadthread holder. This means
+ * we needn't worry about someone else grabbing our context before we
+ * do a cpu_throw().
*/
void
thread_exit(void)
@@ -992,10 +992,6 @@ thread_exit(void)
CTR1(KTR_PROC, "thread_exit: thread %p", td);
KASSERT(!mtx_owned(&Giant), ("dying thread owns giant"));
- if (ke->ke_tdspare != NULL) {
- thread_stash(ke->ke_tdspare);
- ke->ke_tdspare = NULL;
- }
if (td->td_standin != NULL) {
thread_stash(td->td_standin);
td->td_standin = NULL;
@@ -1039,87 +1035,47 @@ thread_exit(void)
("thread_exit: entered with ke_bound set"));
/*
- * The reason for all this hoopla is
- * an attempt to stop our thread stack from being freed
- * until AFTER we have stopped running on it.
- * Since we are under schedlock, almost any method where
- * it is eventually freed by someone else is probably ok.
- * (Especially if they do it under schedlock). We could
- * almost free it here if we could be certain that
- * the uma code wouldn't pull it apart immediatly,
- * but unfortunatly we can not guarantee that.
- *
- * For threads that are exiting and NOT killing their
- * KSEs we can just stash it in the KSE, however
- * in the case where the KSE is also being deallocated,
- * we need to store it somewhere else. It turns out that
- * we will never free the last KSE, so there is always one
- * other KSE available. We might as well just choose one
- * and stash it there. Being under schedlock should make that
- * safe.
- *
- * In borrower threads, we can stash it in the lender
- * Where it won't be needed until this thread is long gone.
- * Borrower threads can't kill their KSE anyhow, so even
- * the KSE would be a safe place for them. It is not
- * necessary to have a KSE (or KSEGRP) at all beyond this
- * point, while we are under the protection of schedlock.
- *
- * Either give the KSE to another thread to use (or make
- * it idle), or free it entirely, possibly along with its
- * ksegrp if it's the last one.
+ * decide what to do with the KSE attached to this thread.
*/
if (ke->ke_flags & KEF_EXIT) {
kse_unlink(ke);
- /*
- * Designate another KSE to hold our thread.
- * Safe as long as we abide by whatever lock
- * we control it with.. The other KSE will not
- * be able to run it until we release the schelock,
- * but we need to be careful about it deciding to
- * write to the stack before then. Luckily
- * I believe that while another thread's
- * standin thread can be used in this way, the
- * spare thread for the KSE cannot be used without
- * holding schedlock at least once.
- */
- ke = FIRST_KSE_IN_PROC(p);
} else {
kse_reassign(ke);
}
-#if 0
- if (ke->ke_bound) {
- /*
- * WE are a borrower..
- * stash our thread with the owner.
- */
- if (ke->ke_bound->td_standin) {
- thread_stash(ke->ke_bound->td_standin);
- }
- ke->ke_bound->td_standin = td;
- } else {
-#endif
- if (ke->ke_tdspare != NULL) {
- thread_stash(ke->ke_tdspare);
- ke->ke_tdspare = NULL;
- }
- ke->ke_tdspare = td;
-#if 0
- }
-#endif
PROC_UNLOCK(p);
td->td_state = TDS_INACTIVE;
td->td_proc = NULL;
td->td_ksegrp = NULL;
td->td_last_kse = NULL;
+ PCPU_SET(deadthread, td);
} else {
PROC_UNLOCK(p);
}
-
cpu_throw();
/* NOTREACHED */
}
+/*
+ * Do any thread specific cleanups that may be needed in wait()
+ * called with Giant held, proc and schedlock not held.
+ */
+void
+thread_wait(struct proc *p)
+{
+ struct thread *td;
+
+ KASSERT((p->p_numthreads == 1), ("Muliple threads in wait1()"));
+ KASSERT((p->p_numksegrps == 1), ("Muliple ksegrps in wait1()"));
+ FOREACH_THREAD_IN_PROC(p, td) {
+ if (td->td_standin != NULL) {
+ thread_free(td->td_standin);
+ td->td_standin = NULL;
+ }
+ cpu_thread_clean(td);
+ }
+ thread_reap(); /* check for zombie threads etc. */
+}
+
/*
* Link a thread to a process.
* set up anything that needs to be initialized for it to
@@ -1145,11 +1101,6 @@ thread_link(struct thread *td, struct ksegrp *kg)
TAILQ_INSERT_HEAD(&kg->kg_threads, td, td_kglist);
p->p_numthreads++;
kg->kg_numthreads++;
- if (oiks_debug && (p->p_numthreads > oiks_max_threads_per_proc)) {
- printf("OIKS %d\n", p->p_numthreads);
- if (oiks_debug > 1)
- Debugger("OIKS");
- }
td->td_kse = NULL;
}
@@ -1167,8 +1118,6 @@ kse_purge(struct proc *p, struct thread *td)
kg->kg_idle_kses--;
TAILQ_REMOVE(&kg->kg_kseq, ke, ke_kglist);
kg->kg_kses--;
- if (ke->ke_tdspare)
- thread_stash(ke->ke_tdspare);
kse_stash(ke);
}
TAILQ_REMOVE(&p->p_ksegrps, kg, kg_ksegrp);
@@ -1512,6 +1461,7 @@ thread_userret(struct thread *td, struct trapframe *frame)
TD_SET_LOAN(td);
ke->ke_bound = td;
ke->ke_thread = NULL;
+ p->p_stats->p_ru.ru_nvcsw++;
mi_switch(); /* kse_reassign() will (re)find td2 */
}
mtx_unlock_spin(&sched_lock);
@@ -1522,12 +1472,7 @@ thread_userret(struct thread *td, struct trapframe *frame)
* for when we re-enter the kernel.
*/
if (td->td_standin == NULL) {
- if (ke->ke_tdspare) {
- td->td_standin = ke->ke_tdspare;
- ke->ke_tdspare = NULL;
- } else {
- td->td_standin = thread_alloc();
- }
+ td->td_standin = thread_alloc();
}
thread_update_uticks();
@@ -1550,6 +1495,8 @@ thread_userret(struct thread *td, struct trapframe *frame)
/*
* Set user context to the UTS.
+ * Will use Giant in cpu_thread_clean() because it uses
+ * kmem_free(kernel_map, ...)
*/
cpu_set_upcall_kse(td, ke);
@@ -1619,6 +1566,7 @@ thread_single(int force_exit)
td = curthread;
p = td->td_proc;
+ mtx_assert(&Giant, MA_OWNED);
PROC_LOCK_ASSERT(p, MA_OWNED);
KASSERT((td != NULL), ("curthread is NULL"));
@@ -1677,6 +1625,7 @@ thread_single(int force_exit)
thread_suspend_one(td);
mtx_unlock(&Giant);
PROC_UNLOCK(p);
+ p->p_stats->p_ru.ru_nvcsw++;
mi_switch();
mtx_unlock_spin(&sched_lock);
mtx_lock(&Giant);
diff --git a/sys/powerpc/aim/vm_machdep.c b/sys/powerpc/aim/vm_machdep.c
index 1e9d9ba..39501fe 100644
--- a/sys/powerpc/aim/vm_machdep.c
+++ b/sys/powerpc/aim/vm_machdep.c
@@ -354,7 +354,7 @@ cpu_thread_exit(struct thread *td)
}
void
-cpu_thread_dtor(struct thread *td)
+cpu_thread_clean(struct thread *td)
{
}
diff --git a/sys/powerpc/powerpc/vm_machdep.c b/sys/powerpc/powerpc/vm_machdep.c
index 1e9d9ba..39501fe 100644
--- a/sys/powerpc/powerpc/vm_machdep.c
+++ b/sys/powerpc/powerpc/vm_machdep.c
@@ -354,7 +354,7 @@ cpu_thread_exit(struct thread *td)
}
void
-cpu_thread_dtor(struct thread *td)
+cpu_thread_clean(struct thread *td)
{
}
diff --git a/sys/sparc64/sparc64/vm_machdep.c b/sys/sparc64/sparc64/vm_machdep.c
index 65bb563..6549cca 100644
--- a/sys/sparc64/sparc64/vm_machdep.c
+++ b/sys/sparc64/sparc64/vm_machdep.c
@@ -114,7 +114,7 @@ cpu_thread_exit(struct thread *td)
}
void
-cpu_thread_dtor(struct thread *td)
+cpu_thread_clean(struct thread *td)
{
}
diff --git a/sys/sys/pcpu.h b/sys/sys/pcpu.h
index 84e89efc..1bc275e 100644
--- a/sys/sys/pcpu.h
+++ b/sys/sys/pcpu.h
@@ -58,6 +58,7 @@ struct pcpu {
struct thread *pc_curthread; /* Current thread */
struct thread *pc_idlethread; /* Idle thread */
struct thread *pc_fpcurthread; /* Fp state owner */
+ struct thread *pc_deadthread; /* Zombie thread or NULL */
struct pcb *pc_curpcb; /* Current pcb */
struct bintime pc_switchtime;
int pc_switchticks;
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 04db3cd..5b2d22d 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -905,7 +905,7 @@ void kse_free(struct kse *ke);
void kse_stash(struct kse *ke);
void cpu_set_upcall(struct thread *td, void *pcb);
void cpu_set_upcall_kse(struct thread *td, struct kse *ke);
-void cpu_thread_dtor(struct thread *);
+void cpu_thread_clean(struct thread *);
void cpu_thread_exit(struct thread *);
void cpu_thread_setup(struct thread *td);
void kse_reassign(struct kse *ke);
@@ -935,6 +935,7 @@ void thread_unsuspend(struct proc *p);
void thread_unsuspend_one(struct thread *td);
int thread_userret(struct thread *td, struct trapframe *frame);
void thread_user_enter(struct proc *p, struct thread *td);
+void thread_wait(struct proc *p);
int thread_add_ticks_intr(int user, uint ticks);
void thread_sanity_check(struct thread *td, char *);
OpenPOWER on IntegriCloud