summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_thread.c
diff options
context:
space:
mode:
authormini <mini@FreeBSD.org>2002-09-16 19:26:48 +0000
committermini <mini@FreeBSD.org>2002-09-16 19:26:48 +0000
commit94ac5d965f6dd6ceff971a557f94ebb21964eb16 (patch)
tree09197457aee9cb1034d0af122f395a7002dbabc2 /sys/kern/kern_thread.c
parent5375a15c910d0210dbf79d04fceb3ab24a2c9422 (diff)
downloadFreeBSD-src-94ac5d965f6dd6ceff971a557f94ebb21964eb16.zip
FreeBSD-src-94ac5d965f6dd6ceff971a557f94ebb21964eb16.tar.gz
Add kernel support needed for the KSE-aware libpthread:
- Use ucontext_t's to store KSE thread state. - Synthesize state for the UTS upon each upcall, rather than saving and copying a trapframe. - Deliver signals to KSE-aware processes via upcall. - Rename kse mailbox structure fields to be more BSD-like. - Store the UTS's stack in struct proc in a stack_t. Reviewed by: bde, deischen, julian Approved by: -arch
Diffstat (limited to 'sys/kern/kern_thread.c')
-rw-r--r--sys/kern/kern_thread.c351
1 files changed, 233 insertions, 118 deletions
diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c
index 75cf5dd..8324773 100644
--- a/sys/kern/kern_thread.c
+++ b/sys/kern/kern_thread.c
@@ -44,6 +44,7 @@
#include <sys/jail.h>
#include <sys/kse.h>
#include <sys/ktr.h>
+#include <sys/ucontext.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
@@ -162,6 +163,39 @@ thread_fini(void *mem, int size)
}
/*
+ * Fill a ucontext_t with a thread's context information.
+ *
+ * This is an analogue to getcontext(3).
+ */
+void
+thread_getcontext(struct thread *td, ucontext_t *uc)
+{
+
+ get_mcontext(td, &uc->uc_mcontext);
+ uc->uc_sigmask = td->td_proc->p_sigmask;
+}
+
+/*
+ * Set a thread's context from a ucontext_t.
+ *
+ * This is an analogue to setcontext(3).
+ */
+int
+thread_setcontext(struct thread *td, ucontext_t *uc)
+{
+ int ret;
+
+ ret = set_mcontext(td, &uc->uc_mcontext);
+ if (ret == 0) {
+ SIG_CANTMASK(uc->uc_sigmask);
+ PROC_LOCK(td->td_proc);
+ td->td_proc->p_sigmask = uc->uc_sigmask;
+ PROC_UNLOCK(td->td_proc);
+ }
+ return (ret);
+}
+
+/*
* Initialize global thread allocation resources.
*/
void
@@ -190,8 +224,8 @@ thread_stash(struct thread *td)
mtx_unlock_spin(&zombie_thread_lock);
}
-/*
- * reap any zombie threads.
+/*
+ * Reap zombie threads.
*/
void
thread_reap(void)
@@ -281,18 +315,28 @@ thread_export_context(struct thread *td)
void *addr1;
void *addr2;
int error;
+ ucontext_t uc;
#ifdef __ia64__
td2_mbx = 0; /* pacify gcc (!) */
#endif
- /* Export the register contents. */
- error = cpu_export_context(td);
+ /* Export the user/machine context. */
+ error = copyin((caddr_t)td->td_mailbox +
+ offsetof(struct thread_mailbox, tm_context),
+ &uc,
+ sizeof(ucontext_t));
+ if (error == 0) {
+ thread_getcontext(td, &uc);
+ error = copyout(&uc, (caddr_t)td->td_mailbox +
+ offsetof(struct thread_mailbox, tm_context),
+ sizeof(ucontext_t));
+ }
ke = td->td_kse;
addr1 = (caddr_t)ke->ke_mailbox
- + offsetof(struct kse_mailbox, kmbx_completed_threads);
+ + offsetof(struct kse_mailbox, km_completed);
addr2 = (caddr_t)td->td_mailbox
- + offsetof(struct thread_mailbox , next_completed);
+ + offsetof(struct thread_mailbox , tm_next);
/* Then link it into it's KSE's list of completed threads. */
if (!error) {
error = td2_mbx = fuword(addr1);
@@ -422,9 +466,7 @@ thread_link(struct thread *td, struct ksegrp *kg)
}
/*
- * Set up the upcall pcb in either a given thread or a new one
- * if none given. Use the upcall for the given KSE
- * XXXKSE possibly fix cpu_set_upcall() to not need td->td_kse set.
+ * Create a thread and schedule it for upcall on the KSE given.
*/
struct thread *
thread_schedule_upcall(struct thread *td, struct kse *ke)
@@ -447,7 +489,18 @@ thread_schedule_upcall(struct thread *td, struct kse *ke)
bcopy(&td->td_startcopy, &td2->td_startcopy,
(unsigned) RANGEOF(struct thread, td_startcopy, td_endcopy));
thread_link(td2, ke->ke_ksegrp);
- cpu_set_upcall(td2, ke->ke_pcb);
+ cpu_set_upcall(td2, td->td_pcb);
+ bcopy(td->td_frame, td2->td_frame, sizeof(struct trapframe));
+ /*
+ * The user context for this thread is selected when we choose
+ * a KSE and return to userland on it. All we need do here is
+ * note that the thread exists in order to perform an upcall.
+ *
+ * Since selecting a KSE to perform the upcall involves locking
+ * that KSE's context to our upcall, its best to wait until the
+ * last possible moment before grabbing a KSE. We do this in
+ * userret().
+ */
td2->td_ucred = crhold(td->td_ucred);
td2->td_flags = TDF_UNBOUND|TDF_UPCALLING;
TD_SET_CAN_RUN(td2);
@@ -456,139 +509,201 @@ thread_schedule_upcall(struct thread *td, struct kse *ke)
}
/*
- * The extra work we go through if we are a threaded process when we
- * return to userland
+ * Schedule an upcall to notify a KSE process recieved signals.
+ *
+ * XXX - Modifying a sigset_t like this is totally bogus.
+ */
+struct thread *
+signal_upcall(struct proc *p, int sig)
+{
+ struct thread *td, *td2;
+ struct kse *ke;
+ sigset_t ss;
+ int error;
+
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+
+ td = FIRST_THREAD_IN_PROC(p);
+ ke = td->td_kse;
+ PROC_UNLOCK(p);
+ error = copyin(&ke->ke_mailbox->km_sigscaught, &ss, sizeof(sigset_t));
+ PROC_LOCK(p);
+ if (error)
+ return (NULL);
+ SIGADDSET(ss, sig);
+ PROC_UNLOCK(p);
+ error = copyout(&ss, &ke->ke_mailbox->km_sigscaught, sizeof(sigset_t));
+ PROC_LOCK(p);
+ if (error)
+ return (NULL);
+ mtx_lock_spin(&sched_lock);
+ td2 = thread_schedule_upcall(td, ke);
+ mtx_unlock_spin(&sched_lock);
+ return (td2);
+}
+
+/*
+ * Consider whether or not an upcall should be made, and update the
+ * TDF_UPCALLING flag appropriately.
+ *
+ * This function is called when the current thread had been bound to a user
+ * thread that performed a syscall that blocked, and is now returning.
+ * Got that? syscall -> msleep -> wakeup -> syscall_return -> us.
+ *
+ * This thread will be returned to the UTS in its mailbox as a completed
+ * thread. We need to decide whether or not to perform an upcall now,
+ * or simply queue the thread for later.
+ *
+ * XXXKSE Future enhancement: We could also return back to
+ * the thread if we haven't had to do an upcall since then.
+ * If the KSE's copy is == the thread's copy, and there are
+ * no other completed threads.
+ */
+static int
+thread_consider_upcalling(struct proc *p, struct ksegrp *kg, struct kse *ke,
+ struct thread *td, struct trapframe *frame)
+{
+ int error;
+
+ /*
+ * Save the thread's context, and link it
+ * into the KSE's list of completed threads.
+ */
+ error = thread_export_context(td);
+ td->td_mailbox = NULL;
+ if (error)
+ /*
+ * Failing to do the KSE operation just defaults
+ * back to synchonous operation, so just return from
+ * the syscall.
+ */
+ return (error);
+
+ /*
+ * Decide whether to perfom an upcall now.
+ */
+ /* Make sure there are no other threads waiting to run. */
+ if (TAILQ_FIRST(&kg->kg_runq)) {
+ /*
+ * Another thread in this KSEG needs to run.
+ * Switch to it instead of performing an upcall,
+ * abondoning this thread. Perform the upcall
+ * later; discard this thread for now.
+ *
+ * XXXKSE - As for the other threads to run;
+ * we COULD rush through all the threads
+ * in this KSEG at this priority, or we
+ * could throw the ball back into the court
+ * and just run the highest prio kse available.
+ * What is OUR priority? The priority of the highest
+ * sycall waiting to be returned?
+ * For now, just let another KSE run (easiest).
+ *
+ * XXXKSE Future enhancement: Shove threads in this
+ * state onto a list of completed threads hanging
+ * off the KSEG. Then, collect them before performing
+ * an upcall. This way, we don't commit to an upcall
+ * on a particular KSE, but report completed threads on
+ * the next upcall to any KSE in this KSEG.
+ *
+ */
+ PROC_LOCK(p);
+ mtx_lock_spin(&sched_lock);
+ thread_exit(); /* Abandon current thread. */
+ /* NOTREACHED */
+ } else
+ /*
+ * Perform an upcall now.
+ *
+ * XXXKSE - Assumes we are going to userland, and not
+ * nested in the kernel.
+ */
+ td->td_flags |= TDF_UPCALLING;
+ return (0);
+}
+
+/*
+ * The extra work we go through if we are a threaded process when we
+ * return to userland.
*
* If we are a KSE process and returning to user mode, check for
* extra work to do before we return (e.g. for more syscalls
* to complete first). If we were in a critical section, we should
* just return to let it finish. Same if we were in the UTS (in
- * which case we will have no thread mailbox registered). The only
- * traps we suport will have set the mailbox. We will clear it here.
+ * which case the mailbox's context's busy indicator will be set).
+ * The only traps we suport will have set the mailbox.
+ * We will clear it here.
*/
int
thread_userret(struct proc *p, struct ksegrp *kg, struct kse *ke,
struct thread *td, struct trapframe *frame)
{
- int error = 0;
+ int error;
+ /*
+ * Ensure that we have a spare thread available.
+ */
if (ke->ke_tdspare == NULL) {
+ mtx_lock(&Giant);
ke->ke_tdspare = thread_alloc();
+ mtx_unlock(&Giant);
}
- if (td->td_flags & TDF_UNBOUND) {
- /*
- * Are we returning from a thread that had a mailbox?
- *
- * XXX Maybe this should be in a separate function.
- */
- if (((td->td_flags & TDF_UPCALLING) == 0) && td->td_mailbox) {
- /*
- * [XXXKSE Future enhancement]
- * We could also go straight back to the syscall
- * if we never had to do an upcall since then.
- * If the KSE's copy is == the thread's copy..
- * AND there are no other completed threads.
- */
+
+ /*
+ * Bound threads need no additional work.
+ */
+ if ((td->td_flags & TDF_UNBOUND) == 0)
+ return (0);
+ error = 0;
+
+ /*
+ * Decide whether or not we should perform an upcall now.
+ */
+ if (((td->td_flags & TDF_UPCALLING) == 0) && td->td_mailbox) {
+ error = thread_consider_upcalling(p, kg, ke, td, frame);
+ if (error != 0)
/*
- * We will go back as an upcall or go do another thread.
- * Either way we need to save the context back to
- * the user thread mailbox.
- * So the UTS can restart it later.
+ * Failing to do the KSE operation just defaults
+ * back to synchonous operation, so just return from
+ * the syscall.
*/
- error = thread_export_context(td);
- td->td_mailbox = NULL;
- if (error) {
- /*
- * Failing to do the KSE
- * operation just defaults operation
- * back to synchonous operation.
- */
- goto cont;
- }
-
- if (TAILQ_FIRST(&kg->kg_runq)) {
- /*
- * Uh-oh.. don't return to the user.
- * Instead, switch to the thread that
- * needs to run. The question is:
- * What do we do with the thread we have now?
- * We have put the completion block
- * on the kse mailbox. If we had more energy,
- * we could lazily do so, assuming someone
- * else might get to userland earlier
- * and deliver it earlier than we could.
- * To do that we could save it off the KSEG.
- * An upcalling KSE would 'reap' all completed
- * threads.
- * Being in a hurry, we'll do nothing and
- * leave it on the current KSE for now.
- *
- * As for the other threads to run;
- * we COULD rush through all the threads
- * in this KSEG at this priority, or we
- * could throw the ball back into the court
- * and just run the highest prio kse available.
- * What is OUR priority?
- * the priority of the highest sycall waiting
- * to be returned?
- * For now, just let another KSE run (easiest).
- */
- PROC_LOCK(p);
- mtx_lock_spin(&sched_lock);
- thread_exit(); /* Abandon current thread. */
- /* NOTREACHED */
- } else { /* if (number of returning syscalls = 1) */
- /*
- * Swap our frame for the upcall frame.
- *
- * XXXKSE Assumes we are going to user land
- * and not nested in the kernel
- */
- td->td_flags |= TDF_UPCALLING;
- }
- }
+ goto cont;
+ }
+ if (td->td_flags & TDF_UPCALLING) {
/*
- * This is NOT just an 'else' clause for the above test...
+ * There is no more work to do and we are going to ride
+ * this thead/KSE up to userland.
*/
- if (td->td_flags & TDF_UPCALLING) {
- CTR3(KTR_PROC, "userret: upcall thread %p (pid %d, %s)",
- td, p->p_pid, p->p_comm);
- /*
- * Make sure that it has the correct frame loaded.
- * While we know that we are on the same KSEGRP
- * as we were created on, we could very easily
- * have come in on another KSE. We therefore need
- * to do the copy of the frame after the last
- * possible switch() (the one above).
- */
- bcopy(ke->ke_frame, frame, sizeof(struct trapframe));
+ CTR3(KTR_PROC, "userret: upcall thread %p (pid %d, %s)",
+ td, p->p_pid, p->p_comm);
+ /*
+ * Set user context to the UTS.
+ */
+ cpu_set_upcall_kse(td, ke);
+ if (error)
/*
- * Decide what we are sending to the user
- * upcall sets one argument. The address of the mbox.
+ * Failing to do the KSE operation just defaults
+ * back to synchonous operation, so just return from
+ * the syscall.
*/
- cpu_set_args(td, ke);
+ goto cont;
- /*
- * There is no more work to do and we are going to ride
- * this thead/KSE up to userland. Make sure the user's
- * pointer to the thread mailbox is cleared before we
- * re-enter the kernel next time for any reason..
- * We might as well do it here.
- */
- td->td_flags &= ~TDF_UPCALLING; /* Hmmmm. */
- error = suword((caddr_t)td->td_kse->ke_mailbox +
- offsetof(struct kse_mailbox, kmbx_current_thread),
- 0);
- }
/*
- * Stop any chance that we may be separated from
- * the KSE we are currently on. This is "biting the bullet",
- * we are committing to go to user space as as THIS KSE here.
+ * Set state and mailbox.
*/
-cont:
- td->td_flags &= ~TDF_UNBOUND;
+ td->td_flags &= ~TDF_UPCALLING;
+ error = suword((caddr_t)td->td_kse->ke_mailbox +
+ offsetof(struct kse_mailbox, km_curthread),
+ 0);
}
+cont:
+ /*
+ * Stop any chance that we may be separated from
+ * the KSE we are currently on. This is "biting the bullet",
+ * we are committing to go to user space as as this KSE here.
+ */
+ td->td_flags &= ~TDF_UNBOUND; /* Bind to this user thread. */
return (error);
}
OpenPOWER on IntegriCloud