summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_kse.c
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>2003-02-01 12:17:09 +0000
committerjulian <julian@FreeBSD.org>2003-02-01 12:17:09 +0000
commite8efa7328e487806fb77d3ec54bf5fa5f8b017ed (patch)
tree5ab8f2c0a0aaeb3da3779201a31e710dee48d388 /sys/kern/kern_kse.c
parent1c5753d03f1205c6e2831f320c4007946a4b050f (diff)
downloadFreeBSD-src-e8efa7328e487806fb77d3ec54bf5fa5f8b017ed.zip
FreeBSD-src-e8efa7328e487806fb77d3ec54bf5fa5f8b017ed.tar.gz
Reversion of commit by Davidxu plus fixes since applied.
I'm not convinced there is anything major wrong with the patch but them's the rules.. I am using my "David's mentor" hat to revert this as he's offline for a while.
Diffstat (limited to 'sys/kern/kern_kse.c')
-rw-r--r--sys/kern/kern_kse.c1163
1 files changed, 515 insertions, 648 deletions
diff --git a/sys/kern/kern_kse.c b/sys/kern/kern_kse.c
index 0585172..78bce30 100644
--- a/sys/kern/kern_kse.c
+++ b/sys/kern/kern_kse.c
@@ -63,7 +63,6 @@
static uma_zone_t ksegrp_zone;
static uma_zone_t kse_zone;
static uma_zone_t thread_zone;
-static uma_zone_t upcall_zone;
/* DEBUG ONLY */
SYSCTL_NODE(_kern, OID_AUTO, threads, CTLFLAG_RW, 0, "thread allocation");
@@ -79,52 +78,16 @@ static int max_groups_per_proc = 5;
SYSCTL_INT(_kern_threads, OID_AUTO, max_groups_per_proc, CTLFLAG_RW,
&max_groups_per_proc, 0, "Limit on thread groups per proc");
-static int virtual_cpu;
-
#define RANGEOF(type, start, end) (offsetof(type, end) - offsetof(type, start))
-TAILQ_HEAD(, thread) zombie_threads = TAILQ_HEAD_INITIALIZER(zombie_threads);
+struct threadqueue zombie_threads = TAILQ_HEAD_INITIALIZER(zombie_threads);
TAILQ_HEAD(, kse) zombie_kses = TAILQ_HEAD_INITIALIZER(zombie_kses);
TAILQ_HEAD(, ksegrp) zombie_ksegrps = TAILQ_HEAD_INITIALIZER(zombie_ksegrps);
-TAILQ_HEAD(, kse_upcall) zombie_upcalls =
- TAILQ_HEAD_INITIALIZER(zombie_upcalls);
-struct mtx kse_zombie_lock;
-MTX_SYSINIT(kse_zombie_lock, &kse_zombie_lock, "kse zombie lock", MTX_SPIN);
+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);
-static void kse_purge_group(struct thread *td);
-static int thread_update_usr_ticks(struct thread *td);
-static int thread_update_sys_ticks(struct thread *td);
-static void thread_alloc_spare(struct thread *td, struct thread *spare);
-
-static int
-sysctl_kse_virtual_cpu(SYSCTL_HANDLER_ARGS)
-{
- int error, new_val;
- int def_val;
-
-#ifdef SMP
- def_val = mp_ncpus;
-#else
- def_val = 1;
-#endif
- if (virtual_cpu == 0)
- new_val = def_val;
- else
- new_val = virtual_cpu;
- error = sysctl_handle_int(oidp, &new_val, 0, req);
- if (error != 0 || req->newptr == NULL)
- return (error);
- if (new_val < 0)
- return (EINVAL);
- virtual_cpu = new_val;
- return (0);
-}
-
-/* DEBUG ONLY */
-SYSCTL_PROC(_kern_threads, OID_AUTO, virtual_cpu, CTLTYPE_INT|CTLFLAG_RW,
- 0, sizeof(virtual_cpu), sysctl_kse_virtual_cpu, "I",
- "debug virtual cpus");
/*
* Prepare a thread for use.
@@ -136,6 +99,7 @@ thread_ctor(void *mem, int size, void *arg)
td = (struct thread *)mem;
td->td_state = TDS_INACTIVE;
+ td->td_flags |= TDF_UNBOUND;
}
/*
@@ -197,7 +161,6 @@ thread_fini(void *mem, int size)
td = (struct thread *)mem;
pmap_dispose_thread(td);
}
-
/*
* Initialize type-stable parts of a kse (when newly created).
*/
@@ -209,7 +172,6 @@ kse_init(void *mem, int size)
ke = (struct kse *)mem;
ke->ke_sched = (struct ke_sched *)&ke[1];
}
-
/*
* Initialize type-stable parts of a ksegrp (when newly created).
*/
@@ -223,7 +185,7 @@ ksegrp_init(void *mem, int size)
}
/*
- * KSE is linked into kse group.
+ * KSE is linked onto the idle queue.
*/
void
kse_link(struct kse *ke, struct ksegrp *kg)
@@ -232,12 +194,12 @@ kse_link(struct kse *ke, struct ksegrp *kg)
TAILQ_INSERT_HEAD(&kg->kg_kseq, ke, ke_kglist);
kg->kg_kses++;
- ke->ke_state = KES_UNQUEUED;
+ ke->ke_state = KES_UNQUEUED;
ke->ke_proc = p;
ke->ke_ksegrp = kg;
+ ke->ke_owner = NULL;
ke->ke_thread = NULL;
- ke->ke_oncpu = NOCPU;
- ke->ke_flags = 0;
+ ke->ke_oncpu = NOCPU;
}
void
@@ -247,13 +209,11 @@ kse_unlink(struct kse *ke)
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--;
+ if (--kg->kg_kses == 0) {
+ ksegrp_unlink(kg);
}
- if (--kg->kg_kses == 0)
- ksegrp_unlink(kg);
/*
* Aggregate stats from the KSE
*/
@@ -268,20 +228,15 @@ ksegrp_link(struct ksegrp *kg, struct proc *p)
TAILQ_INIT(&kg->kg_runq); /* links with td_runq */
TAILQ_INIT(&kg->kg_slpq); /* links with td_runq */
TAILQ_INIT(&kg->kg_kseq); /* all kses in ksegrp */
- TAILQ_INIT(&kg->kg_iq); /* all idle kses in ksegrp */
- TAILQ_INIT(&kg->kg_upcalls); /* all upcall structure in ksegrp */
- kg->kg_proc = p;
- /*
- * the following counters are in the -zero- section
- * and may not need clearing
- */
+ TAILQ_INIT(&kg->kg_lq); /* loan kses in ksegrp */
+ kg->kg_proc = p;
+/* the following counters are in the -zero- section and may not need clearing */
kg->kg_numthreads = 0;
- kg->kg_runnable = 0;
- kg->kg_kses = 0;
- kg->kg_runq_kses = 0; /* XXXKSE change name */
- kg->kg_idle_kses = 0;
- kg->kg_numupcalls = 0;
- /* link it in now that it's consistent */
+ kg->kg_runnable = 0;
+ kg->kg_kses = 0;
+ kg->kg_loan_kses = 0;
+ kg->kg_runq_kses = 0; /* XXXKSE change name */
+/* link it in now that it's consistent */
p->p_numksegrps++;
TAILQ_INSERT_HEAD(&p->p_ksegrps, kg, kg_ksegrp);
}
@@ -292,11 +247,9 @@ ksegrp_unlink(struct ksegrp *kg)
struct proc *p;
mtx_assert(&sched_lock, MA_OWNED);
- KASSERT((kg->kg_numthreads == 0), ("ksegrp_unlink: residual threads"));
- KASSERT((kg->kg_kses == 0), ("ksegrp_unlink: residual kses"));
- KASSERT((kg->kg_numupcalls == 0), ("ksegrp_unlink: residual upcalls"));
-
p = kg->kg_proc;
+ KASSERT(((kg->kg_numthreads == 0) && (kg->kg_kses == 0)),
+ ("kseg_unlink: residual threads or KSEs"));
TAILQ_REMOVE(&p->p_ksegrps, kg, kg_ksegrp);
p->p_numksegrps--;
/*
@@ -305,63 +258,13 @@ ksegrp_unlink(struct ksegrp *kg)
ksegrp_stash(kg);
}
-struct kse_upcall *
-upcall_alloc(void)
-{
- struct kse_upcall *ku;
-
- ku = uma_zalloc(upcall_zone, 0);
- bzero(ku, sizeof(*ku));
- return (ku);
-}
-
-void
-upcall_free(struct kse_upcall *ku)
-{
-
- uma_zfree(upcall_zone, ku);
-}
-
-void
-upcall_link(struct kse_upcall *ku, struct ksegrp *kg)
-{
-
- mtx_assert(&sched_lock, MA_OWNED);
- TAILQ_INSERT_TAIL(&kg->kg_upcalls, ku, ku_link);
- ku->ku_ksegrp = kg;
- kg->kg_numupcalls++;
-}
-
-void
-upcall_unlink(struct kse_upcall *ku)
-{
- struct ksegrp *kg = ku->ku_ksegrp;
-
- mtx_assert(&sched_lock, MA_OWNED);
- KASSERT(ku->ku_owner == NULL, ("%s: have owner", __func__));
- TAILQ_REMOVE(&kg->kg_upcalls, ku, ku_link);
- kg->kg_numupcalls--;
- upcall_stash(ku);
-}
-
-void
-upcall_remove(struct thread *td)
-{
-
- if (td->td_upcall) {
- td->td_upcall->ku_owner = NULL;
- upcall_unlink(td->td_upcall);
- td->td_upcall = 0;
- }
-}
-
/*
- * For a newly created process,
- * link up all the structures and its initial threads etc.
+ * for a newly created process,
+ * link up a the structure and its initial threads etc.
*/
void
proc_linkup(struct proc *p, struct ksegrp *kg,
- struct kse *ke, struct thread *td)
+ struct kse *ke, struct thread *td)
{
TAILQ_INIT(&p->p_ksegrps); /* all ksegrps in proc */
@@ -375,11 +278,6 @@ proc_linkup(struct proc *p, struct ksegrp *kg,
thread_link(td, kg);
}
-/*
-struct kse_thr_interrupt_args {
- struct kse_thr_mailbox * tmbx;
-};
-*/
int
kse_thr_interrupt(struct thread *td, struct kse_thr_interrupt_args *uap)
{
@@ -387,7 +285,10 @@ kse_thr_interrupt(struct thread *td, struct kse_thr_interrupt_args *uap)
struct thread *td2;
p = td->td_proc;
- if (!(p->p_flag & P_KSES) || (uap->tmbx == NULL))
+ /* KSE-enabled processes only, please. */
+ if (!(p->p_flag & P_KSES))
+ return (EINVAL);
+ if (uap->tmbx == NULL)
return (EINVAL);
mtx_lock_spin(&sched_lock);
FOREACH_THREAD_IN_PROC(p, td2) {
@@ -398,7 +299,7 @@ kse_thr_interrupt(struct thread *td, struct kse_thr_interrupt_args *uap)
cv_abort(td2);
else
abortsleep(td2);
- }
+ }
mtx_unlock_spin(&sched_lock);
return (0);
}
@@ -407,11 +308,6 @@ kse_thr_interrupt(struct thread *td, struct kse_thr_interrupt_args *uap)
return (ESRCH);
}
-/*
-struct kse_exit_args {
- register_t dummy;
-};
-*/
int
kse_exit(struct thread *td, struct kse_exit_args *uap)
{
@@ -420,35 +316,27 @@ kse_exit(struct thread *td, struct kse_exit_args *uap)
struct kse *ke;
p = td->td_proc;
- /*
- * Only UTS can call the syscall and current group
- * should be a threaded group.
- */
- if ((td->td_mailbox != NULL) || (td->td_ksegrp->kg_numupcalls == 0))
+ /* Only UTS can do the syscall */
+ if (!(p->p_flag & P_KSES) || (td->td_mailbox != NULL))
return (EINVAL);
- KASSERT((td->td_upcall != NULL), ("%s: not own an upcall", __func__));
-
kg = td->td_ksegrp;
- /* Serialize removing upcall */
+ /* serialize killing kse */
PROC_LOCK(p);
mtx_lock_spin(&sched_lock);
- if ((kg->kg_numupcalls == 1) && (kg->kg_numthreads > 1)) {
+ if ((kg->kg_kses == 1) && (kg->kg_numthreads > 1)) {
mtx_unlock_spin(&sched_lock);
PROC_UNLOCK(p);
return (EDEADLK);
}
ke = td->td_kse;
- upcall_remove(td);
if (p->p_numthreads == 1) {
- kse_purge(p, td);
+ ke->ke_flags &= ~KEF_DOUPCALL;
+ ke->ke_mailbox = NULL;
p->p_flag &= ~P_KSES;
mtx_unlock_spin(&sched_lock);
PROC_UNLOCK(p);
} else {
- if (kg->kg_numthreads == 1) { /* Shutdown a group */
- kse_purge_group(td);
- ke->ke_flags |= KEF_EXIT;
- }
+ ke->ke_flags |= KEF_EXIT;
thread_exit();
/* NOTREACHED */
}
@@ -457,15 +345,10 @@ kse_exit(struct thread *td, struct kse_exit_args *uap)
/*
* Either becomes an upcall or waits for an awakening event and
- * then becomes an upcall. Only error cases return.
+ * THEN becomes an upcall. Only error cases return.
*/
-/*
-struct kse_release_args {
- register_t dummy;
-};
-*/
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;
@@ -473,25 +356,28 @@ kse_release(struct thread *td, struct kse_release_args *uap)
p = td->td_proc;
kg = td->td_ksegrp;
/*
- * Only UTS can call the syscall and current group
- * should be a threaded group.
- */
- if ((td->td_mailbox != NULL) || (td->td_ksegrp->kg_numupcalls == 0))
+ * kse must have a mailbox ready for upcall, and only UTS can
+ * do the syscall.
+ */
+ if (!(p->p_flag & P_KSES) ||
+ (td->td_mailbox != NULL) ||
+ (td->td_kse->ke_mailbox == NULL))
return (EINVAL);
- KASSERT((td->td_upcall != NULL), ("%s: not own an upcall", __func__));
PROC_LOCK(p);
mtx_lock_spin(&sched_lock);
/* Change OURSELF to become an upcall. */
- td->td_flags = TDF_UPCALLING;
- if ((td->td_upcall->ku_flags & KUF_DOUPCALL) == 0 &&
+ td->td_flags = TDF_UPCALLING; /* BOUND */
+ if (!(td->td_kse->ke_flags & (KEF_DOUPCALL|KEF_ASTPENDING)) &&
(kg->kg_completed == NULL)) {
- kg->kg_upsleeps++;
- mtx_unlock_spin(&sched_lock);
- msleep(&kg->kg_completed, &p->p_mtx, PPAUSE|PCATCH, "ksepause",
- NULL);
- kg->kg_upsleeps--;
+ /*
+ * The KSE will however be lendable.
+ */
+ TD_SET_IDLE(td);
PROC_UNLOCK(p);
+ p->p_stats->p_ru.ru_nvcsw++;
+ mi_switch();
+ mtx_unlock_spin(&sched_lock);
} else {
mtx_unlock_spin(&sched_lock);
PROC_UNLOCK(p);
@@ -506,59 +392,61 @@ int
kse_wakeup(struct thread *td, struct kse_wakeup_args *uap)
{
struct proc *p;
+ struct kse *ke;
struct ksegrp *kg;
- struct kse_upcall *ku;
struct thread *td2;
p = td->td_proc;
td2 = NULL;
- ku = NULL;
/* KSE-enabled processes only, please. */
if (!(p->p_flag & P_KSES))
- return (EINVAL);
+ return EINVAL;
- PROC_LOCK(p);
mtx_lock_spin(&sched_lock);
if (uap->mbx) {
FOREACH_KSEGRP_IN_PROC(p, kg) {
- FOREACH_UPCALL_IN_GROUP(kg, ku) {
- if (ku->ku_mailbox == uap->mbx)
- break;
+ FOREACH_KSE_IN_GROUP(kg, ke) {
+ if (ke->ke_mailbox != uap->mbx)
+ continue;
+ td2 = ke->ke_owner;
+ KASSERT((td2 != NULL),("KSE with no owner"));
+ break;
}
- if (ku)
+ if (td2) {
break;
+ }
}
} else {
+ /*
+ * look for any idle KSE to resurrect.
+ */
kg = td->td_ksegrp;
- if (kg->kg_upsleeps) {
- wakeup_one(&kg->kg_completed);
- mtx_unlock_spin(&sched_lock);
- PROC_UNLOCK(p);
- return (0);
+ FOREACH_KSE_IN_GROUP(kg, ke) {
+ td2 = ke->ke_owner;
+ KASSERT((td2 != NULL),("KSE with no owner2"));
+ if (TD_IS_IDLE(td2))
+ break;
}
- ku = TAILQ_FIRST(&kg->kg_upcalls);
+ KASSERT((td2 != NULL), ("no thread(s)"));
}
- if (ku) {
- if ((td2 = ku->ku_owner) == NULL) {
- panic("%s: no owner", __func__);
- } else if (TD_ON_SLEEPQ(td2) &&
- (td2->td_wchan == &kg->kg_completed)) {
- abortsleep(td2);
- } else {
- ku->ku_flags |= KUF_DOUPCALL;
+ if (td2) {
+ if (TD_IS_IDLE(td2)) {
+ TD_CLR_IDLE(td2);
+ setrunnable(td2);
+ } else if (td != td2) {
+ /* guarantee do an upcall ASAP */
+ td2->td_kse->ke_flags |= KEF_DOUPCALL;
}
mtx_unlock_spin(&sched_lock);
- PROC_UNLOCK(p);
return (0);
}
mtx_unlock_spin(&sched_lock);
- PROC_UNLOCK(p);
return (ESRCH);
}
/*
* 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.
+ * All other situations, do allocate a new KSE and schedule an upcall on it.
*/
/* struct kse_create_args {
struct kse_mailbox *mbx;
@@ -568,140 +456,112 @@ int
kse_create(struct thread *td, struct kse_create_args *uap)
{
struct kse *newke;
+ struct kse *ke;
struct ksegrp *newkg;
struct ksegrp *kg;
struct proc *p;
struct kse_mailbox mbx;
- struct kse_upcall *newku;
- int err, ncpus;
+ int err;
p = td->td_proc;
if ((err = copyin(uap->mbx, &mbx, sizeof(mbx))))
return (err);
- /* Too bad, why hasn't kernel always a cpu counter !? */
-#ifdef SMP
- ncpus = mp_ncpus;
-#else
- ncpus = 1;
-#endif
- if (thread_debug && virtual_cpu != 0)
- ncpus = virtual_cpu;
-
- /* Easier to just set it than to test and set */
- p->p_flag |= P_KSES;
+ p->p_flag |= P_KSES; /* easier to just set it than to test and set */
kg = td->td_ksegrp;
if (uap->newgroup) {
- /* Have race condition but it is cheap */
if (p->p_numksegrps >= max_groups_per_proc)
return (EPROCLIM);
/*
* If we want a new KSEGRP it doesn't matter whether
* we have already fired up KSE mode before or not.
- * We put the process in KSE mode and create a new KSEGRP.
+ * We put the process in KSE mode and create a new KSEGRP
+ * and KSE. If our KSE has not got a mailbox yet then
+ * that doesn't matter, just leave it that way. It will
+ * ensure that this thread stay BOUND. It's possible
+ * that the call came form a threaded library and the main
+ * program knows nothing of threads.
*/
newkg = ksegrp_alloc();
bzero(&newkg->kg_startzero, RANGEOF(struct ksegrp,
- kg_startzero, kg_endzero));
+ kg_startzero, kg_endzero));
bcopy(&kg->kg_startcopy, &newkg->kg_startcopy,
RANGEOF(struct ksegrp, kg_startcopy, kg_endcopy));
- mtx_lock_spin(&sched_lock);
- ksegrp_link(newkg, p);
- if (p->p_numksegrps >= max_groups_per_proc) {
- ksegrp_unlink(newkg);
- mtx_unlock_spin(&sched_lock);
- return (EPROCLIM);
- }
- mtx_unlock_spin(&sched_lock);
+ newke = kse_alloc();
} else {
- newkg = kg;
- }
-
- /*
- * Creating upcalls more than number of physical cpu does
- * not help performance.
- */
- if (newkg->kg_numupcalls >= ncpus)
- return (EPROCLIM);
-
- if (newkg->kg_numupcalls == 0) {
- /*
- * Initialize KSE group, optimized for MP.
- * Create KSEs as many as physical cpus, this increases
- * concurrent even if userland is not MP safe and can only run
- * on single CPU (for early version of libpthread, it is true).
- * 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.
- * Highest performance configuration is:
- * N kses = N upcalls = N phyiscal cpus
+ /*
+ * Otherwise, if we have already set this KSE
+ * to have a mailbox, we want to make another KSE here,
+ * but only if there are not already the limit, which
+ * is 1 per CPU max.
+ *
+ * If the current KSE doesn't have a mailbox we just use it
+ * and give it one.
+ *
+ * Because we don't like to access
+ * the KSE outside of schedlock if we are UNBOUND,
+ * (because it can change if we are preempted by an interrupt)
+ * we can deduce it as having a mailbox if we are UNBOUND,
+ * and only need to actually look at it if we are BOUND,
+ * which is safe.
*/
- while (newkg->kg_kses < ncpus) {
+ if ((td->td_flags & TDF_UNBOUND) || td->td_kse->ke_mailbox) {
+ if (thread_debug == 0) { /* if debugging, allow more */
+#ifdef SMP
+ if (kg->kg_kses > mp_ncpus)
+#endif
+ return (EPROCLIM);
+ }
newke = kse_alloc();
- bzero(&newke->ke_startzero, RANGEOF(struct kse,
- ke_startzero, ke_endzero));
+ } else {
+ newke = NULL;
+ }
+ newkg = NULL;
+ }
+ if (newke) {
+ bzero(&newke->ke_startzero, RANGEOF(struct kse,
+ ke_startzero, ke_endzero));
#if 0
- mtx_lock_spin(&sched_lock);
- bcopy(&ke->ke_startcopy, &newke->ke_startcopy,
- RANGEOF(struct kse, ke_startcopy, ke_endcopy));
- mtx_unlock_spin(&sched_lock);
+ bcopy(&ke->ke_startcopy, &newke->ke_startcopy,
+ RANGEOF(struct kse, ke_startcopy, ke_endcopy));
#endif
- mtx_lock_spin(&sched_lock);
- kse_link(newke, newkg);
- if (p->p_sflag & PS_NEEDSIGCHK)
- newke->ke_flags |= KEF_ASTPENDING;
- /* Add engine */
- kse_reassign(newke);
- mtx_unlock_spin(&sched_lock);
+ /* For the first call this may not have been set */
+ if (td->td_standin == NULL) {
+ td->td_standin = thread_alloc();
}
- }
- newku = upcall_alloc();
- newku->ku_mailbox = uap->mbx;
- newku->ku_func = mbx.km_func;
- bcopy(&mbx.km_stack, &newku->ku_stack, sizeof(stack_t));
-
- /* For the first call this may not have been set */
- if (td->td_standin == NULL)
- thread_alloc_spare(td, NULL);
-
- mtx_lock_spin(&sched_lock);
- if (newkg->kg_numupcalls >= ncpus) {
- upcall_free(newku);
+ mtx_lock_spin(&sched_lock);
+ if (newkg) {
+ if (p->p_numksegrps >= max_groups_per_proc) {
+ mtx_unlock_spin(&sched_lock);
+ ksegrp_free(newkg);
+ kse_free(newke);
+ return (EPROCLIM);
+ }
+ ksegrp_link(newkg, p);
+ }
+ else
+ newkg = kg;
+ kse_link(newke, newkg);
+ if (p->p_sflag & PS_NEEDSIGCHK)
+ newke->ke_flags |= KEF_ASTPENDING;
+ newke->ke_mailbox = uap->mbx;
+ newke->ke_upcall = mbx.km_func;
+ bcopy(&mbx.km_stack, &newke->ke_stack, sizeof(stack_t));
+ thread_schedule_upcall(td, newke);
mtx_unlock_spin(&sched_lock);
- return (EPROCLIM);
- }
- upcall_link(newku, newkg);
-
- /*
- * Each upcall structure has an owner thread, find which
- * one owns it.
- */
- if (uap->newgroup) {
- /*
- * Because new ksegrp hasn't thread,
- * create an initial upcall thread to own it.
- */
- thread_schedule_upcall(td, newku);
} else {
/*
- * If current thread hasn't an upcall structure,
- * just assign the upcall to it.
+ * If we didn't allocate a new KSE then the we are using
+ * the exisiting (BOUND) kse.
*/
- if (td->td_upcall == NULL) {
- newku->ku_owner = td;
- td->td_upcall = newku;
- } else {
- /*
- * Create a new upcall thread to own it.
- */
- thread_schedule_upcall(td, newku);
- }
+ ke = td->td_kse;
+ ke->ke_mailbox = uap->mbx;
+ ke->ke_upcall = mbx.km_func;
+ bcopy(&mbx.km_stack, &ke->ke_stack, sizeof(stack_t));
}
- mtx_unlock_spin(&sched_lock);
+ /*
+ * Fill out the KSE-mode specific fields of the new kse.
+ */
return (0);
}
@@ -782,8 +642,6 @@ threadinit(void)
kse_zone = uma_zcreate("KSE", sched_sizeof_kse(),
NULL, NULL, kse_init, NULL,
UMA_ALIGN_CACHE, 0);
- upcall_zone = uma_zcreate("UPCALL", sizeof(struct kse_upcall),
- NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
}
/*
@@ -792,9 +650,9 @@ threadinit(void)
void
thread_stash(struct thread *td)
{
- mtx_lock_spin(&kse_zombie_lock);
+ mtx_lock_spin(&zombie_thread_lock);
TAILQ_INSERT_HEAD(&zombie_threads, td, td_runq);
- mtx_unlock_spin(&kse_zombie_lock);
+ mtx_unlock_spin(&zombie_thread_lock);
}
/*
@@ -803,21 +661,9 @@ thread_stash(struct thread *td)
void
kse_stash(struct kse *ke)
{
- mtx_lock_spin(&kse_zombie_lock);
+ mtx_lock_spin(&zombie_thread_lock);
TAILQ_INSERT_HEAD(&zombie_kses, ke, ke_procq);
- mtx_unlock_spin(&kse_zombie_lock);
-}
-
-/*
- * Stash an embarasingly extra upcall into the zombie upcall queue.
- */
-
-void
-upcall_stash(struct kse_upcall *ku)
-{
- mtx_lock_spin(&kse_zombie_lock);
- TAILQ_INSERT_HEAD(&zombie_upcalls, ku, ku_link);
- mtx_unlock_spin(&kse_zombie_lock);
+ mtx_unlock_spin(&zombie_thread_lock);
}
/*
@@ -826,13 +672,13 @@ upcall_stash(struct kse_upcall *ku)
void
ksegrp_stash(struct ksegrp *kg)
{
- mtx_lock_spin(&kse_zombie_lock);
+ mtx_lock_spin(&zombie_thread_lock);
TAILQ_INSERT_HEAD(&zombie_ksegrps, kg, kg_ksegrp);
- mtx_unlock_spin(&kse_zombie_lock);
+ mtx_unlock_spin(&zombie_thread_lock);
}
/*
- * Reap zombie kse resource.
+ * Reap zombie threads.
*/
void
thread_reap(void)
@@ -840,34 +686,27 @@ thread_reap(void)
struct thread *td_first, *td_next;
struct kse *ke_first, *ke_next;
struct ksegrp *kg_first, * kg_next;
- struct kse_upcall *ku_first, *ku_next;
/*
- * Don't even bother to lock if none at this instant,
- * we really don't care about the next instant..
+ * don't even bother to lock if none at this instant
+ * We really don't care about the next instant..
*/
if ((!TAILQ_EMPTY(&zombie_threads))
|| (!TAILQ_EMPTY(&zombie_kses))
- || (!TAILQ_EMPTY(&zombie_ksegrps))
- || (!TAILQ_EMPTY(&zombie_upcalls))) {
- mtx_lock_spin(&kse_zombie_lock);
+ || (!TAILQ_EMPTY(&zombie_ksegrps))) {
+ mtx_lock_spin(&zombie_thread_lock);
td_first = TAILQ_FIRST(&zombie_threads);
ke_first = TAILQ_FIRST(&zombie_kses);
kg_first = TAILQ_FIRST(&zombie_ksegrps);
- ku_first = TAILQ_FIRST(&zombie_upcalls);
if (td_first)
TAILQ_INIT(&zombie_threads);
if (ke_first)
TAILQ_INIT(&zombie_kses);
if (kg_first)
TAILQ_INIT(&zombie_ksegrps);
- if (ku_first)
- TAILQ_INIT(&zombie_upcalls);
- mtx_unlock_spin(&kse_zombie_lock);
+ mtx_unlock_spin(&zombie_thread_lock);
while (td_first) {
td_next = TAILQ_NEXT(td_first, td_runq);
- if (td_first->td_ucred)
- crfree(td_first->td_ucred);
thread_free(td_first);
td_first = td_next;
}
@@ -881,11 +720,6 @@ thread_reap(void)
ksegrp_free(kg_first);
kg_first = kg_next;
}
- while (ku_first) {
- ku_next = TAILQ_NEXT(ku_first, ku_link);
- upcall_free(ku_first);
- ku_first = ku_next;
- }
}
}
@@ -958,14 +792,20 @@ thread_export_context(struct thread *td)
struct ksegrp *kg;
uintptr_t mbx;
void *addr;
- int error,temp;
+ int error;
ucontext_t uc;
+ uint temp;
p = td->td_proc;
kg = td->td_ksegrp;
/* Export the user/machine context. */
- addr = (void *)(&td->td_mailbox->tm_context);
+#if 0
+ addr = (caddr_t)td->td_mailbox +
+ offsetof(struct kse_thr_mailbox, tm_context);
+#else /* if user pointer arithmetic is valid in the kernel */
+ addr = (void *)(&td->td_mailbox->tm_context);
+#endif
error = copyin(addr, &uc, sizeof(ucontext_t));
if (error)
goto bad;
@@ -975,14 +815,13 @@ thread_export_context(struct thread *td)
if (error)
goto bad;
- /* Exports clock ticks in kernel mode */
- addr = (caddr_t)(&td->td_mailbox->tm_sticks);
- temp = fuword(addr) + td->td_usticks;
- if (suword(addr, temp))
- goto bad;
-
- /* Get address in latest mbox of list pointer */
+ /* get address in latest mbox of list pointer */
+#if 0
+ addr = (caddr_t)td->td_mailbox
+ + offsetof(struct kse_thr_mailbox , tm_next);
+#else /* if user pointer arithmetic is valid in the kernel */
addr = (void *)(&td->td_mailbox->tm_next);
+#endif
/*
* Put the saved address of the previous first
* entry into this one
@@ -996,43 +835,42 @@ thread_export_context(struct thread *td)
PROC_LOCK(p);
if (mbx == (uintptr_t)kg->kg_completed) {
kg->kg_completed = td->td_mailbox;
- /*
- * The thread context may be taken away by
- * other upcall threads when we unlock
- * process lock. it's no longer valid to
- * use it again in any other places.
- */
- td->td_mailbox = NULL;
PROC_UNLOCK(p);
break;
}
PROC_UNLOCK(p);
}
- td->td_usticks = 0;
+ addr = (caddr_t)td->td_mailbox
+ + offsetof(struct kse_thr_mailbox, tm_sticks);
+ temp = fuword(addr) + td->td_usticks;
+ if (suword(addr, temp))
+ goto bad;
return (0);
bad:
PROC_LOCK(p);
psignal(p, SIGSEGV);
PROC_UNLOCK(p);
- /* The mailbox is bad, don't use it */
- td->td_mailbox = NULL;
- td->td_usticks = 0;
return (error);
}
/*
* Take the list of completed mailboxes for this KSEGRP and put them on this
- * upcall's mailbox as it's the next one going up.
+ * KSE's mailbox as it's the next one going up.
*/
static int
-thread_link_mboxes(struct ksegrp *kg, struct kse_upcall *ku)
+thread_link_mboxes(struct ksegrp *kg, struct kse *ke)
{
struct proc *p = kg->kg_proc;
void *addr;
uintptr_t mbx;
- addr = (void *)(&ku->ku_mailbox->km_completed);
+#if 0
+ addr = (caddr_t)ke->ke_mailbox
+ + offsetof(struct kse_mailbox, km_completed);
+#else /* if user pointer arithmetic is valid in the kernel */
+ addr = (void *)(&ke->ke_mailbox->km_completed);
+#endif
for (;;) {
mbx = (uintptr_t)kg->kg_completed;
if (suword(addr, mbx)) {
@@ -1057,91 +895,69 @@ thread_link_mboxes(struct ksegrp *kg, struct kse_upcall *ku)
* This function should be called at statclock interrupt time
*/
int
-thread_statclock(int user)
+thread_add_ticks_intr(int user, uint ticks)
{
struct thread *td = curthread;
+ struct kse *ke = td->td_kse;
- if (td->td_ksegrp->kg_numupcalls == 0)
- return (-1);
+ if (ke->ke_mailbox == NULL)
+ return -1;
if (user) {
/* Current always do via ast() */
- td->td_flags |= (TDF_ASTPENDING|TDF_USTATCLOCK);
- td->td_uuticks++;
+ ke->ke_flags |= KEF_ASTPENDING;
+ ke->ke_uuticks += ticks;
} else {
if (td->td_mailbox != NULL)
- td->td_usticks++;
- else {
- /* XXXKSE
- * We will call thread_user_enter() for every
- * kernel entry in future, so if the thread mailbox
- * is NULL, it must be a UTS kernel, don't account
- * clock ticks for it.
- */
- }
+ td->td_usticks += ticks;
+ else
+ ke->ke_usticks += ticks;
}
- return (0);
+ return 0;
}
-/*
- * Export user mode state clock ticks
- */
static int
-thread_update_usr_ticks(struct thread *td)
+thread_update_uticks(void)
{
+ struct thread *td = curthread;
struct proc *p = td->td_proc;
+ struct kse *ke = td->td_kse;
struct kse_thr_mailbox *tmbx;
- struct kse_upcall *ku;
caddr_t addr;
- uint uticks;
+ uint uticks, sticks;
- if ((ku = td->td_upcall) == NULL)
- return (-1);
-
- tmbx = (void *)fuword((void *)&ku->ku_mailbox->km_curthread);
+ if (ke->ke_mailbox == NULL)
+ return 0;
+
+ uticks = ke->ke_uuticks;
+ ke->ke_uuticks = 0;
+ sticks = ke->ke_usticks;
+ ke->ke_usticks = 0;
+#if 0
+ tmbx = (void *)fuword((caddr_t)ke->ke_mailbox
+ + offsetof(struct kse_mailbox, km_curthread));
+#else /* if user pointer arithmetic is ok in the kernel */
+ tmbx = (void *)fuword( (void *)&ke->ke_mailbox->km_curthread);
+#endif
if ((tmbx == NULL) || (tmbx == (void *)-1))
- return (-1);
- uticks = td->td_uuticks;
- td->td_uuticks = 0;
+ return 0;
if (uticks) {
- addr = (caddr_t)&tmbx->tm_uticks;
+ addr = (caddr_t)tmbx + offsetof(struct kse_thr_mailbox, tm_uticks);
uticks += fuword(addr);
- if (suword(addr, uticks)) {
- PROC_LOCK(p);
- psignal(p, SIGSEGV);
- PROC_UNLOCK(p);
- return (-2);
- }
+ if (suword(addr, uticks))
+ goto bad;
}
- return (0);
-}
-
-/*
- * Export kernel mode state clock ticks
- */
-
-static int
-thread_update_sys_ticks(struct thread *td)
-{
- struct proc *p = td->td_proc;
- caddr_t addr;
- int sticks;
-
- if (td->td_mailbox == NULL)
- return (-1);
- if (td->td_usticks == 0)
- return (0);
- addr = (caddr_t)&td->td_mailbox->tm_sticks;
- sticks = fuword(addr);
- /* XXXKSE use XCHG instead */
- sticks += td->td_usticks;
- td->td_usticks = 0;
- if (suword(addr, sticks)) {
- PROC_LOCK(p);
- psignal(p, SIGSEGV);
- PROC_UNLOCK(p);
- return (-2);
+ if (sticks) {
+ addr = (caddr_t)tmbx + offsetof(struct kse_thr_mailbox, tm_sticks);
+ sticks += fuword(addr);
+ if (suword(addr, sticks))
+ goto bad;
}
- return (0);
+ return 0;
+bad:
+ PROC_LOCK(p);
+ psignal(p, SIGSEGV);
+ PROC_UNLOCK(p);
+ return -1;
}
/*
@@ -1197,7 +1013,6 @@ thread_exit(void)
p->p_numthreads--;
TAILQ_REMOVE(&kg->kg_threads, td, td_kglist);
kg->kg_numthreads--;
-
/*
* The test below is NOT true if we are the
* sole exiting thread. P_STOPPED_SNGL is unset
@@ -1209,28 +1024,25 @@ thread_exit(void)
}
}
- /*
- * Because each upcall structure has an owner thread,
- * owner thread exits only when process is in exiting
- * state, so upcall to userland is no longer needed,
- * deleting upcall structure is safe here.
- * So when all threads in a group is exited, all upcalls
- * in the group should be automatically freed.
- */
- if (td->td_upcall)
- upcall_remove(td);
-
+ /* Reassign this thread's KSE. */
ke->ke_state = KES_UNQUEUED;
- ke->ke_thread = NULL;
+
/*
* Decide what to do with the KSE attached to this thread.
+ * XXX Possibly kse_reassign should do both cases as it already
+ * does some of this.
*/
- if (ke->ke_flags & KEF_EXIT)
+ if (ke->ke_flags & KEF_EXIT) {
+ KASSERT((ke->ke_owner == td),
+ ("thread_exit: KSE exiting with non-owner thread"));
+ ke->ke_thread = NULL;
+ td->td_kse = NULL;
kse_unlink(ke);
- else
+ } else {
+ TD_SET_EXITING(td); /* definitly not runnable */
kse_reassign(ke);
+ }
PROC_UNLOCK(p);
- td->td_kse = NULL;
td->td_state = TDS_INACTIVE;
td->td_proc = NULL;
td->td_ksegrp = NULL;
@@ -1278,12 +1090,10 @@ thread_link(struct thread *td, struct ksegrp *kg)
struct proc *p;
p = kg->kg_proc;
- td->td_state = TDS_INACTIVE;
- td->td_proc = p;
- td->td_ksegrp = kg;
- td->td_last_kse = NULL;
- td->td_flags = 0;
- td->td_kse = NULL;
+ td->td_state = TDS_INACTIVE;
+ td->td_proc = p;
+ td->td_ksegrp = kg;
+ td->td_last_kse = NULL;
LIST_INIT(&td->td_contested);
callout_init(&td->td_slpcallout, 1);
@@ -1291,139 +1101,116 @@ thread_link(struct thread *td, struct ksegrp *kg)
TAILQ_INSERT_HEAD(&kg->kg_threads, td, td_kglist);
p->p_numthreads++;
kg->kg_numthreads++;
+ td->td_kse = NULL;
}
-/*
- * Purge a ksegrp resource. When a ksegrp is preparing to
- * exit, it calls this function.
- */
-void
-kse_purge_group(struct thread *td)
-{
- struct ksegrp *kg;
- struct kse *ke;
-
- kg = td->td_ksegrp;
- KASSERT(kg->kg_numthreads == 1, ("%s: bad thread number", __func__));
- while ((ke = TAILQ_FIRST(&kg->kg_iq)) != NULL) {
- KASSERT(ke->ke_state == KES_IDLE,
- ("%s: wrong idle KSE state", __func__));
- kse_unlink(ke);
- }
- KASSERT((kg->kg_kses == 1),
- ("%s: ksegrp still has %d KSEs", __func__, kg->kg_kses));
- KASSERT((kg->kg_numupcalls == 0),
- ("%s: ksegrp still has %d upcall datas",
- __func__, kg->kg_numupcalls));
-}
-
-/*
- * Purge a process's KSE resource. When a process is preparing to
- * exit, it calls kse_purge to release any extra KSE resources in
- * the process.
- */
void
kse_purge(struct proc *p, struct thread *td)
{
+ /* XXXKSE think about this..
+ may need to wake up threads on loan queue. */
struct ksegrp *kg;
- struct kse *ke;
KASSERT(p->p_numthreads == 1, ("bad thread number"));
mtx_lock_spin(&sched_lock);
while ((kg = TAILQ_FIRST(&p->p_ksegrps)) != NULL) {
TAILQ_REMOVE(&p->p_ksegrps, kg, kg_ksegrp);
p->p_numksegrps--;
- /*
- * There is no ownership for KSE, after all threads
- * in the group exited, it is possible that some KSEs
- * were left in idle queue, gc them now.
- */
- while ((ke = TAILQ_FIRST(&kg->kg_iq)) != NULL) {
- KASSERT(ke->ke_state == KES_IDLE,
- ("%s: wrong idle KSE state", __func__));
- TAILQ_REMOVE(&kg->kg_iq, ke, ke_kgrlist);
- kg->kg_idle_kses--;
- TAILQ_REMOVE(&kg->kg_kseq, ke, ke_kglist);
- kg->kg_kses--;
- kse_stash(ke);
- }
KASSERT(((kg->kg_kses == 0) && (kg != td->td_ksegrp)) ||
- ((kg->kg_kses == 1) && (kg == td->td_ksegrp)),
- ("ksegrp has wrong kg_kses: %d", kg->kg_kses));
- KASSERT((kg->kg_numupcalls == 0),
- ("%s: ksegrp still has %d upcall datas",
- __func__, kg->kg_numupcalls));
-
- if (kg != td->td_ksegrp)
+ ((kg->kg_kses == 1) && (kg == td->td_ksegrp)),
+ ("wrong kg_kses"));
+ if (kg != td->td_ksegrp) {
ksegrp_stash(kg);
+ }
}
TAILQ_INSERT_HEAD(&p->p_ksegrps, td->td_ksegrp, kg_ksegrp);
p->p_numksegrps++;
mtx_unlock_spin(&sched_lock);
}
-/*
- * 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().
- */
-void
-thread_alloc_spare(struct thread *td, struct thread *spare)
-{
- if (td->td_standin)
- return;
- if (spare == NULL)
- 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;
- /* Setup PCB and fork address */
- cpu_set_upcall(spare, td->td_pcb);
- /*
- * XXXKSE do we really need this? (default values for the
- * frame).
- */
- bcopy(td->td_frame, spare->td_frame, sizeof(struct trapframe));
- spare->td_ucred = crhold(td->td_ucred);
-}
/*
* Create a thread and schedule it for upcall on the KSE given.
* Use our thread's standin so that we don't have to allocate one.
*/
struct thread *
-thread_schedule_upcall(struct thread *td, struct kse_upcall *ku)
+thread_schedule_upcall(struct thread *td, struct kse *ke)
{
struct thread *td2;
+ int newkse;
mtx_assert(&sched_lock, MA_OWNED);
+ newkse = (ke != td->td_kse);
/*
- * Schedule an upcall thread on specified kse_upcall,
- * the kse_upcall must be free.
- * td must have a spare thread.
+ * If the owner and kse are BOUND then that thread is planning to
+ * go to userland and upcalls are not expected. So don't make one.
+ * If it is not bound then make it so with the spare thread
+ * anf then borrw back the KSE to allow us to complete some in-kernel
+ * work. When we complete, the Bound thread will have the chance to
+ * complete. This thread will sleep as planned. Hopefully there will
+ * eventually be un unbound thread that can be converted to an
+ * upcall to report the completion of this thread.
*/
- KASSERT(ku->ku_owner == NULL, ("%s: upcall has owner", __func__));
+
if ((td2 = td->td_standin) != NULL) {
td->td_standin = NULL;
} else {
- panic("no reserve thread when scheduling an upcall");
+ if (newkse)
+ panic("no reserve thread when called with a new kse");
+ /*
+ * If called from (e.g.) sleep and we do not have
+ * a reserve thread, then we've used it, so do not
+ * create an upcall.
+ */
return (NULL);
}
CTR3(KTR_PROC, "thread_schedule_upcall: thread %p (pid %d, %s)",
td2, td->td_proc->p_pid, td->td_proc->p_comm);
+ bzero(&td2->td_startzero,
+ (unsigned)RANGEOF(struct thread, td_startzero, td_endzero));
bcopy(&td->td_startcopy, &td2->td_startcopy,
(unsigned) RANGEOF(struct thread, td_startcopy, td_endcopy));
- thread_link(td2, ku->ku_ksegrp);
- /* Let the new thread become owner of the upcall */
- ku->ku_owner = td2;
- td2->td_upcall = ku;
- td2->td_flags = TDF_UPCALLING;
- td2->td_kse = NULL;
- td2->td_state = TDS_CAN_RUN;
+ thread_link(td2, ke->ke_ksegrp);
+ cpu_set_upcall(td2, td->td_pcb);
+
+ /*
+ * XXXKSE do we really need this? (default values for the
+ * frame).
+ */
+ bcopy(td->td_frame, td2->td_frame, sizeof(struct trapframe));
+
+ /*
+ * Bind the new thread to the KSE,
+ * and if it's our KSE, lend it back to ourself
+ * so we can continue running.
+ */
+ td2->td_ucred = crhold(td->td_ucred);
+ td2->td_flags = TDF_UPCALLING; /* note: BOUND */
+ td2->td_kse = ke;
+ td2->td_state = TDS_CAN_RUN;
td2->td_inhibitors = 0;
- setrunqueue(td2);
+ ke->ke_owner = td2;
+ /*
+ * If called from kse_reassign(), we are working on the current
+ * KSE so fake that we borrowed it. If called from
+ * kse_create(), don't, as we have a new kse too.
+ */
+ if (!newkse) {
+ /*
+ * This thread will be scheduled when the current thread
+ * blocks, exits or tries to enter userspace, (which ever
+ * happens first). When that happens the KSe will "revert"
+ * to this thread in a BOUND manner. Since we are called
+ * from msleep() this is going to be "very soon" in nearly
+ * all cases.
+ */
+ TD_SET_LOAN(td2);
+ } else {
+ ke->ke_thread = td2;
+ ke->ke_state = KES_THREAD;
+ setrunqueue(td2);
+ }
return (td2); /* bogus.. should be a void function */
}
@@ -1435,16 +1222,14 @@ thread_schedule_upcall(struct thread *td, struct kse_upcall *ku)
struct thread *
signal_upcall(struct proc *p, int sig)
{
-#if 0
struct thread *td, *td2;
struct kse *ke;
sigset_t ss;
int error;
-#endif
PROC_LOCK_ASSERT(p, MA_OWNED);
return (NULL);
-#if 0
+
td = FIRST_THREAD_IN_PROC(p);
ke = td->td_kse;
PROC_UNLOCK(p);
@@ -1459,31 +1244,28 @@ return (NULL);
if (error)
return (NULL);
if (td->td_standin == NULL)
- thread_alloc_spare(td, NULL);
+ td->td_standin = thread_alloc();
mtx_lock_spin(&sched_lock);
td2 = thread_schedule_upcall(td, ke); /* Bogus JRE */
mtx_unlock_spin(&sched_lock);
return (td2);
-#endif
}
/*
- * Setup done on the thread when it enters the kernel.
+ * setup done on the thread when it enters the kernel.
* XXXKSE Presently only for syscalls but eventually all kernel entries.
*/
void
thread_user_enter(struct proc *p, struct thread *td)
{
- struct ksegrp *kg;
- struct kse_upcall *ku;
+ struct kse *ke;
- kg = td->td_ksegrp;
/*
* First check that we shouldn't just abort.
* But check if we are the single thread first!
* XXX p_singlethread not locked, but should be safe.
*/
- if ((p->p_flag & P_SINGLE_EXIT) && (p->p_singlethread != td)) {
+ if ((p->p_flag & P_WEXIT) && (p->p_singlethread != td)) {
PROC_LOCK(p);
mtx_lock_spin(&sched_lock);
thread_exit();
@@ -1496,37 +1278,43 @@ thread_user_enter(struct proc *p, struct thread *td)
* possibility that we could do this lazily (in kse_reassign()),
* but for now do it every time.
*/
- kg = td->td_ksegrp;
- if (kg->kg_numupcalls) {
- ku = td->td_upcall;
- KASSERT(ku, ("%s: no upcall owned", __func__));
- KASSERT((ku->ku_owner == td), ("%s: wrong owner", __func__));
+ ke = td->td_kse;
+ td->td_flags &= ~TDF_UNBOUND;
+ if (ke->ke_mailbox != NULL) {
+#if 0
+ td->td_mailbox = (void *)fuword((caddr_t)ke->ke_mailbox
+ + offsetof(struct kse_mailbox, km_curthread));
+#else /* if user pointer arithmetic is ok in the kernel */
td->td_mailbox =
- (void *)fuword((void *)&ku->ku_mailbox->km_curthread);
+ (void *)fuword( (void *)&ke->ke_mailbox->km_curthread);
+#endif
if ((td->td_mailbox == NULL) ||
(td->td_mailbox == (void *)-1)) {
- /* Don't schedule upcall when blocked */
- td->td_mailbox = NULL;
+ td->td_mailbox = NULL; /* single thread it.. */
mtx_lock_spin(&sched_lock);
- td->td_flags &= ~TDF_CAN_UNBIND;
+ td->td_flags &= ~(TDF_UNBOUND|TDF_CAN_UNBIND);
mtx_unlock_spin(&sched_lock);
} else {
+ /*
+ * when thread limit reached, act like that the thread
+ * has already done an upcall.
+ */
if (p->p_numthreads > max_threads_per_proc) {
- /*
- * Since kernel thread limit reached,
- * don't schedule upcall anymore.
- * XXXKSE These code in fact needn't.
- */
- mtx_lock_spin(&sched_lock);
- td->td_flags &= ~TDF_CAN_UNBIND;
- mtx_unlock_spin(&sched_lock);
+ if (td->td_standin != NULL) {
+ thread_stash(td->td_standin);
+ td->td_standin = NULL;
+ }
} else {
if (td->td_standin == NULL)
- thread_alloc_spare(td, NULL);
- mtx_lock_spin(&sched_lock);
- td->td_flags |= TDF_CAN_UNBIND;
- mtx_unlock_spin(&sched_lock);
+ td->td_standin = thread_alloc();
}
+ mtx_lock_spin(&sched_lock);
+ td->td_flags |= TDF_CAN_UNBIND;
+ mtx_unlock_spin(&sched_lock);
+ KASSERT((ke->ke_owner == td),
+ ("thread_user_enter: No starting owner "));
+ ke->ke_owner = td;
+ td->td_usticks = 0;
}
}
}
@@ -1547,90 +1335,165 @@ int
thread_userret(struct thread *td, struct trapframe *frame)
{
int error;
- struct kse_upcall *ku;
+ int unbound;
+ struct kse *ke;
struct ksegrp *kg;
+ struct thread *worktodo;
struct proc *p;
struct timespec ts;
- p = td->td_proc;
+ KASSERT((td->td_kse && td->td_kse->ke_thread && td->td_kse->ke_owner),
+ ("thread_userret: bad thread/kse pointers"));
+ KASSERT((td == curthread),
+ ("thread_userret: bad thread argument"));
+
+
kg = td->td_ksegrp;
+ p = td->td_proc;
+ error = 0;
+ unbound = TD_IS_UNBOUND(td);
- /* Nothing to do with non-threaded group/process */
- if (td->td_ksegrp->kg_numupcalls == 0)
- return (0);
+ mtx_lock_spin(&sched_lock);
+ if ((worktodo = kg->kg_last_assigned))
+ worktodo = TAILQ_NEXT(worktodo, td_runq);
+ else
+ worktodo = TAILQ_FIRST(&kg->kg_runq);
/*
- * State clock interrupt hit in userland, it
- * is returning from interrupt, charge thread's
- * userland time for UTS.
+ * Permanently bound threads never upcall but they may
+ * loan out their KSE at this point.
+ * Upcalls imply bound.. They also may want to do some Philantropy.
+ * Temporarily bound threads on the other hand either yield
+ * to other work and transform into an upcall, or proceed back to
+ * userland.
*/
- if (td->td_flags & TDF_USTATCLOCK) {
- thread_update_usr_ticks(td);
- mtx_lock_spin(&sched_lock);
- td->td_flags &= ~TDF_USTATCLOCK;
- mtx_unlock_spin(&sched_lock);
- }
- /*
- * Optimisation:
- * This thread has not started any upcall.
- * If there is no work to report other than ourself,
- * then it can return direct to userland.
- */
if (TD_CAN_UNBIND(td)) {
- mtx_lock_spin(&sched_lock);
- td->td_flags &= ~TDF_CAN_UNBIND;
- mtx_unlock_spin(&sched_lock);
- if ((kg->kg_completed == NULL) &&
- (td->td_upcall->ku_flags & KUF_DOUPCALL) == 0) {
- thread_update_sys_ticks(td);
+ td->td_flags &= ~(TDF_UNBOUND|TDF_CAN_UNBIND);
+ if (!worktodo && (kg->kg_completed == NULL) &&
+ !(td->td_kse->ke_flags & KEF_DOUPCALL)) {
+ /*
+ * This thread has not started any upcall.
+ * If there is no work to report other than
+ * ourself, then it can return direct to userland.
+ */
+justreturn:
+ mtx_unlock_spin(&sched_lock);
+ thread_update_uticks();
td->td_mailbox = NULL;
return (0);
}
+ mtx_unlock_spin(&sched_lock);
error = thread_export_context(td);
+ td->td_usticks = 0;
if (error) {
/*
- * Failing to do the KSE operation just defaults
+ * As we are not running on a borrowed KSE,
+ * failing to do the KSE operation just defaults
* back to synchonous operation, so just return from
* the syscall.
*/
- return (0);
+ goto justreturn;
}
+ mtx_lock_spin(&sched_lock);
/*
- * There is something to report, and we own an upcall
- * strucuture, we can go to userland.
- * Turn ourself into an upcall thread.
+ * Turn ourself into a bound upcall.
+ * We will rely on kse_reassign()
+ * to make us run at a later time.
*/
- mtx_lock_spin(&sched_lock);
td->td_flags |= TDF_UPCALLING;
+
+ /* there may be more work since we re-locked schedlock */
+ if ((worktodo = kg->kg_last_assigned))
+ worktodo = TAILQ_NEXT(worktodo, td_runq);
+ else
+ worktodo = TAILQ_FIRST(&kg->kg_runq);
+ } else if (unbound) {
+ /*
+ * We are an unbound thread, looking to
+ * return to user space. There must be another owner
+ * of this KSE.
+ * We are using a borrowed KSE. save state and exit.
+ * kse_reassign() will recycle the kse as needed,
+ */
mtx_unlock_spin(&sched_lock);
- } else if (td->td_mailbox) {
error = thread_export_context(td);
+ td->td_usticks = 0;
if (error) {
+ /*
+ * There is nothing we can do.
+ * We just lose that context. We
+ * probably should note this somewhere and send
+ * the process a signal.
+ */
PROC_LOCK(td->td_proc);
+ psignal(td->td_proc, SIGSEGV);
mtx_lock_spin(&sched_lock);
+ ke = td->td_kse;
/* possibly upcall with error? */
} else {
- PROC_LOCK(td->td_proc);
- mtx_lock_spin(&sched_lock);
/*
- * There are upcall threads waiting for
- * work to do, wake one of them up.
- * XXXKSE Maybe wake all of them up.
+ * Don't make an upcall, just exit so that the owner
+ * can get its KSE if it wants it.
+ * Our context is already safely stored for later
+ * use by the UTS.
*/
- if (kg->kg_upsleeps)
- wakeup_one(&kg->kg_completed);
+ PROC_LOCK(p);
+ mtx_lock_spin(&sched_lock);
+ ke = td->td_kse;
+ }
+ /*
+ * If the owner is idling, we now have something for it
+ * to report, so make it runnable.
+ * If the owner is not an upcall, make an attempt to
+ * ensure that at least one of any IDLED upcalls can
+ * wake up.
+ */
+ if (ke->ke_owner->td_flags & TDF_UPCALLING) {
+ TD_CLR_IDLE(ke->ke_owner);
+ } else {
+ FOREACH_KSE_IN_GROUP(kg, ke) {
+ if (TD_IS_IDLE(ke->ke_owner)) {
+ TD_CLR_IDLE(ke->ke_owner);
+ setrunnable(ke->ke_owner);
+ break;
+ }
+ }
}
thread_exit();
- /* NOTREACHED */
}
+ /*
+ * We ARE going back to userland with this KSE.
+ * We are permanently bound. We may be an upcall.
+ * If an upcall, check for threads that need to borrow the KSE.
+ * Any other thread that comes ready after this missed the boat.
+ */
+ ke = td->td_kse;
+ /*
+ * If not upcalling, go back to userspace.
+ * If we are, get the upcall set up.
+ */
if (td->td_flags & TDF_UPCALLING) {
- KASSERT(TD_CAN_UNBIND(td) == 0, ("upcall thread can unbind"));
- ku = td->td_upcall;
+ if (worktodo) {
+ /*
+ * force a switch to more urgent 'in kernel'
+ * work. Control will return to this thread
+ * when there is no more work to do.
+ * kse_reassign() will do that for us.
+ */
+ TD_SET_LOAN(td);
+ p->p_stats->p_ru.ru_nvcsw++;
+ mi_switch(); /* kse_reassign() will (re)find worktodo */
+ }
+ td->td_flags &= ~TDF_UPCALLING;
+ if (ke->ke_flags & KEF_DOUPCALL)
+ ke->ke_flags &= ~KEF_DOUPCALL;
+ mtx_unlock_spin(&sched_lock);
+
/*
* There is no more work to do and we are going to ride
- * this thread up to userland as an upcall.
+ * this thread/KSE up to userland as an upcall.
* Do the last parts of the setup needed for the upcall.
*/
CTR3(KTR_PROC, "userret: upcall thread %p (pid %d, %s)",
@@ -1641,27 +1504,16 @@ thread_userret(struct thread *td, struct trapframe *frame)
* Will use Giant in cpu_thread_clean() because it uses
* kmem_free(kernel_map, ...)
*/
- cpu_set_upcall_kse(td, ku);
-
- /*
- * Clear TDF_UPCALLING after set upcall context,
- * profiling code looks TDF_UPCALLING to avoid account
- * a wrong user %EIP
- */
- mtx_lock_spin(&sched_lock);
- td->td_flags &= ~TDF_UPCALLING;
- if (ku->ku_flags & KUF_DOUPCALL)
- ku->ku_flags &= ~KUF_DOUPCALL;
- mtx_unlock_spin(&sched_lock);
+ cpu_set_upcall_kse(td, ke);
- /*
+ /*
* Unhook the list of completed threads.
* anything that completes after this gets to
* come in next time.
* Put the list of completed thread mailboxes on
* this KSE's mailbox.
*/
- error = thread_link_mboxes(kg, ku);
+ error = thread_link_mboxes(kg, ke);
if (error)
goto bad;
@@ -1672,33 +1524,34 @@ thread_userret(struct thread *td, struct trapframe *frame)
* it would be nice if this all happenned only on the first
* time through. (the scan for extra work etc.)
*/
- error = suword((caddr_t)&ku->ku_mailbox->km_curthread, 0);
+#if 0
+ error = suword((caddr_t)ke->ke_mailbox +
+ offsetof(struct kse_mailbox, km_curthread), 0);
+#else /* if user pointer arithmetic is ok in the kernel */
+ error = suword((caddr_t)&ke->ke_mailbox->km_curthread, 0);
+#endif
+ ke->ke_uuticks = ke->ke_usticks = 0;
if (error)
goto bad;
-
- /* Export current system time */
nanotime(&ts);
if (copyout(&ts,
- (caddr_t)&ku->ku_mailbox->km_timeofday, sizeof(ts))) {
+ (caddr_t)&ke->ke_mailbox->km_timeofday, sizeof(ts))) {
goto bad;
}
+ } else {
+ mtx_unlock_spin(&sched_lock);
}
/*
* Optimisation:
* Ensure that we have a spare thread available,
* for when we re-enter the kernel.
*/
- if (td->td_standin == NULL)
- thread_alloc_spare(td, NULL);
+ if (td->td_standin == NULL) {
+ td->td_standin = thread_alloc();
+ }
- /*
- * Clear thread mailbox first, then clear system tick count.
- * The order is important because thread_statclock() use
- * mailbox pointer to see if it is an userland thread or
- * an UTS kernel thread.
- */
+ thread_update_uticks();
td->td_mailbox = NULL;
- td->td_usticks = 0;
return (0);
bad:
@@ -1710,7 +1563,6 @@ bad:
psignal(td->td_proc, SIGSEGV);
PROC_UNLOCK(td->td_proc);
td->td_mailbox = NULL;
- td->td_usticks = 0;
return (error); /* go sync */
}
@@ -1749,6 +1601,7 @@ thread_single(int force_exit)
if (force_exit == SINGLE_EXIT) {
p->p_flag |= P_SINGLE_EXIT;
+ td->td_flags &= ~TDF_UNBOUND;
} else
p->p_flag &= ~P_SINGLE_EXIT;
p->p_flag |= P_STOPPED_SINGLE;
@@ -1771,16 +1624,17 @@ thread_single(int force_exit)
else
abortsleep(td2);
}
+ if (TD_IS_IDLE(td2)) {
+ TD_CLR_IDLE(td2);
+ }
} else {
if (TD_IS_SUSPENDED(td2))
continue;
- /*
- * maybe other inhibitted states too?
- * XXXKSE Is it totally safe to
- * suspend a non-interruptable thread?
- */
+ /* maybe other inhibitted states too? */
if (td2->td_inhibitors &
- (TDI_SLEEPING | TDI_SWAPPED))
+ (TDI_SLEEPING | TDI_SWAPPED |
+ TDI_LOAN | TDI_IDLE |
+ TDI_EXITING))
thread_suspend_one(td2);
}
}
@@ -1806,14 +1660,8 @@ thread_single(int force_exit)
mtx_lock(&Giant);
PROC_LOCK(p);
}
- if (force_exit == SINGLE_EXIT) {
- if (td->td_upcall) {
- mtx_lock_spin(&sched_lock);
- upcall_remove(td);
- mtx_unlock_spin(&sched_lock);
- }
+ if (force_exit == SINGLE_EXIT)
kse_purge(p, td);
- }
return (0);
}
@@ -1855,6 +1703,7 @@ thread_suspend_check(int return_instead)
{
struct thread *td;
struct proc *p;
+ struct kse *ke;
struct ksegrp *kg;
td = curthread;
@@ -1886,6 +1735,16 @@ thread_suspend_check(int return_instead)
mtx_lock_spin(&sched_lock);
while (mtx_owned(&Giant))
mtx_unlock(&Giant);
+ /*
+ * All threads should be exiting
+ * Unless they are the active "singlethread".
+ * destroy un-needed KSEs as we go..
+ * KSEGRPS may implode too as #kses -> 0.
+ */
+ ke = td->td_kse;
+ if (ke->ke_owner == td &&
+ (kg->kg_kses >= kg->kg_numthreads ))
+ ke->ke_flags |= KEF_EXIT;
thread_exit();
}
@@ -1893,6 +1752,14 @@ thread_suspend_check(int return_instead)
* When a thread suspends, it just
* moves to the processes's suspend queue
* and stays there.
+ *
+ * XXXKSE if TDF_BOUND is true
+ * it will not release it's KSE which might
+ * lead to deadlock if there are not enough KSEs
+ * to complete all waiting threads.
+ * Maybe be able to 'lend' it out again.
+ * (lent kse's can not go back to userland?)
+ * and can only be lent in STOPPED state.
*/
mtx_lock_spin(&sched_lock);
if ((p->p_flag & P_STOPPED_SIG) &&
OpenPOWER on IntegriCloud