- Split the struct kse into struct upcall and struct kse. struct kse will

soon be visible only to schedulers.  This greatly simplifies much the
   KSE code.

Submitted by:	davidxu
This commit is contained in:
Jeff Roberson 2003-02-17 05:14:26 +00:00
parent 50cc4892cb
commit 5215b1872f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=111028
19 changed files with 1433 additions and 1338 deletions

View File

@ -309,7 +309,7 @@ cpu_set_upcall(struct thread *td, void *pcb)
}
void
cpu_set_upcall_kse(struct thread *td, struct kse *ke)
cpu_set_upcall_kse(struct thread *td, struct kse_upcall *ku)
{
/* XXX */

View File

@ -312,8 +312,6 @@ cpu_set_upcall(struct thread *td, void *pcb)
{
struct pcb *pcb2;
td->td_flags |= TDF_UPCALLING;
/* Point the pcb to the top of the stack. */
pcb2 = td->td_pcb;
@ -370,7 +368,7 @@ cpu_set_upcall(struct thread *td, void *pcb)
* in thread_userret() itself can be done as well.
*/
void
cpu_set_upcall_kse(struct thread *td, struct kse *ke)
cpu_set_upcall_kse(struct thread *td, struct kse_upcall *ku)
{
/*
@ -387,15 +385,15 @@ cpu_set_upcall_kse(struct thread *td, struct kse *ke)
* function.
*/
td->td_frame->tf_esp =
(int)ke->ke_stack.ss_sp + ke->ke_stack.ss_size - 16;
td->td_frame->tf_eip = (int)ke->ke_upcall;
(int)ku->ku_stack.ss_sp + ku->ku_stack.ss_size - 16;
td->td_frame->tf_eip = (int)ku->ku_func;
/*
* Pass the address of the mailbox for this kse to the uts
* function as a parameter on the stack.
*/
suword((void *)(td->td_frame->tf_esp + sizeof(void *)),
(int)ke->ke_mailbox);
(int)ku->ku_mailbox);
}
void

View File

@ -170,15 +170,6 @@ dumpthread(volatile struct proc *p, volatile struct thread *td)
if (TD_AWAITING_INTR(td)) {
db_printf("[IWAIT]");
}
if (TD_LENDER(td)) {
db_printf("[LOAN]");
}
if (TD_IS_IDLE(td)) {
db_printf("[IDLE]");
}
if (TD_IS_EXITING(td)) {
db_printf("[EXIT]");
}
break;
case TDS_CAN_RUN:
db_printf("[Can run]");

View File

@ -312,8 +312,6 @@ cpu_set_upcall(struct thread *td, void *pcb)
{
struct pcb *pcb2;
td->td_flags |= TDF_UPCALLING;
/* Point the pcb to the top of the stack. */
pcb2 = td->td_pcb;
@ -370,7 +368,7 @@ cpu_set_upcall(struct thread *td, void *pcb)
* in thread_userret() itself can be done as well.
*/
void
cpu_set_upcall_kse(struct thread *td, struct kse *ke)
cpu_set_upcall_kse(struct thread *td, struct kse_upcall *ku)
{
/*
@ -387,15 +385,15 @@ cpu_set_upcall_kse(struct thread *td, struct kse *ke)
* function.
*/
td->td_frame->tf_esp =
(int)ke->ke_stack.ss_sp + ke->ke_stack.ss_size - 16;
td->td_frame->tf_eip = (int)ke->ke_upcall;
(int)ku->ku_stack.ss_sp + ku->ku_stack.ss_size - 16;
td->td_frame->tf_eip = (int)ku->ku_func;
/*
* Pass the address of the mailbox for this kse to the uts
* function as a parameter on the stack.
*/
suword((void *)(td->td_frame->tf_esp + sizeof(void *)),
(int)ke->ke_mailbox);
(int)ku->ku_mailbox);
}
void

View File

@ -117,7 +117,7 @@ cpu_set_upcall(struct thread *td, void *pcb)
}
void
cpu_set_upcall_kse(struct thread *td, struct kse *ke)
cpu_set_upcall_kse(struct thread *td, struct kse_upcall *ku)
{
}

View File

@ -379,7 +379,6 @@ proc0_init(void *dummy __unused)
ke->ke_oncpu = 0;
ke->ke_state = KES_THREAD;
ke->ke_thread = td;
ke->ke_owner = td;
p->p_peers = 0;
p->p_leader = p;

View File

