Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 28 Aug 2002 16:59:04 -0700 (PDT)
From:      Jonathan Mini <mini@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 16716 for review
Message-ID:  <200208282359.g7SNx4Qa022840@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://people.freebsd.org/~peter/p4db/chv.cgi?CH=16716

Change 16716 by mini@mini_stylus on 2002/08/28 16:58:11

	- Invert the logic in thread_userret(), so that we don't have
	  so many nested blocks.
	- Split out the logic to determine whether we upcall on the current
	  thread or just discard it into thread_consider_upcalling().
	- Don't upcall on a thread when its UTS is busy.

Affected files ...

.. //depot/projects/kse/sys/kern/kern_thread.c#92 edit

Differences ...

==== //depot/projects/kse/sys/kern/kern_thread.c#92 (text+ko) ====

@@ -483,145 +483,194 @@
 }
 
 /*
- * The extra work we go through if we are a threaded process when we 
- * return to userland
+ * 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 discard, 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);
+
+	/*
+	 * Second, decide whether to perfom an upcall now.
+	 */
+	discard = 0;
+
+	/* 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.
+		 *
+		 * 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).
+		 */
+		discard = 1;
+
+	/* Make sure the KSE's UTS context is free for use. */
+	if (fuword((caddr_t)ke->ke_mailbox +
+	    offsetof(struct kse_mailbox, km_context) +
+	    offsetof(ucontext_t, uc_busy)) != 0)
+		/*
+		 * The KSE's UTS context is currently marked busy. This
+		 * means the UTS is currently running, so switch to it
+		 * instead of performing another upcall (abandon this
+		 * thread).
+		 */
+		discard = 1;
+
+	/* Discard thread or mark for upcall. */
+	if (discard) {
+		/*
+		 * Perform the upcall later; discard this thread for
+		 * now.
+		 *
+		 * 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 equal the
+ * struct thread address).  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;
 	caddr_t ucp;
 	ucontext_t uc;
 
+	/*
+	 * Ensure that we have a spare thread available.
+	 */
 	if (ke->ke_tdspare == NULL) {
 		ke->ke_tdspare = thread_alloc();
 	}
-	if (td->td_flags & TDF_UNBOUND) {
+
+	/*
+	 * 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)
+			goto cont;
+	}
+	if (td->td_flags & TDF_UPCALLING) {
 		/*
-		 * Are we returning from a thread that had a mailbox?
-		 *
-		 * XXX Maybe this should be in a separate function.
+		 * 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) == 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.
-			 */
-			/*
-			 * 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.
-			 */
-			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;
-			}
+		CTR3(KTR_PROC, "userret: upcall thread %p (pid %d, %s)",
+		    td, p->p_pid, p->p_comm);
 
-			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;
-			}
-		}
 		/*
-		 * This is NOT just an 'else' clause for the above test...
+		 * Fetch the current UTS context from userland.
 		 */
-		if (td->td_flags & TDF_UPCALLING) {
-			CTR3(KTR_PROC, "userret: upcall thread %p (pid %d, %s)",
-			    td, p->p_pid, p->p_comm);
+		ucp = (caddr_t)ke->ke_mailbox +
+		    offsetof(struct kse_mailbox, km_context);
+		error = copyin(ucp, &uc, sizeof(ucontext_t));
+		if (error)
 			/*
-			 * There is no more work to do and we are going to ride
-			 * this thead/KSE up to userland.
+			 * Failing to do the KSE operation just defaults
+			 * back to synchonous operation, so just return from
+			 * the syscall.
 			 */
+			goto cont;
 
-			/*
-			 * Set user/machine context to the UTS.
-			 */
-			ucp = (caddr_t)ke->ke_mailbox +
-			    offsetof(struct kse_mailbox, km_context);
-			if (copyin(ucp, &uc, sizeof(ucontext_t)) != 0)
-				/*
-				 * If copyin() fails here, act as if it had
-				 * read nothing but nulls. This will cause
-				 * the process to crash from the bogus state
-				 * information.
-				 */
-				bzero(&uc, sizeof(ucontext_t));
-			thread_setcontext(td, &uc);
-			/* Mark the context busy (XXXSMP: must be atomic). */
-			suword(ucp + offsetof(ucontext_t, uc_busy), 1);
+		/*
+		 * Set user context to the UTS's.
+		 *
+		 * XXX - Add the busy marker to thread_setcontext().
+		 */
+		thread_setcontext(td, &uc);
+		suword(ucp + offsetof(ucontext_t, uc_busy), (intptr_t)td);
 
-			/*
-			 * Make sure the user's pointer to the thread
-			 * mailbox is cleared before we re-enter the kernel
-			 * next time for any reason..
-			 */
-			td->td_flags &= ~TDF_UPCALLING;	/* Hmmmm. */
-			error = suword((caddr_t)td->td_kse->ke_mailbox +
-			    offsetof(struct kse_mailbox, km_curthread),
-			    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.
 		 */
+		td->td_flags &= ~TDF_UPCALLING;
+		error = suword((caddr_t)td->td_kse->ke_mailbox +
+		    offsetof(struct kse_mailbox, km_curthread),
+		    0);
+	}
 cont:
-		td->td_flags &= ~TDF_UNBOUND;
-	}
+	/*
+	 * 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);
 }
 

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe p4-projects" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200208282359.g7SNx4Qa022840>