aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_kse.c996
1 files changed, 19 insertions, 977 deletions
diff --git a/sys/kern/kern_kse.c b/sys/kern/kern_kse.c
index fc6a77c33257..522ed763412b 100644
--- a/sys/kern/kern_kse.c
+++ b/sys/kern/kern_kse.c
@@ -33,76 +33,49 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
-#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/smp.h>
#include <sys/sysctl.h>
#include <sys/sysproto.h>
-#include <sys/filedesc.h>
#include <sys/sched.h>
#include <sys/signalvar.h>
#include <sys/sleepqueue.h>
-#include <sys/sx.h>
-#include <sys/tty.h>
-#include <sys/turnstile.h>
-#include <sys/user.h>
#include <sys/kse.h>
#include <sys/ktr.h>
-#include <sys/ucontext.h>
-
-#include <vm/vm.h>
-#include <vm/vm_extern.h>
-#include <vm/vm_object.h>
-#include <vm/pmap.h>
#include <vm/uma.h>
-#include <vm/vm_map.h>
-
-#include <machine/frame.h>
/*
* KSEGRP related storage.
*/
-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");
-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 = 1500;
-SYSCTL_INT(_kern_threads, OID_AUTO, max_threads_per_proc, CTLFLAG_RW,
- &max_threads_per_proc, 0, "Limit on threads per proc");
+extern int virtual_cpu;
+extern int thread_debug;
+extern int max_threads_per_proc;
+extern int max_groups_per_proc;
+extern int max_threads_hits;
+extern struct mtx kse_zombie_lock;
-static int max_groups_per_proc = 500;
-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 max_threads_hits;
-SYSCTL_INT(_kern_threads, OID_AUTO, max_threads_hits, CTLFLAG_RD,
- &max_threads_hits, 0, "");
-
-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);
-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);
-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, int user);
static void thread_alloc_spare(struct thread *td, struct thread *spare);
+/* move to proc.h */
+extern void kse_purge(struct proc *p, struct thread *td);
+extern void kse_purge_group(struct thread *td);
+void kseinit(void);
+void kse_GC(void);
+
+static int virtual_cpu;
+SYSCTL_DECL(_kern_threads);
+
static int
sysctl_kse_virtual_cpu(SYSCTL_HANDLER_ARGS)
{
@@ -132,232 +105,6 @@ SYSCTL_PROC(_kern_threads, OID_AUTO, virtual_cpu, CTLTYPE_INT|CTLFLAG_RW,
0, sizeof(virtual_cpu), sysctl_kse_virtual_cpu, "I",
"debug virtual cpus");
-/*
- * Thread ID allocator. The allocator keeps track of assigned IDs by
- * using a bitmap. The bitmap is created in parts. The parts are linked
- * together.
- */
-typedef u_long tid_bitmap_word;
-
-#define TID_IDS_PER_PART 1024
-#define TID_IDS_PER_IDX (sizeof(tid_bitmap_word) << 3)
-#define TID_BITMAP_SIZE (TID_IDS_PER_PART / TID_IDS_PER_IDX)
-#define TID_MIN (PID_MAX + 1)
-
-struct tid_bitmap_part {
- STAILQ_ENTRY(tid_bitmap_part) bmp_next;
- tid_bitmap_word bmp_bitmap[TID_BITMAP_SIZE];
- int bmp_base;
- int bmp_free;
-};
-
-static STAILQ_HEAD(, tid_bitmap_part) tid_bitmap =
- STAILQ_HEAD_INITIALIZER(tid_bitmap);
-static uma_zone_t tid_zone;
-
-struct mtx tid_lock;
-MTX_SYSINIT(tid_lock, &tid_lock, "TID lock", MTX_DEF);
-
-/*
- * Prepare a thread for use.
- */
-static void
-thread_ctor(void *mem, int size, void *arg)
-{
- struct thread *td;
-
- td = (struct thread *)mem;
- td->td_tid = 0;
- td->td_state = TDS_INACTIVE;
- td->td_oncpu = NOCPU;
- td->td_critnest = 1;
-}
-
-/*
- * Reclaim a thread after use.
- */
-static void
-thread_dtor(void *mem, int size, void *arg)
-{
- struct thread *td;
- struct tid_bitmap_part *bmp;
- int bit, idx, tid;
-
- td = (struct thread *)mem;
-
- if (td->td_tid > PID_MAX) {
- STAILQ_FOREACH(bmp, &tid_bitmap, bmp_next) {
- if (td->td_tid >= bmp->bmp_base &&
- td->td_tid < bmp->bmp_base + TID_IDS_PER_PART)
- break;
- }
- KASSERT(bmp != NULL, ("No TID bitmap?"));
- mtx_lock(&tid_lock);
- tid = td->td_tid - bmp->bmp_base;
- idx = tid / TID_IDS_PER_IDX;
- bit = 1UL << (tid % TID_IDS_PER_IDX);
- bmp->bmp_bitmap[idx] |= bit;
- bmp->bmp_free++;
- mtx_unlock(&tid_lock);
- }
-
-#ifdef INVARIANTS
- /* Verify that this thread is in a safe state to free. */
- switch (td->td_state) {
- case TDS_INHIBITED:
- case TDS_RUNNING:
- case TDS_CAN_RUN:
- case TDS_RUNQ:
- /*
- * We must never unlink a thread that is in one of
- * these states, because it is currently active.
- */
- panic("bad state for thread unlinking");
- /* NOTREACHED */
- case TDS_INACTIVE:
- break;
- default:
- panic("bad thread state");
- /* NOTREACHED */
- }
-#endif
-}
-
-/*
- * Initialize type-stable parts of a thread (when newly created).
- */
-static void
-thread_init(void *mem, int size)
-{
- struct thread *td;
-
- td = (struct thread *)mem;
- vm_thread_new(td, 0);
- cpu_thread_setup(td);
- td->td_sleepqueue = sleepq_alloc();
- td->td_turnstile = turnstile_alloc();
- td->td_sched = (struct td_sched *)&td[1];
-}
-
-/*
- * Tear down type-stable parts of a thread (just before being discarded).
- */
-static void
-thread_fini(void *mem, int size)
-{
- struct thread *td;
-
- td = (struct thread *)mem;
- turnstile_free(td->td_turnstile);
- sleepq_free(td->td_sleepqueue);
- vm_thread_dispose(td);
-}
-
-/*
- * Initialize type-stable parts of a kse (when newly created).
- */
-static void
-kse_init(void *mem, int size)
-{
- struct kse *ke;
-
- ke = (struct kse *)mem;
- ke->ke_sched = (struct ke_sched *)&ke[1];
-}
-
-/*
- * Initialize type-stable parts of a ksegrp (when newly created).
- */
-static void
-ksegrp_init(void *mem, int size)
-{
- struct ksegrp *kg;
-
- kg = (struct ksegrp *)mem;
- kg->kg_sched = (struct kg_sched *)&kg[1];
-}
-
-/*
- * KSE is linked into kse group.
- */
-void
-kse_link(struct kse *ke, struct ksegrp *kg)
-{
- struct proc *p = kg->kg_proc;
-
- TAILQ_INSERT_HEAD(&kg->kg_kseq, ke, ke_kglist);
- kg->kg_kses++;
- ke->ke_state = KES_UNQUEUED;
- ke->ke_proc = p;
- ke->ke_ksegrp = kg;
- ke->ke_thread = NULL;
- ke->ke_oncpu = NOCPU;
- ke->ke_flags = 0;
-}
-
-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);
-}
-
-void
-ksegrp_link(struct ksegrp *kg, struct proc *p)
-{
-
- TAILQ_INIT(&kg->kg_threads);
- 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
- */
- 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 */
- p->p_numksegrps++;
- TAILQ_INSERT_HEAD(&p->p_ksegrps, kg, kg_ksegrp);
-}
-
-void
-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;
- TAILQ_REMOVE(&p->p_ksegrps, kg, kg_ksegrp);
- p->p_numksegrps--;
- /*
- * Aggregate stats from the KSE
- */
- ksegrp_stash(kg);
-}
struct kse_upcall *
upcall_alloc(void)
@@ -409,25 +156,6 @@ upcall_remove(struct thread *td)
}
}
-/*
- * For a newly created process,
- * link up all the structures and its initial threads etc.
- */
-void
-proc_linkup(struct proc *p, struct ksegrp *kg,
- struct kse *ke, struct thread *td)
-{
-
- TAILQ_INIT(&p->p_ksegrps); /* all ksegrps in proc */
- TAILQ_INIT(&p->p_threads); /* all threads in proc */
- TAILQ_INIT(&p->p_suspended); /* Threads suspended */
- p->p_numksegrps = 0;
- p->p_numthreads = 0;
-
- ksegrp_link(kg, p);
- kse_link(ke, kg);
- thread_link(td, kg);
-}
#ifndef _SYS_SYSPROTO_H_
struct kse_switchin_args {
@@ -907,50 +635,17 @@ kse_create(struct thread *td, struct kse_create_args *uap)
}
/*
- * Initialize global thread allocation resources.
+ * Initialize global kse related resources.
*/
void
-threadinit(void)
+kseinit(void)
{
- thread_zone = uma_zcreate("THREAD", sched_sizeof_thread(),
- thread_ctor, thread_dtor, thread_init, thread_fini,
- UMA_ALIGN_CACHE, 0);
- tid_zone = uma_zcreate("TID", sizeof(struct tid_bitmap_part),
- NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
- ksegrp_zone = uma_zcreate("KSEGRP", sched_sizeof_ksegrp(),
- NULL, NULL, ksegrp_init, NULL,
- UMA_ALIGN_CACHE, 0);
- 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);
}
/*
- * Stash an embarasingly extra thread into the zombie thread queue.
- */
-void
-thread_stash(struct thread *td)
-{
- mtx_lock_spin(&kse_zombie_lock);
- TAILQ_INSERT_HEAD(&zombie_threads, td, td_runq);
- mtx_unlock_spin(&kse_zombie_lock);
-}
-
-/*
- * Stash an embarasingly extra kse into the zombie kse queue.
- */
-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);
-}
-
-/*
* Stash an embarasingly extra upcall into the zombie upcall queue.
*/
@@ -963,66 +658,23 @@ upcall_stash(struct kse_upcall *ku)
}
/*
- * Stash an embarasingly extra ksegrp into the zombie ksegrp queue.
- */
-void
-ksegrp_stash(struct ksegrp *kg)
-{
- mtx_lock_spin(&kse_zombie_lock);
- TAILQ_INSERT_HEAD(&zombie_ksegrps, kg, kg_ksegrp);
- mtx_unlock_spin(&kse_zombie_lock);
-}
-
-/*
* Reap zombie kse resource.
*/
void
-thread_reap(void)
+kse_GC(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..
*/
- if ((!TAILQ_EMPTY(&zombie_threads))
- || (!TAILQ_EMPTY(&zombie_kses))
- || (!TAILQ_EMPTY(&zombie_ksegrps))
- || (!TAILQ_EMPTY(&zombie_upcalls))) {
+ if ((!TAILQ_EMPTY(&zombie_upcalls))) {
mtx_lock_spin(&kse_zombie_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);
- 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;
- }
- while (ke_first) {
- ke_next = TAILQ_NEXT(ke_first, ke_procq);
- kse_free(ke_first);
- ke_first = ke_next;
- }
- while (kg_first) {
- kg_next = TAILQ_NEXT(kg_first, kg_ksegrp);
- ksegrp_free(kg_first);
- kg_first = kg_next;
- }
while (ku_first) {
ku_next = TAILQ_NEXT(ku_first, ku_link);
upcall_free(ku_first);
@@ -1032,110 +684,6 @@ thread_reap(void)
}
/*
- * Allocate a ksegrp.
- */
-struct ksegrp *
-ksegrp_alloc(void)
-{
- return (uma_zalloc(ksegrp_zone, M_WAITOK));
-}
-
-/*
- * Allocate a kse.
- */
-struct kse *
-kse_alloc(void)
-{
- return (uma_zalloc(kse_zone, M_WAITOK));
-}
-
-/*
- * Allocate a thread.
- */
-struct thread *
-thread_alloc(void)
-{
- thread_reap(); /* check if any zombies to get */
- return (uma_zalloc(thread_zone, M_WAITOK));
-}
-
-/*
- * Deallocate a ksegrp.
- */
-void
-ksegrp_free(struct ksegrp *td)
-{
- uma_zfree(ksegrp_zone, td);
-}
-
-/*
- * Deallocate a kse.
- */
-void
-kse_free(struct kse *td)
-{
- uma_zfree(kse_zone, td);
-}
-
-/*
- * Deallocate a thread.
- */
-void
-thread_free(struct thread *td)
-{
-
- cpu_thread_clean(td);
- uma_zfree(thread_zone, td);
-}
-
-/*
- * Assign a thread ID.
- */
-int
-thread_new_tid(void)
-{
- struct tid_bitmap_part *bmp, *new;
- int bit, idx, tid;
-
- mtx_lock(&tid_lock);
- STAILQ_FOREACH(bmp, &tid_bitmap, bmp_next) {
- if (bmp->bmp_free)
- break;
- }
- /* Create a new bitmap if we run out of free bits. */
- if (bmp == NULL) {
- mtx_unlock(&tid_lock);
- new = uma_zalloc(tid_zone, M_WAITOK);
- mtx_lock(&tid_lock);
- bmp = STAILQ_LAST(&tid_bitmap, tid_bitmap_part, bmp_next);
- if (bmp == NULL || bmp->bmp_free < TID_IDS_PER_PART/2) {
- /* 1=free, 0=assigned. This way we can use ffsl(). */
- memset(new->bmp_bitmap, ~0U, sizeof(new->bmp_bitmap));
- new->bmp_base = (bmp == NULL) ? TID_MIN :
- bmp->bmp_base + TID_IDS_PER_PART;
- new->bmp_free = TID_IDS_PER_PART;
- STAILQ_INSERT_TAIL(&tid_bitmap, new, bmp_next);
- bmp = new;
- new = NULL;
- }
- } else
- new = NULL;
- /* We have a bitmap with available IDs. */
- idx = 0;
- while (idx < TID_BITMAP_SIZE && bmp->bmp_bitmap[idx] == 0UL)
- idx++;
- bit = ffsl(bmp->bmp_bitmap[idx]) - 1;
- tid = bmp->bmp_base + idx * TID_IDS_PER_IDX + bit;
- bmp->bmp_bitmap[idx] &= ~(1UL << bit);
- bmp->bmp_free--;
- mtx_unlock(&tid_lock);
-
- if (new != NULL)
- uma_zfree(tid_zone, new);
- return (tid);
-}
-
-/*
* Store the thread context in the UTS's mailbox.
* then add the mailbox at the head of a list we are building in user space.
* The list is anchored in the ksegrp structure.
@@ -1332,241 +880,6 @@ thread_update_usr_ticks(struct thread *td, int user)
}
/*
- * 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 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)
-{
- struct thread *td;
- struct kse *ke;
- struct proc *p;
- struct ksegrp *kg;
-
- td = curthread;
- kg = td->td_ksegrp;
- p = td->td_proc;
- ke = td->td_kse;
-
- mtx_assert(&sched_lock, MA_OWNED);
- KASSERT(p != NULL, ("thread exiting without a process"));
- KASSERT(ke != NULL, ("thread exiting without a kse"));
- KASSERT(kg != NULL, ("thread exiting without a kse group"));
- PROC_LOCK_ASSERT(p, MA_OWNED);
- CTR1(KTR_PROC, "thread_exit: thread %p", td);
- mtx_assert(&Giant, MA_NOTOWNED);
-
- if (td->td_standin != NULL) {
- thread_stash(td->td_standin);
- td->td_standin = NULL;
- }
-
- cpu_thread_exit(td); /* XXXSMP */
-
- /*
- * The last thread is left attached to the process
- * So that the whole bundle gets recycled. Skip
- * all this stuff.
- */
- if (p->p_numthreads > 1) {
- thread_unlink(td);
- if (p->p_maxthrwaits)
- wakeup(&p->p_numthreads);
- /*
- * The test below is NOT true if we are the
- * sole exiting thread. P_STOPPED_SNGL is unset
- * in exit1() after it is the only survivor.
- */
- if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) {
- if (p->p_numthreads == p->p_suspcount) {
- thread_unsuspend_one(p->p_singlethread);
- }
- }
-
- /*
- * 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);
-
- sched_exit_thread(FIRST_THREAD_IN_PROC(p), td);
- sched_exit_kse(FIRST_KSE_IN_PROC(p), ke);
- ke->ke_state = KES_UNQUEUED;
- ke->ke_thread = NULL;
- /*
- * Decide what to do with the KSE attached to this thread.
- */
- if (ke->ke_flags & KEF_EXIT) {
- kse_unlink(ke);
- if (kg->kg_kses == 0) {
- sched_exit_ksegrp(FIRST_KSEGRP_IN_PROC(p), kg);
- ksegrp_unlink(kg);
- }
- }
- else
- kse_reassign(ke);
- PROC_UNLOCK(p);
- td->td_kse = NULL;
- td->td_state = TDS_INACTIVE;
-#if 0
- td->td_proc = NULL;
-#endif
- td->td_ksegrp = NULL;
- td->td_last_kse = NULL;
- PCPU_SET(deadthread, td);
- } else {
- PROC_UNLOCK(p);
- }
- /* XXX Shouldn't cpu_throw() here. */
- mtx_assert(&sched_lock, MA_OWNED);
- cpu_throw(td, choosethread());
- panic("I'm a teapot!");
- /* NOTREACHED */
-}
-
-/*
- * Do any thread specific cleanups that may be needed in wait()
- * called with Giant, proc and schedlock not held.
- */
-void
-thread_wait(struct proc *p)
-{
- struct thread *td;
-
- mtx_assert(&Giant, MA_NOTOWNED);
- KASSERT((p->p_numthreads == 1), ("Multiple threads in wait1()"));
- KASSERT((p->p_numksegrps == 1), ("Multiple 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
- * be used by the process.
- *
- * Note that we do not link to the proc's ucred here.
- * The thread is linked as if running but no KSE assigned.
- */
-void
-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_kflags = 0;
- td->td_kse = NULL;
-
- LIST_INIT(&td->td_contested);
- callout_init(&td->td_slpcallout, CALLOUT_MPSAFE);
- TAILQ_INSERT_HEAD(&p->p_threads, td, td_plist);
- TAILQ_INSERT_HEAD(&kg->kg_threads, td, td_kglist);
- p->p_numthreads++;
- kg->kg_numthreads++;
-}
-
-void
-thread_unlink(struct thread *td)
-{
- struct proc *p = td->td_proc;
- struct ksegrp *kg = td->td_ksegrp;
-
- mtx_assert(&sched_lock, MA_OWNED);
- TAILQ_REMOVE(&p->p_threads, td, td_plist);
- p->p_numthreads--;
- TAILQ_REMOVE(&kg->kg_threads, td, td_kglist);
- kg->kg_numthreads--;
- /* could clear a few other things here */
-}
-
-/*
- * Purge a ksegrp resource. When a ksegrp is preparing to
- * exit, it calls this function.
- */
-static 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.
- */
-static void
-kse_purge(struct proc *p, struct thread *td)
-{
- struct ksegrp *kg;
- struct kse *ke;
-
- KASSERT(p->p_numthreads == 1, ("bad thread number"));
- 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)
- ksegrp_stash(kg);
- }
- TAILQ_INSERT_HEAD(&p->p_ksegrps, td->td_ksegrp, kg_ksegrp);
- p->p_numksegrps++;
-}
-
-/*
* 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().
@@ -1963,277 +1276,6 @@ out:
return (error); /* go sync */
}
-/*
- * Enforce single-threading.
- *
- * Returns 1 if the caller must abort (another thread is waiting to
- * exit the process or similar). Process is locked!
- * Returns 0 when you are successfully the only thread running.
- * A process has successfully single threaded in the suspend mode when
- * There are no threads in user mode. Threads in the kernel must be
- * allowed to continue until they get to the user boundary. They may even
- * copy out their return values and data before suspending. They may however be
- * accellerated in reaching the user boundary as we will wake up
- * any sleeping threads that are interruptable. (PCATCH).
- */
-int
-thread_single(int force_exit)
-{
- struct thread *td;
- struct thread *td2;
- struct proc *p;
-
- td = curthread;
- p = td->td_proc;
- mtx_assert(&Giant, MA_NOTOWNED);
- PROC_LOCK_ASSERT(p, MA_OWNED);
- KASSERT((td != NULL), ("curthread is NULL"));
-
- if ((p->p_flag & P_SA) == 0 && p->p_numthreads == 1)
- return (0);
-
- /* Is someone already single threading? */
- if (p->p_singlethread)
- return (1);
-
- if (force_exit == SINGLE_EXIT) {
- p->p_flag |= P_SINGLE_EXIT;
- } else
- p->p_flag &= ~P_SINGLE_EXIT;
- p->p_flag |= P_STOPPED_SINGLE;
- mtx_lock_spin(&sched_lock);
- p->p_singlethread = td;
- while ((p->p_numthreads - p->p_suspcount) != 1) {
- FOREACH_THREAD_IN_PROC(p, td2) {
- if (td2 == td)
- continue;
- td2->td_flags |= TDF_ASTPENDING;
- if (TD_IS_INHIBITED(td2)) {
- if (force_exit == SINGLE_EXIT) {
- if (TD_IS_SUSPENDED(td2)) {
- thread_unsuspend_one(td2);
- }
- if (TD_ON_SLEEPQ(td2) &&
- (td2->td_flags & TDF_SINTR)) {
- sleepq_abort(td2);
- }
- } else {
- if (TD_IS_SUSPENDED(td2))
- continue;
- /*
- * maybe other inhibitted states too?
- * XXXKSE Is it totally safe to
- * suspend a non-interruptable thread?
- */
- if (td2->td_inhibitors &
- (TDI_SLEEPING | TDI_SWAPPED))
- thread_suspend_one(td2);
- }
- }
- }
- /*
- * Maybe we suspended some threads.. was it enough?
- */
- if ((p->p_numthreads - p->p_suspcount) == 1)
- break;
-
- /*
- * Wake us up when everyone else has suspended.
- * In the mean time we suspend as well.
- */
- thread_suspend_one(td);
- PROC_UNLOCK(p);
- mi_switch(SW_VOL);
- mtx_unlock_spin(&sched_lock);
- PROC_LOCK(p);
- mtx_lock_spin(&sched_lock);
- }
- if (force_exit == SINGLE_EXIT) {
- if (td->td_upcall)
- upcall_remove(td);
- kse_purge(p, td);
- }
- mtx_unlock_spin(&sched_lock);
- return (0);
-}
-
-/*
- * Called in from locations that can safely check to see
- * whether we have to suspend or at least throttle for a
- * single-thread event (e.g. fork).
- *
- * Such locations include userret().
- * If the "return_instead" argument is non zero, the thread must be able to
- * accept 0 (caller may continue), or 1 (caller must abort) as a result.
- *
- * The 'return_instead' argument tells the function if it may do a
- * thread_exit() or suspend, or whether the caller must abort and back
- * out instead.
- *
- * If the thread that set the single_threading request has set the
- * P_SINGLE_EXIT bit in the process flags then this call will never return
- * if 'return_instead' is false, but will exit.
- *
- * P_SINGLE_EXIT | return_instead == 0| return_instead != 0
- *---------------+--------------------+---------------------
- * 0 | returns 0 | returns 0 or 1
- * | when ST ends | immediatly
- *---------------+--------------------+---------------------
- * 1 | thread exits | returns 1
- * | | immediatly
- * 0 = thread_exit() or suspension ok,
- * other = return error instead of stopping the thread.
- *
- * While a full suspension is under effect, even a single threading
- * thread would be suspended if it made this call (but it shouldn't).
- * This call should only be made from places where
- * thread_exit() would be safe as that may be the outcome unless
- * return_instead is set.
- */
-int
-thread_suspend_check(int return_instead)
-{
- struct thread *td;
- struct proc *p;
-
- td = curthread;
- p = td->td_proc;
- mtx_assert(&Giant, MA_NOTOWNED);
- PROC_LOCK_ASSERT(p, MA_OWNED);
- while (P_SHOULDSTOP(p)) {
- if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) {
- KASSERT(p->p_singlethread != NULL,
- ("singlethread not set"));
- /*
- * The only suspension in action is a
- * single-threading. Single threader need not stop.
- * XXX Should be safe to access unlocked
- * as it can only be set to be true by us.
- */
- if (p->p_singlethread == td)
- return (0); /* Exempt from stopping. */
- }
- if (return_instead)
- return (1);
-
- mtx_lock_spin(&sched_lock);
- thread_stopped(p);
- /*
- * If the process is waiting for us to exit,
- * this thread should just suicide.
- * Assumes that P_SINGLE_EXIT implies P_STOPPED_SINGLE.
- */
- if ((p->p_flag & P_SINGLE_EXIT) && (p->p_singlethread != td)) {
- if (p->p_flag & P_SA)
- thread_exit();
- else
- thr_exit1();
- }
-
- /*
- * When a thread suspends, it just
- * moves to the processes's suspend queue
- * and stays there.
- */
- thread_suspend_one(td);
- if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) {
- if (p->p_numthreads == p->p_suspcount) {
- thread_unsuspend_one(p->p_singlethread);
- }
- }
- PROC_UNLOCK(p);
- mi_switch(SW_INVOL);
- mtx_unlock_spin(&sched_lock);
- PROC_LOCK(p);
- }
- return (0);
-}
-
-void
-thread_suspend_one(struct thread *td)
-{
- struct proc *p = td->td_proc;
-
- mtx_assert(&sched_lock, MA_OWNED);
- PROC_LOCK_ASSERT(p, MA_OWNED);
- KASSERT(!TD_IS_SUSPENDED(td), ("already suspended"));
- p->p_suspcount++;
- TD_SET_SUSPENDED(td);
- TAILQ_INSERT_TAIL(&p->p_suspended, td, td_runq);
- /*
- * Hack: If we are suspending but are on the sleep queue
- * then we are in msleep or the cv equivalent. We
- * want to look like we have two Inhibitors.
- * May already be set.. doesn't matter.
- */
- if (TD_ON_SLEEPQ(td))
- TD_SET_SLEEPING(td);
-}
-
-void
-thread_unsuspend_one(struct thread *td)
-{
- struct proc *p = td->td_proc;
-
- mtx_assert(&sched_lock, MA_OWNED);
- PROC_LOCK_ASSERT(p, MA_OWNED);
- TAILQ_REMOVE(&p->p_suspended, td, td_runq);
- TD_CLR_SUSPENDED(td);
- p->p_suspcount--;
- setrunnable(td);
-}
-
-/*
- * Allow all threads blocked by single threading to continue running.
- */
-void
-thread_unsuspend(struct proc *p)
-{
- struct thread *td;
-
- mtx_assert(&sched_lock, MA_OWNED);
- PROC_LOCK_ASSERT(p, MA_OWNED);
- if (!P_SHOULDSTOP(p)) {
- while (( td = TAILQ_FIRST(&p->p_suspended))) {
- thread_unsuspend_one(td);
- }
- } else if ((P_SHOULDSTOP(p) == P_STOPPED_SINGLE) &&
- (p->p_numthreads == p->p_suspcount)) {
- /*
- * Stopping everything also did the job for the single
- * threading request. Now we've downgraded to single-threaded,
- * let it continue.
- */
- thread_unsuspend_one(p->p_singlethread);
- }
-}
-
-void
-thread_single_end(void)
-{
- struct thread *td;
- struct proc *p;
-
- td = curthread;
- p = td->td_proc;
- PROC_LOCK_ASSERT(p, MA_OWNED);
- p->p_flag &= ~(P_STOPPED_SINGLE | P_SINGLE_EXIT);
- mtx_lock_spin(&sched_lock);
- p->p_singlethread = NULL;
- /*
- * If there are other threads they mey now run,
- * unless of course there is a blanket 'stop order'
- * on the process. The single threader must be allowed
- * to continue however as this is a bad place to stop.
- */
- if ((p->p_numthreads != 1) && (!P_SHOULDSTOP(p))) {
- while (( td = TAILQ_FIRST(&p->p_suspended))) {
- thread_unsuspend_one(td);
- }
- }
- mtx_unlock_spin(&sched_lock);
-}
-
int
thread_upcall_check(struct thread *td)
{