@ -375,7 +375,7 @@ statclock(frame)
* Charge the time as appropriate.
*/
if (p->p_flag & P_KSES)
thread_add_ticks_intr(1, 1);
thread_statclock(1);
p->p_uticks++;
if (ke->ke_ksegrp->kg_nice > NZERO)
cp_time[CP_NICE]++;
@ -399,7 +399,7 @@ statclock(frame)
cp_time[CP_INTR]++;
} else {
if (p->p_flag & P_KSES)
thread_add_ticks_intr(0, 1);
thread_statclock(0);
td->td_sticks++;
p->p_sticks++;
if (p != PCPU_GET(idlethread)->td_proc)

View File

@ -210,10 +210,7 @@ kern_execve(td, fname, argv, envv, mac_p)
* so unset the associated flags and lose KSE mode.
*/
p->p_flag &= ~P_KSES;
td->td_flags &= ~TDF_UNBOUND;
td->td_mailbox = NULL;
td->td_kse->ke_mailbox = NULL;
td->td_kse->ke_flags &= ~KEF_DOUPCALL;
thread_single_end();
}
p->p_flag |= P_INEXEC;

View File

@ -147,7 +147,7 @@ exit1(td, rv)
}
/*
* XXXXKSE: MUST abort all other threads before proceeding past here.
* XXXKSE: MUST abort all other threads before proceeding past here.
*/
PROC_LOCK(p);
if (p->p_flag & P_KSES) {
@ -156,17 +156,6 @@ exit1(td, rv)
* if so, act apropriatly, (exit or suspend);
*/
thread_suspend_check(0);
/*
* Here is a trick..
* We need to free up our KSE to process other threads
* so that we can safely set the UNBOUND flag
* (whether or not we have a mailbox) as we are NEVER
* going to return to the user.
* The flag will not be set yet if we are exiting
* because of a signal, pagefault, or similar
* (or even an exit(2) from the UTS).
*/
td->td_flags |= TDF_UNBOUND;
/*
* Kill off the other threads. This requires
@ -192,7 +181,6 @@ exit1(td, rv)
* Turn off threading support.
*/
p->p_flag &= ~P_KSES;
td->td_flags &= ~TDF_UNBOUND;
thread_single_end(); /* Don't need this any more. */
}
/*

View File

@ -499,9 +499,7 @@ fork1(td, flags, pages, procp)
/* Set up the thread as an active thread (as if runnable). */
ke2->ke_state = KES_THREAD;
ke2->ke_thread = td2;
ke2->ke_owner = td2;
td2->td_kse = ke2;
td2->td_flags &= ~TDF_UNBOUND; /* For the rest of this syscall. */
/*
* Duplicate sub-structures as needed.

File diff suppressed because it is too large Load Diff

View File

@ -1506,9 +1506,6 @@ psignal(p, sig)
if (TD_IS_SLEEPING(td) &&
(td->td_flags & TDF_SINTR))
thread_suspend_one(td);
else if (TD_IS_IDLE(td)) {
thread_suspend_one(td);
}
}
if (p->p_suspcount == p->p_numthreads) {
mtx_unlock_spin(&sched_lock);
@ -1621,9 +1618,6 @@ tdsignal(struct thread *td, int sig, sig_t action)
cv_abort(td);
else
abortsleep(td);
} else if (TD_IS_IDLE(td)) {
TD_CLR_IDLE(td);
setrunnable(td);
}
#ifdef SMP
else {

View File

@ -111,7 +111,7 @@ static void runq_readjust(struct runq *rq, struct kse *ke);
* Functions that manipulate runnability from a thread perspective. *
************************************************************************/
/*
* Select the KSE that will be run next. From that find the thread, and x
* Select the KSE that will be run next. From that find the thread, and
* remove it from the KSEGRP's run queue. If there is thread clustering,
* this will be what does it.
*/
@ -127,7 +127,7 @@ choosethread(void)
td = ke->ke_thread;
KASSERT((td->td_kse == ke), ("kse/thread mismatch"));
kg = ke->ke_ksegrp;
if (TD_IS_UNBOUND(td)) {
if (td->td_proc->p_flag & P_KSES) {
TAILQ_REMOVE(&kg->kg_runq, td, td_runq);
if (kg->kg_last_assigned == td) {
kg->kg_last_assigned = TAILQ_PREV(td,
@ -158,9 +158,8 @@ choosethread(void)
}
/*
* Given a KSE (now surplus or at least loanable), either assign a new
* runable thread to it (and put it in the run queue) or put it in
* the ksegrp's idle KSE list.
* Given a surplus KSE, either assign a new runable thread to it
* (and put it in the run queue) or put it in the ksegrp's idle KSE list.
* Or maybe give it back to its owner if it's been loaned.
* Assumes that the original thread is either not runnable or
* already on the run queue
@ -170,108 +169,54 @@ kse_reassign(struct kse *ke)
{
struct ksegrp *kg;
struct thread *td;
struct thread *owner;
struct thread *original;
int loaned;
struct kse_upcall *ku;
KASSERT((ke->ke_owner), ("reassigning KSE with no owner"));
KASSERT((ke->ke_thread && TD_IS_INHIBITED(ke->ke_thread)),
("reassigning KSE with no or runnable thread"));
mtx_assert(&sched_lock, MA_OWNED);
kg = ke->ke_ksegrp;
owner = ke->ke_owner;
loaned = TD_LENDER(owner);
original = ke->ke_thread;
if (TD_CAN_UNBIND(original) && (original->td_standin)) {
KASSERT((owner == original),
("Early thread borrowing?"));
KASSERT(original == NULL || TD_IS_INHIBITED(original),
("reassigning KSE with runnable thread"));
kg = ke->ke_ksegrp;
if (original) {
/*
* The outgoing thread is "threaded" and has never
* scheduled an upcall.
* decide whether this is a short or long term event
* and thus whether or not to schedule an upcall.
* if it is a short term event, just suspend it in
* If the outgoing thread is in threaded group and has never
* scheduled an upcall, decide whether this is a short
* or long term event and thus whether or not to schedule
* an upcall.
* If it is a short term event, just suspend it in
* a way that takes its KSE with it.
* Select the events for which we want to schedule upcalls.
* For now it's just sleep.
* Other threads that still have not fired an upcall
* are held to their KSE using the temorary Binding.
* XXXKSE eventually almost any inhibition could do.
*/
if (TD_ON_SLEEPQ(original)) {
/*
* An bound thread that can still unbind itself
* has been scheduled out.
* If it is sleeping, then we need to schedule an
* upcall.
* XXXKSE eventually almost any inhibition could do.
if (TD_CAN_UNBIND(original) && (original->td_standin) &&
TD_ON_SLEEPQ(original)) {
/*
* Release ownership of upcall, and schedule an upcall
* thread, this new upcall thread becomes the owner of
* the upcall structure.
*/
ku = original->td_upcall;
ku->ku_owner = NULL;
original->td_upcall = NULL;
original->td_flags &= ~TDF_CAN_UNBIND;
original->td_flags |= TDF_UNBOUND;
thread_schedule_upcall(original, ke);
owner = ke->ke_owner;
loaned = 1;
thread_schedule_upcall(original, ku);
}
original->td_kse = NULL;
}
/*
* If the current thread was borrowing, then make things consistent
* by giving it back to the owner for the moment. The original thread
* must be unbound and have already used its chance for
* firing off an upcall. Threads that have not yet made an upcall
* can not borrow KSEs.
*/
if (loaned) {
TD_CLR_LOAN(owner);
ke->ke_thread = owner;
original->td_kse = NULL; /* give it amnesia */
/*
* Upcalling threads have lower priority than all
* in-kernel threads, However threads that have loaned out
* their KSE and are NOT upcalling have the priority that
* they have. In other words, only look for other work if
* the owner is not runnable, OR is upcalling.
*/
if (TD_CAN_RUN(owner) &&
((owner->td_flags & TDF_UPCALLING) == 0)) {
setrunnable(owner);
CTR2(KTR_RUNQ, "kse_reassign: ke%p -> td%p (give back)",
ke, owner);
return;
}
}
/*
* Either the owner is not runnable, or is an upcall.
* Find the first unassigned thread
* If there is a 'last assigned' then see what's next.
* otherwise look at what is first.
*/
if ((td = kg->kg_last_assigned)) {
if ((td = kg->kg_last_assigned) != NULL)
td = TAILQ_NEXT(td, td_runq);
} else {
else
td = TAILQ_FIRST(&kg->kg_runq);
}
/*
* If we found one assign it the kse, otherwise idle the kse.
* If we found one, assign it the kse, otherwise idle the kse.
*/
if (td) {
/*
* Assign the new thread to the KSE.
* and make the KSE runnable again,
*/
if (TD_IS_BOUND(owner)) {
/*
* If there is a reason to keep the previous
* owner, do so.
*/
TD_SET_LOAN(owner);
} else {
/* otherwise, cut it free */
ke->ke_owner = td;
owner->td_kse = NULL;
}
kg->kg_last_assigned = td;
td->td_kse = ke;
ke->ke_thread = td;
@ -280,43 +225,11 @@ kse_reassign(struct kse *ke)
return;
}
/*
* Now handle any waiting upcall.
* Since we didn't make them runnable before.
*/
if (TD_CAN_RUN(owner)) {
setrunnable(owner);
CTR2(KTR_RUNQ, "kse_reassign: ke%p -> td%p (give back)",
ke, owner);
return;
}
/*
* It is possible that this is the last thread in the group
* because the KSE is being shut down or the process
* is exiting.
*/
if (TD_IS_EXITING(owner) || (ke->ke_flags & KEF_EXIT)) {
ke->ke_thread = NULL;
owner->td_kse = NULL;
kse_unlink(ke);
return;
}
/*
* At this stage all we know is that the owner
* is the same as the 'active' thread in the KSE
* and that it is
* Presently NOT loaned out.
* Put it on the loanable queue. Make it fifo
* so that long term sleepers donate their KSE's first.
*/
KASSERT((TD_IS_BOUND(owner)), ("kse_reassign: UNBOUND lender"));
ke->ke_state = KES_THREAD;
ke->ke_flags |= KEF_ONLOANQ;
TAILQ_INSERT_TAIL(&kg->kg_lq, ke, ke_kgrlist);
kg->kg_loan_kses++;
CTR1(KTR_RUNQ, "kse_reassign: ke%p on loan queue", ke);
ke->ke_state = KES_IDLE;
ke->ke_thread = NULL;
TAILQ_INSERT_TAIL(&kg->kg_iq, ke, ke_kgrlist);
kg->kg_idle_kses++;
CTR1(KTR_RUNQ, "kse_reassign: ke%p on idle queue", ke);
return;
}
@ -325,7 +238,7 @@ kse_reassign(struct kse *ke)
* Remove a thread from its KSEGRP's run queue.
* This in turn may remove it from a KSE if it was already assigned
* to one, possibly causing a new thread to be assigned to the KSE
* and the KSE getting a new priority (unless it's a BOUND thread/KSE pair).
* and the KSE getting a new priority.
*/
static void
remrunqueue(struct thread *td)
@ -335,17 +248,16 @@ remrunqueue(struct thread *td)
struct kse *ke;
mtx_assert(&sched_lock, MA_OWNED);
KASSERT ((TD_ON_RUNQ(td)), ("remrunqueue: Bad state on run queue"));
KASSERT((TD_ON_RUNQ(td)), ("remrunqueue: Bad state on run queue"));
kg = td->td_ksegrp;
ke = td->td_kse;
/*
* If it's a bound thread/KSE pair, take the shortcut. All non-KSE
* threads are BOUND.
*/
CTR1(KTR_RUNQ, "remrunqueue: td%p", td);
kg->kg_runnable--;
TD_SET_CAN_RUN(td);
if (TD_IS_BOUND(td)) {
/*
* If it is not a threaded process, take the shortcut.
*/
if ((td->td_proc->p_flag & P_KSES) == 0) {
/* Bring its kse with it, leave the thread attached */
sched_rem(ke);
ke->ke_state = KES_THREAD;
@ -363,7 +275,7 @@ remrunqueue(struct thread *td)
sched_rem(ke);
ke->ke_state = KES_THREAD;
td2 = kg->kg_last_assigned;
KASSERT((td2 != NULL), ("last assigned has wrong value "));
KASSERT((td2 != NULL), ("last assigned has wrong value"));
if (td2 == td)
kg->kg_last_assigned = td3;
kse_reassign(ke);
@ -381,14 +293,14 @@ adjustrunqueue( struct thread *td, int newpri)
struct kse *ke;
mtx_assert(&sched_lock, MA_OWNED);
KASSERT ((TD_ON_RUNQ(td)), ("adjustrunqueue: Bad state on run queue"));
/*
* If it's a bound thread/KSE pair, take the shortcut. All non-KSE
* threads are BOUND.
*/
KASSERT((TD_ON_RUNQ(td)), ("adjustrunqueue: Bad state on run queue"));
ke = td->td_kse;
CTR1(KTR_RUNQ, "adjustrunqueue: td%p", td);
if (TD_IS_BOUND(td)) {
/*
* If it is not a threaded process, take the shortcut.
*/
if ((td->td_proc->p_flag & P_KSES) == 0) {
/* We only care about the kse in the run queue. */
td->td_priority = newpri;
if (ke->ke_rqindex != (newpri / RQ_PPQ)) {
@ -397,9 +309,8 @@ adjustrunqueue( struct thread *td, int newpri)
}
return;
}
/*
* An unbound thread. This is not optimised yet.
*/
/* It is a threaded process */
kg = td->td_ksegrp;
kg->kg_runnable--;
TD_SET_CAN_RUN(td);
@ -439,48 +350,17 @@ setrunqueue(struct thread *td)
sched_add(td->td_kse);
return;
}
/*
* If the process is threaded but the thread is bound then
* there is still a little extra to do re. KSE loaning.
*/
if (TD_IS_BOUND(td)) {
KASSERT((td->td_kse != NULL),
("queueing BAD thread to run queue"));
ke = td->td_kse;
KASSERT((ke->ke_owner == ke->ke_thread),
("setrunqueue: Hey KSE loaned out"));
if (ke->ke_flags & KEF_ONLOANQ) {
ke->ke_flags &= ~KEF_ONLOANQ;
TAILQ_REMOVE(&kg->kg_lq, ke, ke_kgrlist);
kg->kg_loan_kses--;
}
sched_add(td->td_kse);
return;
}
/*
* Ok, so we are threading with this thread.
* We don't have a KSE, see if we can get one..
*/
tda = kg->kg_last_assigned;
if ((ke = td->td_kse) == NULL) {
/*
* We will need a KSE, see if there is one..
* First look for a free one, before getting desperate.
* If we can't get one, our priority is not high enough..
* that's ok..
*/
if (kg->kg_loan_kses) {
if (kg->kg_idle_kses) {
/*
* Failing that see if we can borrow one.
* There is a free one so it's ours for the asking..
*/
ke = TAILQ_FIRST(&kg->kg_lq);
TAILQ_REMOVE(&kg->kg_lq, ke, ke_kgrlist);
ke->ke_flags &= ~KEF_ONLOANQ;
ke = TAILQ_FIRST(&kg->kg_iq);
TAILQ_REMOVE(&kg->kg_iq, ke, ke_kgrlist);
ke->ke_state = KES_THREAD;
TD_SET_LOAN(ke->ke_owner);
ke->ke_thread = NULL;
kg->kg_loan_kses--;
kg->kg_idle_kses--;
} else if (tda && (tda->td_priority > td->td_priority)) {
/*
* None free, but there is one we can commandeer.
@ -495,11 +375,7 @@ setrunqueue(struct thread *td)
} else {
/*
* Temporarily disassociate so it looks like the other cases.
* If the owner wasn't lending before, then it is now..
*/
if (!TD_LENDER(ke->ke_owner)) {
TD_SET_LOAN(ke->ke_owner);
}
ke->ke_thread = NULL;
td->td_kse = NULL;
}
@ -831,6 +707,7 @@ thread_sanity_check(struct thread *td, char *string)
if (kg->kg_last_assigned && (saw_lastassigned == 0)) {
panc(string, "where on earth does lastassigned point?");
}
#if 0
FOREACH_THREAD_IN_GROUP(kg, td2) {
if (((td2->td_flags & TDF_UNBOUND) == 0) &&
(TD_ON_RUNQ(td2))) {
@ -840,6 +717,7 @@ thread_sanity_check(struct thread *td, char *string)
}
}
}
#endif
#if 0
if ((unassigned + assigned) != kg->kg_runnable) {
panc(string, "wrong number in runnable");

File diff suppressed because it is too large Load Diff

View File

@ -254,7 +254,7 @@ static struct witness_order_list_entry order_lists[] = {
#endif
{ "clk", &lock_class_mtx_spin },
{ "mutex profiling lock", &lock_class_mtx_spin },
{ "zombie_thread_lock", &lock_class_mtx_spin },
{ "kse zombie lock", &lock_class_mtx_spin },
{ "ALD Queue", &lock_class_mtx_spin },
#ifdef __ia64__
{ "MCA spin lock", &lock_class_mtx_spin },

View File

@ -285,7 +285,7 @@ cpu_set_upcall(struct thread *td, void *pcb)
}
void
cpu_set_upcall_kse(struct thread *td, struct kse *ke)
cpu_set_upcall_kse(struct thread *td, struct kse_upcall *ku)
{
return;

View File

@ -285,7 +285,7 @@ cpu_set_upcall(struct thread *td, void *pcb)
}
void
cpu_set_upcall_kse(struct thread *td, struct kse *ke)
cpu_set_upcall_kse(struct thread *td, struct kse_upcall *ku)
{
return;

View File

@ -141,7 +141,7 @@ cpu_set_upcall(struct thread *td, void *pcb)
}
void
cpu_set_upcall_kse(struct thread *td, struct kse *ke)
cpu_set_upcall_kse(struct thread *td, struct kse_upcall *ku)
{
}

View File

@ -258,7 +258,7 @@ They would be given priorities calculated from the KSEG.
* This is what is put to sleep and reactivated.
* The first KSE available in the correct group will run this thread.
* If several are available, use the one on the same CPU as last time.
* When wating to be run, threads are hung off the KSEGRP in priority order.
* When waiting to be run, threads are hung off the KSEGRP in priority order.
* with N runnable and queued KSEs in the KSEGRP, the first N threads
* are linked to them. Other threads are not yet assigned.
*/
@ -298,8 +298,10 @@ struct thread {
struct ucred *td_ucred; /* (k) Reference to credentials. */
void (*td_switchin)(void); /* (k) Switchin special func. */
struct thread *td_standin; /* (?) Use this for an upcall */
struct kse_upcall *td_upcall; /* our upcall structure. */
u_int64_t td_sticks; /* (j) Statclock hits in system mode. */
u_int td_usticks; /* (?) Statclock kernel hits, for UTS */
u_int td_uuticks; /* Statclock hits in user, for UTS */
u_int td_usticks; /* Statclock hits in kernel, for UTS */
u_int td_critnest; /* (k) Critical section nest level. */
#define td_endzero td_base_pri
@ -334,7 +336,6 @@ struct thread {
struct td_sched *td_sched; /* Scheduler specific data */
};
/* flags kept in td_flags */
#define TDF_UNBOUND 0x000001 /* May give away the kse, uses the kg runq. */
#define TDF_INPANIC 0x000002 /* Caused a panic, let it drive crashdump. */
#define TDF_CAN_UNBIND 0x000004 /* Only temporarily bound. */
#define TDF_SINTR 0x000008 /* Sleep is interruptible. */
@ -346,6 +347,7 @@ struct thread {
#define TDF_INMSLEEP 0x000400 /* Don't recurse in msleep(). */
#define TDF_TIMOFAIL 0x001000 /* Timeout from sleep after we were awake. */
#define TDF_INTERRUPT 0x002000 /* Thread is marked as interrupted. */
#define TDF_USTATCLOCK 0x004000 /* Stat clock hits in userland. */
#define TDF_DEADLKTREAT 0x800000 /* Lock aquisition - deadlock treatment. */
#define TDI_SUSPENDED 0x0001 /* On suspension queue. */
@ -353,25 +355,17 @@ struct thread {
#define TDI_SWAPPED 0x0004 /* Stack not in mem.. bad juju if run. */
#define TDI_LOCK 0x0008 /* Stopped on a lock. */
#define TDI_IWAIT 0x0010 /* Awaiting interrupt. */
#define TDI_LOAN 0x0020 /* bound thread's KSE is lent */
#define TDI_IDLE 0x0040 /* kse_release() made us surplus */
#define TDI_EXITING 0x0080 /* Thread is in exit processing */
#define TD_IS_UNBOUND(td) ((td)->td_flags & TDF_UNBOUND)
#define TD_IS_BOUND(td) (!TD_IS_UNBOUND(td))
#define TD_CAN_UNBIND(td) \
(((td)->td_flags & (TDF_UNBOUND|TDF_CAN_UNBIND)) == TDF_CAN_UNBIND)
#define TD_CAN_UNBIND(td) \
(((td)->td_flags & TDF_CAN_UNBIND) == TDF_CAN_UNBIND && \
((td)->td_upcall != NULL))
#define TD_IS_SLEEPING(td) ((td)->td_inhibitors & TDI_SLEEPING)
#define TD_ON_SLEEPQ(td) ((td)->td_wchan != NULL)
#define TD_IS_SUSPENDED(td) ((td)->td_inhibitors & TDI_SUSPENDED)
#define TD_IS_SWAPPED(td) ((td)->td_inhibitors & TDI_SWAPPED)
#define TD_ON_LOCK(td) ((td)->td_inhibitors & TDI_LOCK)
#define TD_LENDER(td) ((td)->td_inhibitors & TDI_LOAN)
#define TD_AWAITING_INTR(td) ((td)->td_inhibitors & TDI_IWAIT)
#define TD_IS_IDLE(td) ((td)->td_inhibitors & TDI_IDLE)
#define TD_IS_EXITING(td) ((td)->td_inhibitors & TDI_EXITING)
#define TD_IS_RUNNING(td) ((td)->td_state == TDS_RUNNING)
#define TD_ON_RUNQ(td) ((td)->td_state == TDS_RUNQ)
#define TD_CAN_RUN(td) ((td)->td_state == TDS_CAN_RUN)
@ -379,12 +373,12 @@ struct thread {
#define TD_SET_INHIB(td, inhib) do { \
(td)->td_state = TDS_INHIBITED; \
(td)->td_inhibitors |= inhib; \
(td)->td_inhibitors |= (inhib); \
} while (0)
#define TD_CLR_INHIB(td, inhib) do { \
if (((td)->td_inhibitors & inhib) && \
(((td)->td_inhibitors &= ~inhib) == 0)) \
if (((td)->td_inhibitors & (inhib)) && \
(((td)->td_inhibitors &= ~(inhib)) == 0)) \
(td)->td_state = TDS_CAN_RUN; \
} while (0)
@ -393,8 +387,6 @@ struct thread {
#define TD_SET_LOCK(td) TD_SET_INHIB((td), TDI_LOCK)
#define TD_SET_SUSPENDED(td) TD_SET_INHIB((td), TDI_SUSPENDED)
#define TD_SET_IWAIT(td) TD_SET_INHIB((td), TDI_IWAIT)
#define TD_SET_LOAN(td) TD_SET_INHIB((td), TDI_LOAN)
#define TD_SET_IDLE(td) TD_SET_INHIB((td), TDI_IDLE)
#define TD_SET_EXITING(td) TD_SET_INHIB((td), TDI_EXITING)
#define TD_CLR_SLEEPING(td) TD_CLR_INHIB((td), TDI_SLEEPING)
@ -402,8 +394,6 @@ struct thread {
#define TD_CLR_LOCK(td) TD_CLR_INHIB((td), TDI_LOCK)
#define TD_CLR_SUSPENDED(td) TD_CLR_INHIB((td), TDI_SUSPENDED)
#define TD_CLR_IWAIT(td) TD_CLR_INHIB((td), TDI_IWAIT)
#define TD_CLR_LOAN(td) TD_CLR_INHIB((td), TDI_LOAN)
#define TD_CLR_IDLE(td) TD_CLR_INHIB((td), TDI_IDLE)
#define TD_SET_RUNNING(td) do {(td)->td_state = TDS_RUNNING; } while (0)
#define TD_SET_RUNQ(td) do {(td)->td_state = TDS_RUNQ; } while (0)
@ -414,16 +404,6 @@ struct thread {
(td)->td_wchan = NULL; \
} while (0)
/*
* Traps for young players:
* The main thread variable that controls whether a thread acts as a threaded
* or unthreaded thread is the TDF_UNBOUND flag.
* i.e. they bind themselves to whatever thread thay are first scheduled with.
* You may see BOUND threads in KSE processes but you should never see
* UNBOUND threads in non KSE processes.
*/
/*
* The schedulable entity that can be given a context to run.
* A process may have several of these. Probably one per processor
@ -441,48 +421,46 @@ struct kse {
#define ke_startzero ke_flags
int ke_flags; /* (j) KEF_* flags. */
struct thread *ke_thread; /* Active associated thread. */
struct thread *ke_owner; /* Always points to the owner */
fixpt_t ke_pctcpu; /* (j) %cpu during p_swtime. */
u_int ke_uuticks; /* Statclock hits in user, for UTS */
u_int ke_usticks; /* Statclock hits in kernel, for UTS */
u_char ke_oncpu; /* (j) Which cpu we are on. */
char ke_rqindex; /* (j) Run queue index. */
enum {
KES_UNUSED = 0x0,
KES_IDLE,
KES_ONRUNQ,
KES_UNQUEUED, /* in transit */
KES_THREAD /* slaved to thread state */
} ke_state; /* (j) S* process status. */
struct kse_mailbox *ke_mailbox; /* the userland mailbox address */
stack_t ke_stack;
void *ke_upcall;
struct thread *ke_tdspare; /* spare thread for upcalls */
#define ke_endzero ke_dummy
u_char ke_dummy;
struct ke_sched *ke_sched; /* Scheduler specific data */
};
/* flags kept in ke_flags */
#define KEF_OWEUPC 0x00002 /* Owe process an addupc() call at next ast. */
#define KEF_OWEUPC 0x008000 /* Owe thread an addupc() call at next ast. */
#define KEF_IDLEKSE 0x00004 /* A 'Per CPU idle process'.. has one thread */
#define KEF_LOANED 0x00008 /* On loan from the bound thread to another */
#define KEF_USER 0x00200 /* Process is not officially in the kernel */
#define KEF_ASTPENDING 0x00400 /* KSE has a pending ast. */
#define KEF_NEEDRESCHED 0x00800 /* Process needs to yield. */
#define KEF_ONLOANQ 0x01000 /* KSE is on loan queue. */
#define KEF_DIDRUN 0x02000 /* KSE actually ran. */
#define KEF_EXIT 0x04000 /* KSE is being killed. */
#define KEF_DOUPCALL 0x08000 /* KSE should do upcall now. */
/*
* (*) A bound KSE with a bound thread in a KSE process may be lent to
* Other threads, as long as those threads do not leave the kernel.
* The other threads must be either exiting, or be unbound with a valid
* mailbox so that they can save their state there rather than going
* to user space. While this happens the real bound thread is still linked
* to the kse via the ke_bound field, and the KSE has its "KEF_LOANED
* flag set.
* The upcall management structure.
* The upcall is used when returning to userland. If a thread does not have
* an upcall on return to userland the thread exports its context and exits.
*/
struct kse_upcall {
TAILQ_ENTRY(kse_upcall) ku_link; /* List of upcalls in KSEG. */
struct ksegrp *ku_ksegrp; /* Associated KSEG. */
struct thread *ku_owner; /* owning thread */
int ku_flags; /* KUF_* flags. */
struct kse_mailbox *ku_mailbox; /* userland mailbox address. */
stack_t ku_stack; /* userland upcall stack. */
void *ku_func; /* userland upcall function. */
};
#define KUF_DOUPCALL 0x00001 /* Do upcall now, don't wait */
/*
* Kernel-scheduled entity group (KSEG). The scheduler considers each KSEG to
@ -493,18 +471,20 @@ struct ksegrp {
struct proc *kg_proc; /* Process that contains this KSEG. */
TAILQ_ENTRY(ksegrp) kg_ksegrp; /* Queue of KSEGs in kg_proc. */
TAILQ_HEAD(, kse) kg_kseq; /* (ke_kglist) All KSEs. */
TAILQ_HEAD(, kse) kg_lq; /* (ke_kgrlist) Loan KSEs. */
TAILQ_HEAD(, kse) kg_iq; /* (ke_kgrlist) All idle KSEs. */
TAILQ_HEAD(, thread) kg_threads;/* (td_kglist) All threads. */
TAILQ_HEAD(, thread) kg_runq; /* (td_runq) waiting RUNNABLE threads */
TAILQ_HEAD(, thread) kg_slpq; /* (td_runq) NONRUNNABLE threads. */
TAILQ_HEAD(, kse_upcall) kg_upcalls; /* All upcalls in the group */
#define kg_startzero kg_estcpu
u_int kg_estcpu; /* Sum of the same field in KSEs. */
u_int kg_slptime; /* (j) How long completely blocked. */
struct thread *kg_last_assigned; /* Last thread assigned to a KSE */
int kg_runnable; /* Num runnable threads on queue. */
int kg_runq_kses; /* Num KSEs on runq. */
int kg_loan_kses; /* Num KSEs on loan queue. */
struct thread *kg_last_assigned; /* (j) Last thread assigned to a KSE */
int kg_runnable; /* (j) Num runnable threads on queue. */
int kg_runq_kses; /* (j) Num KSEs on runq. */
int kg_idle_kses; /* (j) Num KSEs on iq */
int kg_numupcalls; /* (j) Num upcalls */
int kg_upsleeps; /* (c) Num threads in kse_release() */
struct kse_thr_mailbox *kg_completed; /* (c) completed thread mboxes */
#define kg_endzero kg_pri_class
@ -513,8 +493,8 @@ struct ksegrp {
u_char kg_user_pri; /* (j) User pri from estcpu and nice. */
char kg_nice; /* (j?/k?) Process "nice" value. */
#define kg_endcopy kg_numthreads
int kg_numthreads; /* Num threads in total */
int kg_kses; /* Num KSEs in group. */
int kg_numthreads; /* (j) Num threads in total */
int kg_kses; /* (j) Num KSEs in group. */
struct kg_sched *kg_sched; /* Scheduler specific data */
};
@ -709,6 +689,8 @@ MALLOC_DECLARE(M_ZOMBIE);
TAILQ_FOREACH((td), &(kg)->kg_threads, td_kglist)
#define FOREACH_KSE_IN_GROUP(kg, ke) \
TAILQ_FOREACH((ke), &(kg)->kg_kseq, ke_kglist)
#define FOREACH_UPCALL_IN_GROUP(kg, ku) \
TAILQ_FOREACH((ku), &(kg)->kg_upcalls, ku_link)
#define FOREACH_THREAD_IN_PROC(p, td) \
TAILQ_FOREACH((td), &(p)->p_threads, td_plist)
@ -923,7 +905,7 @@ struct kse *kse_alloc(void);
void kse_free(struct kse *ke);
void kse_stash(struct kse *ke);
void cpu_set_upcall(struct thread *td, void *pcb);
void cpu_set_upcall_kse(struct thread *td, struct kse *ke);
void cpu_set_upcall_kse(struct thread *td, struct kse_upcall *ku);
void cpu_thread_clean(struct thread *);
void cpu_thread_exit(struct thread *);
void cpu_thread_setup(struct thread *td);
@ -932,7 +914,6 @@ void kse_link(struct kse *ke, struct ksegrp *kg);
void kse_unlink(struct kse *ke);
void ksegrp_link(struct ksegrp *kg, struct proc *p);
void ksegrp_unlink(struct ksegrp *kg);
void make_kse_runnable(struct kse *ke);
struct thread *signal_upcall(struct proc *p, int sig);
struct thread *thread_alloc(void);
void thread_exit(void) __dead2;
@ -941,7 +922,7 @@ void thread_free(struct thread *td);
void thread_getcontext(struct thread *td, ucontext_t *uc);
void thread_link(struct thread *td, struct ksegrp *kg);
void thread_reap(void);
struct thread *thread_schedule_upcall(struct thread *td, struct kse *ke);
struct thread *thread_schedule_upcall(struct thread *td, struct kse_upcall *ku);
int thread_setcontext(struct thread *td, ucontext_t *uc);
int thread_single(int how);
#define SINGLE_NO_EXIT 0 /* values for 'how' */
@ -955,8 +936,13 @@ void thread_unsuspend_one(struct thread *td);
int thread_userret(struct thread *td, struct trapframe *frame);
void thread_user_enter(struct proc *p, struct thread *td);
void thread_wait(struct proc *p);
int thread_add_ticks_intr(int user, uint ticks);
int thread_statclock(int user);
struct kse_upcall *upcall_alloc(void);
void upcall_free(struct kse_upcall *ku);
void upcall_link(struct kse_upcall *ku, struct ksegrp *kg);
void upcall_unlink(struct kse_upcall *ku);
void upcall_remove(struct thread *td);
void upcall_stash(struct kse_upcall *ke);
void thread_sanity_check(struct thread *td, char *);
#endif /* _KERNEL */