Comment kse_create() and make a few minor code cleanups

Reviewed by:	davidxu
This commit is contained in:
Julian Elischer 2004-08-01 23:02:00 +00:00
parent 1903a591c9
commit 6e0fbb01c5
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=132985

View file

@ -482,12 +482,19 @@ kse_create(struct thread *td, struct kse_create_args *uap)
ncpus = mp_ncpus;
if (virtual_cpu != 0)
ncpus = virtual_cpu;
if (!(mbx.km_flags & KMF_BOUND))
sa = TDP_SA;
else {
if (mbx.km_curthread == NULL)
/*
* If the new UTS mailbox says that this
* will be a BOUND lwp, then it had better
* have its thread mailbox already there.
* In addition, this ksegrp will be limited to
* a concurrency of 1. There is more on this later.
*/
if (mbx.km_flags & KMF_BOUND) {
if (mbx.km_curthread == NULL)
return (EINVAL);
ncpus = 1;
} else {
sa = TDP_SA;
}
PROC_LOCK(p);
@ -496,18 +503,21 @@ kse_create(struct thread *td, struct kse_create_args *uap)
p->p_flag |= P_SA;
}
PROC_UNLOCK(p);
if (!sa && !uap->newgroup && !first)
/*
* Now pay attention!
* If we are going to be bound, then we need to be either
* a new group, or the first call ever. In either
* case we will be creating (or be) the only thread in a group.
* and the concurrency will be set to 1.
* This is not quite right, as we may still make ourself
* bound after making other ksegrps but it will do for now.
* The library will only try do this much.
*/
if (!sa && !(uap->newgroup || first))
return (EINVAL);
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.
*/
newkg = ksegrp_alloc();
bzero(&newkg->kg_startzero, RANGEOF(struct ksegrp,
kg_startzero, kg_endzero));
@ -526,47 +536,55 @@ kse_create(struct thread *td, struct kse_create_args *uap)
mtx_unlock_spin(&sched_lock);
PROC_UNLOCK(p);
} else {
if (!first && ((td->td_pflags & TDP_SA) ^ sa) != 0)
/*
* We want to make a thread in our own ksegrp.
* If we are just the first call, either kind
* is ok, but if not then either we must be
* already an upcallable thread to make another,
* or a bound thread to make one of those.
* Once again, not quite right but good enough for now.. XXXKSE
*/
if (!first && ((td->td_pflags & TDP_SA) != sa))
return (EINVAL);
newkg = kg;
}
/*
* Creating upcalls more than number of physical cpu does
* not help performance.
*/
if (newkg->kg_numupcalls >= ncpus)
return (EPROCLIM);
/*
* This test is a bit "indirect".
* It might simplify things if we made a direct way of testing
* if a ksegrp has been worked on before.
* In the case of a bound request and the concurrency being set to
* one, the concurrency will already be 1 so it's just inefficient
* but not dangerous to call this again. XXX
*/
if (newkg->kg_numupcalls == 0) {
/*
* Initialize KSE group
* Initialize KSE group with the appropriate
* concurrency.
*
* For multiplxed group, create KSEs as many as physical
* cpus. This increases concurrent even if userland
* is not MP safe and can only run on single CPU.
* 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.
* For a multiplexed group, create as as much concurrency
* as the number of physical cpus.
* This increases concurrency in the kernel even if the
* userland is not MP safe and can only run on a single CPU.
* In an ideal world, every physical cpu should execute a
* thread. If there is enough concurrency, threads in the
* kernel can be executed parallel on different cpus at
* full speed without being restricted by the number of
* upcalls the userland provides.
* Adding more upcall structures only increases concurrency
* in userland.
*
* For bound thread group, because there is only thread in the
* group, we only create one KSE for the group. Thread in this
* kind of group will never schedule an upcall when blocked,
* this intends to simulate pthread system scope thread.
* For a bound thread group, because there is only one thread
* in the group, we only set the concurrency for the group
* to 1. A thread in this kind of group will never schedule
* an upcall when blocked. This simulates pthread system
* scope thread behaviour.
*/
while (newkg->kg_kses < ncpus) {
newke = kse_alloc();
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);
#endif
mtx_lock_spin(&sched_lock);
kse_link(newke, newkg);
sched_fork_kse(td, newke);
@ -575,27 +593,47 @@ kse_create(struct thread *td, struct kse_create_args *uap)
mtx_unlock_spin(&sched_lock);
}
}
/*
* Even bound LWPs get a mailbox and an upcall to hold it.
*/
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 */
/*
* For the first call this may not have been set.
* Of course nor may it actually be needed.
*/
if (td->td_standin == NULL)
thread_alloc_spare(td, NULL);
/*
* Creating upcalls more than number of physical cpu does
* not help performance.
*/
PROC_LOCK(p);
if (newkg->kg_numupcalls >= ncpus) {
PROC_UNLOCK(p);
upcall_free(newku);
return (EPROCLIM);
}
/*
* If we are the first time, and a normal thread,
* then trnasfer all the signals back to the 'process'.
* SA threading will make a special thread to handle them.
if (first && sa) {
SIGSETOR(p->p_siglist, td->td_siglist);
SIGEMPTYSET(td->td_siglist);
SIGFILLSET(td->td_sigmask);
SIG_CANTMASK(td->td_sigmask);
}
/*
* Make the new upcall available to the ksegrp,.
* It may or may not use it, but its available.
*/
mtx_lock_spin(&sched_lock);
PROC_UNLOCK(p);
upcall_link(newku, newkg);
@ -608,14 +646,15 @@ kse_create(struct thread *td, struct kse_create_args *uap)
*/
if (uap->newgroup) {
/*
* Because new ksegrp hasn't thread,
* Because the new ksegrp hasn't a thread,
* create an initial upcall thread to own it.
*/
newtd = thread_schedule_upcall(td, newku);
} else {
/*
* If current thread hasn't an upcall structure,
* If the current thread hasn't an upcall structure,
* just assign the upcall to it.
* It'll just return.
*/
if (td->td_upcall == NULL) {
newku->ku_owner = td;
@ -629,20 +668,55 @@ kse_create(struct thread *td, struct kse_create_args *uap)
}
}
mtx_unlock_spin(&sched_lock);
/*
* Let the UTS instance know its LWPID.
* It doesn't really care. But the debugger will.
*/
suword32(&newku->ku_mailbox->km_lwp, newtd->td_tid);
/*
* In the same manner, if the UTS has a current user thread,
* then it is also running on this LWP so set it as well.
* The library could do that of course.. but why not..
*/
if (mbx.km_curthread)
suword32(&mbx.km_curthread->tm_lwp, newtd->td_tid);
if (!sa) {
newtd->td_mailbox = mbx.km_curthread;
if (sa) {
newtd->td_pflags |= TDP_SA;
} else {
newtd->td_pflags &= ~TDP_SA;
/*
* Since a library will use the mailbox pointer to
* identify even a bound thread, and the mailbox pointer
* will never be allowed to change after this syscall
* for a bound thread, set it here so the library can
* find the thread after the syscall returns.
*/
newtd->td_mailbox = mbx.km_curthread;
if (newtd != td) {
/*
* If we did create a new thread then
* make sure it goes to the right place
* when it starts up, and make sure that it runs
* at full speed when it gets there.
* thread_schedule_upcall() copies all cpu state
* to the new thread, so we should clear single step
* flag here.
*/
cpu_set_upcall_kse(newtd, newku);
if (p->p_flag & P_TRACED)
ptrace_clear_single_step(newtd);
}
} else {
newtd->td_pflags |= TDP_SA;
}
/*
* If we are starting a new thread, kick it off.
*/
if (newtd != td) {
mtx_lock_spin(&sched_lock);
setrunqueue(newtd);