NetBSD/sys/kern/kern_sa.c
chs 9a3f782ac4 define a new LWP flag which indicates that we're in the process of
doing a context switch.  use this on sparc and sparc64 to avoid trying
to access user memory (writing the register windows back to the stack)
in this case (since it's both unnecessary and wrong).
2005-01-30 16:56:26 +00:00

1788 lines
43 KiB
C

/* $NetBSD: kern_sa.c,v 1.57 2005/01/30 16:56:26 chs Exp $ */
/*-
* Copyright (c) 2001 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Nathan J. Williams.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_sa.c,v 1.57 2005/01/30 16:56:26 chs Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/types.h>
#include <sys/ucontext.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/sa.h>
#include <sys/savar.h>
#include <sys/syscallargs.h>
#include <uvm/uvm_extern.h>
static struct sadata_vp *sa_newsavp(struct sadata *);
static __inline int sa_stackused(struct sastack *, struct sadata *);
static __inline void sa_setstackfree(struct sastack *, struct sadata *);
static struct sastack *sa_getstack(struct sadata *);
static __inline struct sastack *sa_getstack0(struct sadata *);
static __inline int sast_compare(struct sastack *, struct sastack *);
#ifdef MULTIPROCESSOR
static int sa_increaseconcurrency(struct lwp *, int);
#endif
static void sa_setwoken(struct lwp *);
static void sa_switchcall(void *);
static int sa_newcachelwp(struct lwp *);
static __inline void sa_makeupcalls(struct lwp *);
static struct lwp *sa_vp_repossess(struct lwp *l);
static __inline int sa_pagefault(struct lwp *, ucontext_t *);
static int sa_upcall0(struct lwp *, int, struct lwp *, struct lwp *,
size_t, void *, struct sadata_upcall *);
static void sa_upcall_getstate(union sau_state *, struct lwp *);
MALLOC_DEFINE(M_SA, "sa", "Scheduler activations");
#define SA_DEBUG
#ifdef SA_DEBUG
#define DPRINTF(x) do { if (sadebug) printf x; } while (0)
#define DPRINTFN(n,x) do { if (sadebug & (1<<(n-1))) printf x; } while (0)
int sadebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define SA_LWP_STATE_LOCK(l, f) do { \
(f) = (l)->l_flag; \
(l)->l_flag &= ~L_SA; \
} while (/*CONSTCOND*/ 0)
#define SA_LWP_STATE_UNLOCK(l, f) do { \
(l)->l_flag |= (f) & L_SA; \
} while (/*CONSTCOND*/ 0)
SPLAY_PROTOTYPE(sasttree, sastack, sast_node, sast_compare);
SPLAY_GENERATE(sasttree, sastack, sast_node, sast_compare);
/*
* sadata_upcall_alloc:
*
* Allocate an sadata_upcall structure.
*/
struct sadata_upcall *
sadata_upcall_alloc(int waitok)
{
/* XXX zero the memory? */
return (pool_get(&saupcall_pool, waitok ? PR_WAITOK : PR_NOWAIT));
}
/*
* sadata_upcall_free:
*
* Free an sadata_upcall structure, and any associated
* argument data.
*/
void
sadata_upcall_free(struct sadata_upcall *sau)
{
extern struct pool siginfo_pool; /* XXX Ew. */
/*
* XXX We have to know what the origin of sau_arg is
* XXX in order to do the right thing, here. Sucks
* XXX to be a non-garbage-collecting kernel.
*/
if (sau->sau_arg) {
switch (sau->sau_type) {
case SA_UPCALL_SIGNAL:
case SA_UPCALL_SIGEV:
pool_put(&siginfo_pool, sau->sau_arg);
break;
default:
panic("sadata_free: unknown type of sau_arg: %d",
sau->sau_type);
}
}
pool_put(&saupcall_pool, sau);
}
static struct sadata_vp *
sa_newsavp(struct sadata *sa)
{
struct sadata_vp *vp, *qvp;
/* Allocate virtual processor data structure */
vp = pool_get(&savp_pool, PR_WAITOK);
/* Initialize. */
memset(vp, 0, sizeof(*vp));
simple_lock_init(&vp->savp_lock);
vp->savp_lwp = NULL;
vp->savp_wokenq_head = NULL;
vp->savp_faultaddr = 0;
vp->savp_ofaultaddr = 0;
LIST_INIT(&vp->savp_lwpcache);
vp->savp_ncached = 0;
SIMPLEQ_INIT(&vp->savp_upcalls);
simple_lock(&sa->sa_lock);
/* find first free savp_id and add vp to sorted slist */
if (SLIST_EMPTY(&sa->sa_vps) ||
SLIST_FIRST(&sa->sa_vps)->savp_id != 0) {
vp->savp_id = 0;
SLIST_INSERT_HEAD(&sa->sa_vps, vp, savp_next);
} else {
SLIST_FOREACH(qvp, &sa->sa_vps, savp_next) {
if (SLIST_NEXT(qvp, savp_next) == NULL ||
SLIST_NEXT(qvp, savp_next)->savp_id !=
qvp->savp_id + 1)
break;
}
vp->savp_id = qvp->savp_id + 1;
SLIST_INSERT_AFTER(qvp, vp, savp_next);
}
simple_unlock(&sa->sa_lock);
return (vp);
}
int
sys_sa_register(struct lwp *l, void *v, register_t *retval)
{
struct sys_sa_register_args /* {
syscallarg(sa_upcall_t) new;
syscallarg(sa_upcall_t *) old;
syscallarg(int) flags;
syscallarg(ssize_t) stackinfo_offset;
} */ *uap = v;
struct proc *p = l->l_proc;
struct sadata *sa;
sa_upcall_t prev;
int error;
if (p->p_sa == NULL) {
/* Allocate scheduler activations data structure */
sa = pool_get(&sadata_pool, PR_WAITOK);
/* Initialize. */
memset(sa, 0, sizeof(*sa));
simple_lock_init(&sa->sa_lock);
sa->sa_flag = SCARG(uap, flags) & SA_FLAG_ALL;
sa->sa_maxconcurrency = 1;
sa->sa_concurrency = 1;
SPLAY_INIT(&sa->sa_stackstree);
sa->sa_stacknext = NULL;
if (SCARG(uap, flags) & SA_FLAG_STACKINFO)
sa->sa_stackinfo_offset = SCARG(uap, stackinfo_offset);
else
sa->sa_stackinfo_offset = 0;
sa->sa_nstacks = 0;
SLIST_INIT(&sa->sa_vps);
p->p_sa = sa;
KASSERT(l->l_savp == NULL);
}
if (l->l_savp == NULL) {
l->l_savp = sa_newsavp(p->p_sa);
sa_newcachelwp(l);
}
prev = p->p_sa->sa_upcall;
p->p_sa->sa_upcall = SCARG(uap, new);
if (SCARG(uap, old)) {
error = copyout(&prev, SCARG(uap, old),
sizeof(prev));
if (error)
return (error);
}
return (0);
}
void
sa_release(struct proc *p)
{
struct sadata *sa;
struct sastack *sast, *next;
struct sadata_vp *vp;
struct lwp *l;
sa = p->p_sa;
KDASSERT(sa != NULL);
KASSERT(p->p_nlwps <= 1);
for (sast = SPLAY_MIN(sasttree, &sa->sa_stackstree); sast != NULL;
sast = next) {
next = SPLAY_NEXT(sasttree, &sa->sa_stackstree, sast);
SPLAY_REMOVE(sasttree, &sa->sa_stackstree, sast);
pool_put(&sastack_pool, sast);
}
p->p_flag &= ~P_SA;
while ((vp = SLIST_FIRST(&p->p_sa->sa_vps)) != NULL) {
SLIST_REMOVE_HEAD(&p->p_sa->sa_vps, savp_next);
pool_put(&savp_pool, vp);
}
pool_put(&sadata_pool, sa);
p->p_sa = NULL;
l = LIST_FIRST(&p->p_lwps);
if (l) {
KASSERT(LIST_NEXT(l, l_sibling) == NULL);
l->l_savp = NULL;
}
}
static __inline int
sa_stackused(struct sastack *sast, struct sadata *sa)
{
unsigned int gen;
if (copyin((void *)&((struct sa_stackinfo_t *)
((char *)sast->sast_stack.ss_sp +
sa->sa_stackinfo_offset))->sasi_stackgen,
&gen, sizeof(unsigned int)) != 0) {
#ifdef DIAGNOSTIC
printf("sa_stackused: couldn't copyin sasi_stackgen");
#endif
sigexit(curlwp, SIGILL);
/* NOTREACHED */
}
return (sast->sast_gen != gen);
}
static __inline void
sa_setstackfree(struct sastack *sast, struct sadata *sa)
{
if (copyin((void *)&((struct sa_stackinfo_t *)
((char *)sast->sast_stack.ss_sp +
sa->sa_stackinfo_offset))->sasi_stackgen,
&sast->sast_gen, sizeof(unsigned int)) != 0) {
#ifdef DIAGNOSTIC
printf("sa_setstackfree: couldn't copyin sasi_stackgen");
#endif
sigexit(curlwp, SIGILL);
/* NOTREACHED */
}
}
/*
* Find next free stack, starting at sa->sa_stacknext.
*/
static struct sastack *
sa_getstack(struct sadata *sa)
{
struct sastack *sast;
SCHED_ASSERT_UNLOCKED();
if ((sast = sa->sa_stacknext) == NULL || sa_stackused(sast, sa))
sast = sa_getstack0(sa);
if (sast == NULL)
return NULL;
sast->sast_gen++;
return sast;
}
static __inline struct sastack *
sa_getstack0(struct sadata *sa)
{
struct sastack *start;
if (sa->sa_stacknext == NULL) {
sa->sa_stacknext = SPLAY_MIN(sasttree, &sa->sa_stackstree);
if (sa->sa_stacknext == NULL)
return NULL;
}
start = sa->sa_stacknext;
while (sa_stackused(sa->sa_stacknext, sa)) {
sa->sa_stacknext = SPLAY_NEXT(sasttree, &sa->sa_stackstree,
sa->sa_stacknext);
if (sa->sa_stacknext == NULL)
sa->sa_stacknext = SPLAY_MIN(sasttree,
&sa->sa_stackstree);
if (sa->sa_stacknext == start)
return NULL;
}
return sa->sa_stacknext;
}
static __inline int
sast_compare(struct sastack *a, struct sastack *b)
{
if ((vaddr_t)a->sast_stack.ss_sp + a->sast_stack.ss_size <=
(vaddr_t)b->sast_stack.ss_sp)
return (-1);
if ((vaddr_t)a->sast_stack.ss_sp >=
(vaddr_t)b->sast_stack.ss_sp + b->sast_stack.ss_size)
return (1);
return (0);
}
int
sys_sa_stacks(struct lwp *l, void *v, register_t *retval)
{
struct sys_sa_stacks_args /* {
syscallarg(int) num;
syscallarg(stack_t *) stacks;
} */ *uap = v;
struct sadata *sa = l->l_proc->p_sa;
struct sastack *sast, newsast;
int count, error, f, i;
/* We have to be using scheduler activations */
if (sa == NULL)
return (EINVAL);
count = SCARG(uap, num);
if (count < 0)
return (EINVAL);
SA_LWP_STATE_LOCK(l, f);
error = 0;
for (i = 0; i < count; i++) {
error = copyin(SCARG(uap, stacks) + i, &newsast.sast_stack,
sizeof(stack_t));
if (error) {
count = i;
break;
}
if ((sast = SPLAY_FIND(sasttree, &sa->sa_stackstree, &newsast))) {
DPRINTFN(9, ("sa_stacks(%d.%d) returning stack %p\n",
l->l_proc->p_pid, l->l_lid,
newsast.sast_stack.ss_sp));
if (sa_stackused(sast, sa) == 0) {
count = i;
error = EEXIST;
break;
}
} else if (sa->sa_nstacks >= SA_MAXNUMSTACKS * sa->sa_concurrency) {
DPRINTFN(9, ("sa_stacks(%d.%d) already using %d stacks\n",
l->l_proc->p_pid, l->l_lid,
SA_MAXNUMSTACKS * sa->sa_concurrency));
count = i;
error = ENOMEM;
break;
} else {
DPRINTFN(9, ("sa_stacks(%d.%d) adding stack %p\n",
l->l_proc->p_pid, l->l_lid,
newsast.sast_stack.ss_sp));
sast = pool_get(&sastack_pool, PR_WAITOK);
sast->sast_stack = newsast.sast_stack;
SPLAY_INSERT(sasttree, &sa->sa_stackstree, sast);
sa->sa_nstacks++;
}
sa_setstackfree(sast, sa);
}
SA_LWP_STATE_UNLOCK(l, f);
*retval = count;
return (error);
}
int
sys_sa_enable(struct lwp *l, void *v, register_t *retval)
{
struct proc *p = l->l_proc;
struct sadata *sa = p->p_sa;
struct sadata_vp *vp = l->l_savp;
int error;
DPRINTF(("sys_sa_enable(%d.%d)\n", l->l_proc->p_pid,
l->l_lid));
/* We have to be using scheduler activations */
if (sa == NULL || vp == NULL)
return (EINVAL);
if (p->p_flag & P_SA) /* Already running! */
return (EBUSY);
error = sa_upcall(l, SA_UPCALL_NEWPROC, l, NULL, 0, NULL);
if (error)
return (error);
/* Assign this LWP to the virtual processor */
vp->savp_lwp = l;
p->p_flag |= P_SA;
l->l_flag |= L_SA; /* We are now an activation LWP */
/* This will not return to the place in user space it came from. */
return (0);
}
#ifdef MULTIPROCESSOR
static int
sa_increaseconcurrency(struct lwp *l, int concurrency)
{
struct proc *p;
struct lwp *l2;
struct sadata *sa;
vaddr_t uaddr;
boolean_t inmem;
int addedconcurrency, error, s;
p = l->l_proc;
sa = p->p_sa;
addedconcurrency = 0;
simple_lock(&sa->sa_lock);
while (sa->sa_maxconcurrency < concurrency) {
sa->sa_maxconcurrency++;
sa->sa_concurrency++;
simple_unlock(&sa->sa_lock);
inmem = uvm_uarea_alloc(&uaddr);
if (__predict_false(uaddr == 0)) {
/* reset concurrency */
simple_lock(&sa->sa_lock);
sa->sa_maxconcurrency--;
sa->sa_concurrency--;
simple_unlock(&sa->sa_lock);
return (addedconcurrency);
} else {
newlwp(l, p, uaddr, inmem, 0, NULL, 0,
child_return, 0, &l2);
l2->l_flag |= L_SA;
l2->l_savp = sa_newsavp(sa);
if (l2->l_savp) {
l2->l_savp->savp_lwp = l2;
cpu_setfunc(l2, sa_switchcall, NULL);
error = sa_upcall(l2, SA_UPCALL_NEWPROC,
NULL, NULL, 0, NULL);
if (error) {
/* free new savp */
SLIST_REMOVE(&sa->sa_vps, l2->l_savp,
sadata_vp, savp_next);
pool_put(&savp_pool, l2->l_savp);
}
} else
error = 1;
if (error) {
/* put l2 into l's LWP cache */
l2->l_savp = l->l_savp;
PHOLD(l2);
SCHED_LOCK(s);
sa_putcachelwp(p, l2);
SCHED_UNLOCK(s);
/* reset concurrency */
simple_lock(&sa->sa_lock);
sa->sa_maxconcurrency--;
sa->sa_concurrency--;
simple_unlock(&sa->sa_lock);
return (addedconcurrency);
}
SCHED_LOCK(s);
setrunnable(l2);
SCHED_UNLOCK(s);
addedconcurrency++;
}
simple_lock(&sa->sa_lock);
}
simple_unlock(&sa->sa_lock);
return (addedconcurrency);
}
#endif
int
sys_sa_setconcurrency(struct lwp *l, void *v, register_t *retval)
{
struct sys_sa_setconcurrency_args /* {
syscallarg(int) concurrency;
} */ *uap = v;
struct sadata *sa = l->l_proc->p_sa;
#ifdef MULTIPROCESSOR
struct sadata_vp *vp = l->l_savp;
int ncpus, s;
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
#endif
DPRINTFN(11,("sys_sa_concurrency(%d.%d)\n", l->l_proc->p_pid,
l->l_lid));
/* We have to be using scheduler activations */
if (sa == NULL)
return (EINVAL);
if (SCARG(uap, concurrency) < 1)
return (EINVAL);
*retval = 0;
/*
* Concurrency greater than the number of physical CPUs does
* not make sense.
* XXX Should we ever support hot-plug CPUs, this will need
* adjustment.
*/
#ifdef MULTIPROCESSOR
if (SCARG(uap, concurrency) > sa->sa_maxconcurrency) {
ncpus = 0;
for (CPU_INFO_FOREACH(cii, ci))
ncpus++;
*retval += sa_increaseconcurrency(l,
min(SCARG(uap, concurrency), ncpus));
}
#endif
DPRINTFN(11,("sys_sa_concurrency(%d.%d) want %d, have %d, max %d\n",
l->l_proc->p_pid, l->l_lid, SCARG(uap, concurrency),
sa->sa_concurrency, sa->sa_maxconcurrency));
#ifdef MULTIPROCESSOR
if (SCARG(uap, concurrency) > sa->sa_concurrency) {
SCHED_LOCK(s);
SLIST_FOREACH(vp, &sa->sa_vps, savp_next) {
if (vp->savp_lwp->l_flag & L_SA_IDLE) {
vp->savp_lwp->l_flag &=
~(L_SA_IDLE|L_SA_YIELD|L_SINTR);
SCHED_UNLOCK(s);
DPRINTFN(11,("sys_sa_concurrency(%d.%d) NEWPROC vp %d\n",
l->l_proc->p_pid, l->l_lid, vp->savp_id));
cpu_setfunc(vp->savp_lwp, sa_switchcall, NULL);
/* error = */ sa_upcall(vp->savp_lwp, SA_UPCALL_NEWPROC,
NULL, NULL, 0, NULL);
SCHED_LOCK(s);
sa->sa_concurrency++;
setrunnable(vp->savp_lwp);
KDASSERT((vp->savp_lwp->l_flag & L_SINTR) == 0);
(*retval)++;
}
if (sa->sa_concurrency == SCARG(uap, concurrency))
break;
}
SCHED_UNLOCK(s);
}
#endif
return (0);
}
int
sys_sa_yield(struct lwp *l, void *v, register_t *retval)
{
struct proc *p = l->l_proc;
if (p->p_sa == NULL || !(p->p_flag & P_SA)) {
DPRINTFN(1,("sys_sa_yield(%d.%d) proc %p not SA (p_sa %p, flag %s)\n",
p->p_pid, l->l_lid, p, p->p_sa, p->p_flag & P_SA ? "T" : "F"));
return (EINVAL);
}
sa_yield(l);
return (EJUSTRETURN);
}
void
sa_yield(struct lwp *l)
{
struct proc *p = l->l_proc;
struct sadata *sa = p->p_sa;
struct sadata_vp *vp = l->l_savp;
int ret;
KERNEL_LOCK_ASSERT_LOCKED();
if (vp->savp_lwp != l) {
/*
* We lost the VP on our way here, this happens for
* instance when we sleep in systrace. This will end
* in an SA_UNBLOCKED_UPCALL in sa_setwoken().
*/
DPRINTFN(1,("sa_yield(%d.%d) lost VP\n",
p->p_pid, l->l_lid));
KDASSERT(l->l_flag & L_SA_BLOCKING);
return;
}
/*
* If we're the last running LWP, stick around to recieve
* signals.
*/
KDASSERT((l->l_flag & L_SA_YIELD) == 0);
DPRINTFN(1,("sa_yield(%d.%d) going dormant\n",
p->p_pid, l->l_lid));
/*
* A signal will probably wake us up. Worst case, the upcall
* happens and just causes the process to yield again.
*/
/* s = splsched(); */ /* Protect from timer expirations */
KDASSERT(vp->savp_lwp == l);
/*
* If we were told to make an upcall or exit before
* the splsched(), make sure we process it instead of
* going to sleep. It might make more sense for this to
* be handled inside of tsleep....
*/
ret = 0;
l->l_flag |= L_SA_YIELD;
if (l->l_flag & L_SA_UPCALL) {
/* KERNEL_PROC_UNLOCK(l); in upcallret() */
upcallret(l);
KERNEL_PROC_LOCK(l);
}
while (l->l_flag & L_SA_YIELD) {
DPRINTFN(1,("sa_yield(%d.%d) really going dormant\n",
p->p_pid, l->l_lid));
simple_lock(&sa->sa_lock);
sa->sa_concurrency--;
simple_unlock(&sa->sa_lock);
ret = tsleep((caddr_t) l, PUSER | PCATCH, "sawait", 0);
simple_lock(&sa->sa_lock);
sa->sa_concurrency++;
simple_unlock(&sa->sa_lock);
KDASSERT(vp->savp_lwp == l || p->p_flag & P_WEXIT);
/* KERNEL_PROC_UNLOCK(l); in upcallret() */
upcallret(l);
KERNEL_PROC_LOCK(l);
}
/* splx(s); */
DPRINTFN(1,("sa_yield(%d.%d) returned, ret %d, userret %p\n",
p->p_pid, l->l_lid, ret, p->p_userret));
}
int
sys_sa_preempt(struct lwp *l, void *v, register_t *retval)
{
/* XXX Implement me. */
return (ENOSYS);
}
/* XXX Hm, naming collision. */
void
sa_preempt(struct lwp *l)
{
struct proc *p = l->l_proc;
struct sadata *sa = p->p_sa;
/*
* Defer saving the lwp's state because on some ports
* preemption can occur between generating an unblocked upcall
* and processing the upcall queue.
*/
if (sa->sa_flag & SA_FLAG_PREEMPT)
sa_upcall(l, SA_UPCALL_PREEMPTED | SA_UPCALL_DEFER_EVENT,
l, NULL, 0, NULL);
}
/*
* Set up the user-level stack and trapframe to do an upcall.
*
* NOTE: This routine WILL FREE "arg" in the case of failure! Callers
* should not touch the "arg" pointer once calling sa_upcall().
*/
int
sa_upcall(struct lwp *l, int type, struct lwp *event, struct lwp *interrupted,
size_t argsize, void *arg)
{
struct sadata_upcall *sau;
struct sadata *sa = l->l_proc->p_sa;
struct sadata_vp *vp = l->l_savp;
struct sastack *sast;
int error, f;
/* XXX prevent recursive upcalls if we sleep for memory */
SA_LWP_STATE_LOCK(l, f);
sast = sa_getstack(sa);
SA_LWP_STATE_UNLOCK(l, f);
if (sast == NULL) {
return (ENOMEM);
}
DPRINTFN(9,("sa_upcall(%d.%d) using stack %p\n",
l->l_proc->p_pid, l->l_lid, sast->sast_stack.ss_sp));
SA_LWP_STATE_LOCK(l, f);
sau = sadata_upcall_alloc(1);
SA_LWP_STATE_UNLOCK(l, f);
error = sa_upcall0(l, type, event, interrupted, argsize, arg, sau);
if (error) {
sadata_upcall_free(sau);
sa_setstackfree(sast, sa);
return (error);
}
sau->sau_stack = sast->sast_stack;
SIMPLEQ_INSERT_TAIL(&vp->savp_upcalls, sau, sau_next);
l->l_flag |= L_SA_UPCALL;
return (0);
}
static int
sa_upcall0(struct lwp *l, int type, struct lwp *event, struct lwp *interrupted,
size_t argsize, void *arg, struct sadata_upcall *sau)
{
KDASSERT((event == NULL) || (event != interrupted));
sau->sau_flags = 0;
sau->sau_arg = 0;
if (type & SA_UPCALL_DEFER_EVENT) {
sau->sau_event.ss_deferred.ss_lwp = event;
sau->sau_flags |= SAU_FLAG_DEFERRED_EVENT;
} else
sa_upcall_getstate(&sau->sau_event, event);
if (type & SA_UPCALL_DEFER_INTERRUPTED) {
sau->sau_interrupted.ss_deferred.ss_lwp = interrupted;
sau->sau_flags |= SAU_FLAG_DEFERRED_INTERRUPTED;
} else
sa_upcall_getstate(&sau->sau_interrupted, interrupted);
sau->sau_type = type & SA_UPCALL_TYPE_MASK;
sau->sau_argsize = argsize;
sau->sau_arg = arg;
return (0);
}
static void
sa_upcall_getstate(union sau_state *ss, struct lwp *l)
{
caddr_t sp;
size_t ucsize;
if (l) {
l->l_flag |= L_SA_SWITCHING;
getucontext(l, &ss->ss_captured.ss_ctx);
l->l_flag &= ~L_SA_SWITCHING;
sp = (void *)
((intptr_t)_UC_MACHINE_SP(&ss->ss_captured.ss_ctx));
sp = STACK_ALIGN(sp, ~_UC_UCONTEXT_ALIGN);
ucsize = roundup(sizeof(ucontext_t), (~_UC_UCONTEXT_ALIGN) + 1);
ss->ss_captured.ss_sa.sa_context = (ucontext_t *)
STACK_ALLOC(sp, ucsize);
ss->ss_captured.ss_sa.sa_id = l->l_lid;
ss->ss_captured.ss_sa.sa_cpu = l->l_savp->savp_id;
} else
ss->ss_captured.ss_sa.sa_context = NULL;
}
/*
* Detect double pagefaults and pagefaults on upcalls.
* - double pagefaults are detected by comparing the previous faultaddr
* against the current faultaddr
* - pagefaults on upcalls are detected by checking if the userspace
* thread is running on an upcall stack
*/
static __inline int
sa_pagefault(struct lwp *l, ucontext_t *l_ctx)
{
struct proc *p;
struct sadata *sa;
struct sadata_vp *vp;
struct sastack sast;
p = l->l_proc;
sa = p->p_sa;
vp = l->l_savp;
KDASSERT(vp->savp_lwp == l);
if (vp->savp_faultaddr == vp->savp_ofaultaddr) {
DPRINTFN(10,("sa_pagefault(%d.%d) double page fault\n",
p->p_pid, l->l_lid));
return 1;
}
sast.sast_stack.ss_sp = (void *)(intptr_t)_UC_MACHINE_SP(l_ctx);
sast.sast_stack.ss_size = 1;
if (SPLAY_FIND(sasttree, &sa->sa_stackstree, &sast)) {
DPRINTFN(10,("sa_pagefault(%d.%d) upcall page fault\n",
p->p_pid, l->l_lid));
return 1;
}
vp->savp_ofaultaddr = vp->savp_faultaddr;
return 0;
}
/*
* Called by tsleep(). Block current LWP and switch to another.
*
* WE ARE NOT ALLOWED TO SLEEP HERE! WE ARE CALLED FROM WITHIN
* TSLEEP() ITSELF! We are called with sched_lock held, and must
* hold it right through the mi_switch() call.
*/
void
sa_switch(struct lwp *l, int type)
{
struct proc *p = l->l_proc;
struct sadata_vp *vp = l->l_savp;
struct sadata_upcall *sau;
struct lwp *l2;
int error, s;
DPRINTFN(4,("sa_switch(%d.%d type %d VP %d)\n", p->p_pid, l->l_lid,
type, vp->savp_lwp ? vp->savp_lwp->l_lid : 0));
SCHED_ASSERT_LOCKED();
if (p->p_flag & P_WEXIT) {
mi_switch(l, NULL);
return;
}
if (l->l_flag & L_SA_YIELD) {
/*
* Case 0: we're blocking in sa_yield
*/
if (vp->savp_wokenq_head == NULL && p->p_userret == NULL) {
l->l_flag |= L_SA_IDLE;
mi_switch(l, NULL);
} else {
/* make us running again. */
unsleep(l);
l->l_stat = LSONPROC;
l->l_proc->p_nrlwps++;
s = splsched();
SCHED_UNLOCK(s);
}
return;
} else if (vp->savp_lwp == l) {
/*
* Case 1: we're blocking for the first time; generate
* a SA_BLOCKED upcall and allocate resources for the
* UNBLOCKED upcall.
*/
/*
* The process of allocating a new LWP could cause
* sleeps. We're called from inside sleep, so that
* would be Bad. Therefore, we must use a cached new
* LWP. The first thing that this new LWP must do is
* allocate another LWP for the cache. */
l2 = sa_getcachelwp(vp);
if (l2 == NULL) {
/* XXXSMP */
/* No upcall for you! */
/* XXX The consequences of this are more subtle and
* XXX the recovery from this situation deserves
* XXX more thought.
*/
/* XXXUPSXXX Should only happen with concurrency > 1 */
#ifdef DIAGNOSTIC
printf("sa_switch(%d.%d): no cached LWP for upcall.\n",
p->p_pid, l->l_lid);
#endif
mi_switch(l, NULL);
return;
}
/*
* XXX We need to allocate the sadata_upcall structure here,
* XXX since we can't sleep while waiting for memory inside
* XXX sa_upcall(). It would be nice if we could safely
* XXX allocate the sadata_upcall structure on the stack, here.
*/
sau = sadata_upcall_alloc(0);
if (sau == NULL) {
#ifdef DIAGNOSTIC
printf("sa_switch(%d.%d): couldn't allocate upcall data.\n",
p->p_pid, l->l_lid);
#endif
sa_putcachelwp(p, l2); /* PHOLD from sa_getcachelwp */
mi_switch(l, NULL);
return;
}
cpu_setfunc(l2, sa_switchcall, sau);
error = sa_upcall0(l2, SA_UPCALL_BLOCKED, l, NULL, 0, NULL,
sau);
if (error) {
#ifdef DIAGNOSTIC
printf("sa_switch(%d.%d): Error %d from sa_upcall()\n",
p->p_pid, l->l_lid, error);
#endif
sa_putcachelwp(p, l2); /* PHOLD from sa_getcachelwp */
mi_switch(l, NULL);
return;
}
/*
* Perform the double/upcall pagefault check.
* We do this only here since we need l's ucontext to
* get l's userspace stack. sa_upcall0 above has saved
* it for us.
* The L_SA_PAGEFAULT flag is set in the MD
* pagefault code to indicate a pagefault. The MD
* pagefault code also saves the faultaddr for us.
*/
if ((l->l_flag & L_SA_PAGEFAULT) && sa_pagefault(l,
&sau->sau_event.ss_captured.ss_ctx) != 0) {
sadata_upcall_free(sau);
sa_putcachelwp(p, l2); /* PHOLD from sa_getcachelwp */
mi_switch(l, NULL);
DPRINTFN(10,("sa_switch(%d.%d) page fault resolved\n",
p->p_pid, l->l_lid));
if (vp->savp_faultaddr == vp->savp_ofaultaddr)
vp->savp_ofaultaddr = -1;
return;
}
DPRINTFN(8,("sa_switch(%d.%d) blocked upcall %d\n",
p->p_pid, l->l_lid, l2->l_lid));
l->l_flag |= L_SA_BLOCKING;
l2->l_priority = l2->l_usrpri;
vp->savp_blocker = l;
vp->savp_lwp = l2;
setrunnable(l2);
PRELE(l2); /* Remove the artificial hold-count */
KDASSERT(l2 != l);
} else if (vp->savp_lwp != NULL) {
/*
* Case 2: We've been woken up while another LWP was
* on the VP, but we're going back to sleep without
* having returned to userland and delivering the
* SA_UNBLOCKED upcall (select and poll cause this
* kind of behavior a lot). We just switch back to the
* LWP that had been running and let it have another
* go. If the LWP on the VP was idling, don't make it
* run again, though.
*/
if (vp->savp_lwp->l_flag & L_SA_YIELD)
l2 = NULL;
else {
l2 = vp->savp_lwp; /* XXXUPSXXX Unfair advantage for l2 ? */
if((l2->l_stat != LSRUN) || ((l2->l_flag & L_INMEM) == 0))
l2 = NULL;
}
} else {
/* NOTREACHED */
panic("sa_vp empty");
}
DPRINTFN(4,("sa_switch(%d.%d) switching to LWP %d.\n",
p->p_pid, l->l_lid, l2 ? l2->l_lid : 0));
mi_switch(l, l2);
DPRINTFN(4,("sa_switch(%d.%d flag %x) returned.\n",
p->p_pid, l->l_lid, l->l_flag));
KDASSERT(l->l_wchan == 0);
SCHED_ASSERT_UNLOCKED();
}
static void
sa_switchcall(void *arg)
{
struct lwp *l, *l2;
struct proc *p;
struct sadata_vp *vp;
struct sadata_upcall *sau;
struct sastack *sast;
int s;
l2 = curlwp;
p = l2->l_proc;
vp = l2->l_savp;
if (p->p_flag & P_WEXIT)
lwp_exit(l2);
KDASSERT(vp->savp_lwp == l2);
sau = arg;
DPRINTFN(6,("sa_switchcall(%d.%d)\n", p->p_pid, l2->l_lid));
l2->l_flag &= ~L_SA;
if (LIST_EMPTY(&vp->savp_lwpcache)) {
/* Allocate the next cache LWP */
DPRINTFN(6,("sa_switchcall(%d.%d) allocating LWP\n",
p->p_pid, l2->l_lid));
sa_newcachelwp(l2);
}
if (sau) {
l = vp->savp_blocker;
sast = sa_getstack(p->p_sa);
if (sast) {
sau->sau_stack = sast->sast_stack;
SIMPLEQ_INSERT_TAIL(&vp->savp_upcalls, sau, sau_next);
l2->l_flag |= L_SA_UPCALL;
} else {
#ifdef DIAGNOSTIC
printf("sa_switchcall(%d.%d flag %x): Not enough stacks.\n",
p->p_pid, l->l_lid, l->l_flag);
#endif
sadata_upcall_free(sau);
PHOLD(l2);
SCHED_LOCK(s);
sa_putcachelwp(p, l2); /* sets L_SA */
vp->savp_lwp = l;
mi_switch(l2, NULL);
/* mostly NOTREACHED */
SCHED_ASSERT_UNLOCKED();
splx(s);
}
}
l2->l_flag |= L_SA;
upcallret(l2);
}
static int
sa_newcachelwp(struct lwp *l)
{
struct proc *p;
struct lwp *l2;
vaddr_t uaddr;
boolean_t inmem;
int s;
p = l->l_proc;
if (p->p_flag & P_WEXIT)
return (0);
inmem = uvm_uarea_alloc(&uaddr);
if (__predict_false(uaddr == 0)) {
return (ENOMEM);
} else {
newlwp(l, p, uaddr, inmem, 0, NULL, 0, child_return, 0, &l2);
/* We don't want this LWP on the process's main LWP list, but
* newlwp helpfully puts it there. Unclear if newlwp should
* be tweaked.
*/
PHOLD(l2);
SCHED_LOCK(s);
l2->l_savp = l->l_savp;
sa_putcachelwp(p, l2);
SCHED_UNLOCK(s);
}
return (0);
}
/*
* Take a normal process LWP and place it in the SA cache.
* LWP must not be running!
*/
void
sa_putcachelwp(struct proc *p, struct lwp *l)
{
struct sadata_vp *vp;
SCHED_ASSERT_LOCKED();
vp = l->l_savp;
LIST_REMOVE(l, l_sibling);
p->p_nlwps--;
l->l_stat = LSSUSPENDED;
l->l_flag |= (L_DETACHED | L_SA);
/* XXX lock sadata */
DPRINTFN(5,("sa_putcachelwp(%d.%d) Adding LWP %d to cache\n",
p->p_pid, curlwp->l_lid, l->l_lid));
LIST_INSERT_HEAD(&vp->savp_lwpcache, l, l_sibling);
vp->savp_ncached++;
/* XXX unlock */
}
/*
* Fetch a LWP from the cache.
*/
struct lwp *
sa_getcachelwp(struct sadata_vp *vp)
{
struct lwp *l;
struct proc *p;
SCHED_ASSERT_LOCKED();
l = NULL;
/* XXX lock sadata */
if (vp->savp_ncached > 0) {
vp->savp_ncached--;
l = LIST_FIRST(&vp->savp_lwpcache);
LIST_REMOVE(l, l_sibling);
p = l->l_proc;
LIST_INSERT_HEAD(&p->p_lwps, l, l_sibling);
p->p_nlwps++;
DPRINTFN(5,("sa_getcachelwp(%d.%d) Got LWP %d from cache.\n",
p->p_pid, curlwp->l_lid, l->l_lid));
}
/* XXX unlock */
return l;
}
void
sa_unblock_userret(struct lwp *l)
{
struct proc *p;
struct lwp *l2;
struct sadata *sa;
struct sadata_vp *vp;
struct sadata_upcall *sau;
struct sastack *sast;
int f, s;
p = l->l_proc;
sa = p->p_sa;
vp = l->l_savp;
if (p->p_flag & P_WEXIT)
return;
SCHED_ASSERT_UNLOCKED();
KERNEL_PROC_LOCK(l);
SA_LWP_STATE_LOCK(l, f);
DPRINTFN(7,("sa_unblock_userret(%d.%d %x) \n", p->p_pid, l->l_lid,
l->l_flag));
sa_setwoken(l);
/* maybe NOTREACHED */
SCHED_LOCK(s);
if (l != vp->savp_lwp) {
/* Invoke an "unblocked" upcall */
DPRINTFN(8,("sa_unblock_userret(%d.%d) unblocking\n",
p->p_pid, l->l_lid));
l2 = sa_vp_repossess(l);
SCHED_UNLOCK(s);
if (l2 == NULL)
lwp_exit(l);
sast = sa_getstack(sa);
if (p->p_flag & P_WEXIT)
lwp_exit(l);
sau = sadata_upcall_alloc(1);
sau->sau_arg = NULL;
if (p->p_flag & P_WEXIT) {
sadata_upcall_free(sau);
lwp_exit(l);
}
PHOLD(l2);
KDASSERT(sast != NULL);
DPRINTFN(9,("sa_unblock_userret(%d.%d) using stack %p\n",
l->l_proc->p_pid, l->l_lid, sast->sast_stack.ss_sp));
/*
* Defer saving the event lwp's state because a
* PREEMPT upcall could be on the queue already.
*/
if (sa_upcall0(l, SA_UPCALL_UNBLOCKED | SA_UPCALL_DEFER_EVENT,
l, l2, 0, NULL, sau) != 0) {
/*
* We were supposed to deliver an UNBLOCKED
* upcall, but don't have resources to do so.
*/
#ifdef DIAGNOSTIC
printf("sa_unblock_userret: out of upcall resources"
" for %d.%d\n", p->p_pid, l->l_lid);
#endif
sigexit(l, SIGABRT);
/* NOTREACHED */
}
sau->sau_stack = sast->sast_stack;
SCHED_LOCK(s);
SIMPLEQ_INSERT_TAIL(&vp->savp_upcalls, sau, sau_next);
l->l_flag |= L_SA_UPCALL;
l->l_flag &= ~L_SA_BLOCKING;
sa_putcachelwp(p, l2);
}
SCHED_UNLOCK(s);
SA_LWP_STATE_UNLOCK(l, f);
KERNEL_PROC_UNLOCK(l);
}
void
sa_upcall_userret(struct lwp *l)
{
struct lwp *l2;
struct proc *p;
struct sadata *sa;
struct sadata_vp *vp;
struct sadata_upcall *sau;
struct sastack *sast;
int f, s;
p = l->l_proc;
sa = p->p_sa;
vp = l->l_savp;
SCHED_ASSERT_UNLOCKED();
KERNEL_PROC_LOCK(l);
SA_LWP_STATE_LOCK(l, f);
DPRINTFN(7,("sa_upcall_userret(%d.%d %x) \n", p->p_pid, l->l_lid,
l->l_flag));
KDASSERT((l->l_flag & L_SA_BLOCKING) == 0);
sast = NULL;
if (SIMPLEQ_EMPTY(&vp->savp_upcalls) && vp->savp_wokenq_head != NULL)
sast = sa_getstack(sa);
SCHED_LOCK(s);
if (SIMPLEQ_EMPTY(&vp->savp_upcalls) && vp->savp_wokenq_head != NULL &&
sast != NULL) {
/* Invoke an "unblocked" upcall */
l2 = vp->savp_wokenq_head;
vp->savp_wokenq_head = l2->l_forw;
DPRINTFN(9,("sa_upcall_userret(%d.%d) using stack %p\n",
l->l_proc->p_pid, l->l_lid, sast->sast_stack.ss_sp));
SCHED_UNLOCK(s);
if (p->p_flag & P_WEXIT)
lwp_exit(l);
DPRINTFN(8,("sa_upcall_userret(%d.%d) unblocking %d\n",
p->p_pid, l->l_lid, l2->l_lid));
sau = sadata_upcall_alloc(1);
sau->sau_arg = NULL;
if (p->p_flag & P_WEXIT) {
sadata_upcall_free(sau);
lwp_exit(l);
}
if (sa_upcall0(l, SA_UPCALL_UNBLOCKED, l2, l, 0, NULL,
sau) != 0) {
/*
* We were supposed to deliver an UNBLOCKED
* upcall, but don't have resources to do so.
*/
#ifdef DIAGNOSTIC
printf("sa_upcall_userret: out of upcall resources"
" for %d.%d\n", p->p_pid, l->l_lid);
#endif
sigexit(l, SIGABRT);
/* NOTREACHED */
}
sau->sau_stack = sast->sast_stack;
SIMPLEQ_INSERT_TAIL(&vp->savp_upcalls, sau, sau_next);
l2->l_flag &= ~L_SA_BLOCKING;
SCHED_LOCK(s);
sa_putcachelwp(p, l2); /* PHOLD from sa_setwoken */
SCHED_UNLOCK(s);
} else {
SCHED_UNLOCK(s);
if (sast)
sa_setstackfree(sast, sa);
}
KDASSERT(vp->savp_lwp == l);
while (!SIMPLEQ_EMPTY(&vp->savp_upcalls))
sa_makeupcalls(l);
if (vp->savp_wokenq_head == NULL)
l->l_flag &= ~L_SA_UPCALL;
SA_LWP_STATE_UNLOCK(l, f);
KERNEL_PROC_UNLOCK(l);
return;
}
static __inline void
sa_makeupcalls(struct lwp *l)
{
struct lwp *l2, *eventq;
struct proc *p;
struct sadata *sa;
struct sadata_vp *vp;
struct sa_t **sapp, *sap;
struct sa_t self_sa;
struct sa_t *sas[3], *sasp;
struct sadata_upcall *sau;
union sau_state e_ss;
void *stack, *ap;
ucontext_t u, *up;
size_t sz;
int i, nint, nevents, s, type;
p = l->l_proc;
sa = p->p_sa;
vp = l->l_savp;
sau = SIMPLEQ_FIRST(&vp->savp_upcalls);
SIMPLEQ_REMOVE_HEAD(&vp->savp_upcalls, sau_next);
if (sau->sau_flags & SAU_FLAG_DEFERRED_EVENT)
sa_upcall_getstate(&sau->sau_event,
sau->sau_event.ss_deferred.ss_lwp);
if (sau->sau_flags & SAU_FLAG_DEFERRED_INTERRUPTED)
sa_upcall_getstate(&sau->sau_interrupted,
sau->sau_interrupted.ss_deferred.ss_lwp);
#ifdef __MACHINE_STACK_GROWS_UP
stack = sau->sau_stack.ss_sp;
#else
stack = (caddr_t)sau->sau_stack.ss_sp + sau->sau_stack.ss_size;
#endif
stack = STACK_ALIGN(stack, ALIGNBYTES);
self_sa.sa_id = l->l_lid;
self_sa.sa_cpu = vp->savp_id;
sas[0] = &self_sa;
nevents = 0;
nint = 0;
if (sau->sau_event.ss_captured.ss_sa.sa_context != NULL) {
if (copyout(&sau->sau_event.ss_captured.ss_ctx,
sau->sau_event.ss_captured.ss_sa.sa_context,
sizeof(ucontext_t)) != 0) {
#ifdef DIAGNOSTIC
printf("sa_makeupcalls(%d.%d): couldn't copyout"
" context of event LWP %d\n",
p->p_pid, l->l_lid,
sau->sau_event.ss_captured.ss_sa.sa_id);
#endif
sigexit(l, SIGILL);
/* NOTREACHED */
}
sas[1] = &sau->sau_event.ss_captured.ss_sa;
nevents = 1;
}
if (sau->sau_interrupted.ss_captured.ss_sa.sa_context != NULL) {
KDASSERT(sau->sau_interrupted.ss_captured.ss_sa.sa_context !=
sau->sau_event.ss_captured.ss_sa.sa_context);
if (copyout(&sau->sau_interrupted.ss_captured.ss_ctx,
sau->sau_interrupted.ss_captured.ss_sa.sa_context,
sizeof(ucontext_t)) != 0) {
#ifdef DIAGNOSTIC
printf("sa_makeupcalls(%d.%d): couldn't copyout"
" context of interrupted LWP %d\n",
p->p_pid, l->l_lid,
sau->sau_interrupted.ss_captured.ss_sa.sa_id);
#endif
sigexit(l, SIGILL);
/* NOTREACHED */
}
sas[2] = &sau->sau_interrupted.ss_captured.ss_sa;
nint = 1;
}
eventq = NULL;
if (sau->sau_type == SA_UPCALL_UNBLOCKED) {
SCHED_LOCK(s);
eventq = vp->savp_wokenq_head;
vp->savp_wokenq_head = NULL;
SCHED_UNLOCK(s);
l2 = eventq;
while (l2 != NULL) {
nevents++;
l2 = l2->l_forw;
}
}
/* Copy out the activation's ucontext */
u.uc_stack = sau->sau_stack;
u.uc_flags = _UC_STACK;
up = (void *)STACK_ALLOC(stack, sizeof(ucontext_t));
stack = STACK_GROW(stack, sizeof(ucontext_t));
if (copyout(&u, up, sizeof(ucontext_t)) != 0) {
sadata_upcall_free(sau);
#ifdef DIAGNOSTIC
printf("sa_makeupcalls: couldn't copyout activation"
" ucontext for %d.%d to %p\n", l->l_proc->p_pid, l->l_lid,
up);
#endif
sigexit(l, SIGILL);
/* NOTREACHED */
}
sas[0]->sa_context = up;
/* Next, copy out the sa_t's and pointers to them. */
sz = (1 + nevents + nint) * sizeof(struct sa_t);
sap = (void *)STACK_ALLOC(stack, sz);
sap += 1 + nevents + nint;
stack = STACK_GROW(stack, sz);
sz = (1 + nevents + nint) * sizeof(struct sa_t *);
sapp = (void *)STACK_ALLOC(stack, sz);
sapp += 1 + nevents + nint;
stack = STACK_GROW(stack, sz);
KDASSERT(nint <= 1);
for (i = nevents + nint; i >= 0; i--) {
sap--;
sapp--;
if (i == 1 + nevents) /* interrupted sa */
sasp = sas[2];
else if (i <= 1) /* self_sa and event sa */
sasp = sas[i];
else { /* extra sas */
KDASSERT(sau->sau_type == SA_UPCALL_UNBLOCKED);
KDASSERT(eventq != NULL);
l2 = eventq;
KDASSERT(l2 != NULL);
eventq = l2->l_forw;
DPRINTFN(8,("sa_makeupcalls(%d.%d) unblocking extra %d\n",
p->p_pid, l->l_lid, l2->l_lid));
sa_upcall_getstate(&e_ss, l2);
SCHED_LOCK(s);
l2->l_flag &= ~L_SA_BLOCKING;
sa_putcachelwp(p, l2); /* PHOLD from sa_setwoken */
SCHED_UNLOCK(s);
if (copyout(&e_ss.ss_captured.ss_ctx,
e_ss.ss_captured.ss_sa.sa_context,
sizeof(ucontext_t)) != 0) {
#ifdef DIAGNOSTIC
printf("sa_makeupcalls(%d.%d): couldn't copyout"
" context of event LWP %d\n",
p->p_pid, l->l_lid, e_ss.ss_captured.ss_sa.sa_id);
#endif
sigexit(l, SIGILL);
/* NOTREACHED */
}
sasp = &e_ss.ss_captured.ss_sa;
}
if ((copyout(sasp, sap, sizeof(struct sa_t)) != 0) ||
(copyout(&sap, sapp, sizeof(struct sa_t *)) != 0)) {
/* Copying onto the stack didn't work. Die. */
sadata_upcall_free(sau);
#ifdef DIAGNOSTIC
printf("sa_makeupcalls: couldn't copyout sa_t "
"%d for %d.%d\n", i, p->p_pid, l->l_lid);
#endif
sigexit(l, SIGILL);
/* NOTREACHED */
}
}
KDASSERT(eventq == NULL);
/* Copy out the arg, if any */
/* xxx assume alignment works out; everything so far has been
* a structure, so...
*/
if (sau->sau_arg) {
ap = STACK_ALLOC(stack, sau->sau_argsize);
stack = STACK_GROW(stack, sau->sau_argsize);
if (copyout(sau->sau_arg, ap, sau->sau_argsize) != 0) {
/* Copying onto the stack didn't work. Die. */
sadata_upcall_free(sau);
#ifdef DIAGNOSTIC
printf("sa_makeupcalls(%d.%d): couldn't copyout"
" sadata_upcall arg %p size %ld to %p \n",
p->p_pid, l->l_lid,
sau->sau_arg, (long) sau->sau_argsize, ap);
#endif
sigexit(l, SIGILL);
/* NOTREACHED */
}
} else {
ap = NULL;
#ifdef __hppa__
stack = STACK_ALIGN(stack, HPPA_FRAME_SIZE);
#endif
}
type = sau->sau_type;
sadata_upcall_free(sau);
DPRINTFN(7,("sa_makeupcalls(%d.%d): type %d\n", p->p_pid,
l->l_lid, type));
cpu_upcall(l, type, nevents, nint, sapp, ap, stack, sa->sa_upcall);
l->l_flag &= ~L_SA_YIELD;
}
static void
sa_setwoken(struct lwp *l)
{
struct lwp *l2, *vp_lwp;
struct proc *p;
struct sadata *sa;
struct sadata_vp *vp;
int s;
SCHED_LOCK(s);
if ((l->l_flag & L_SA_BLOCKING) == 0) {
SCHED_UNLOCK(s);
return;
}
p = l->l_proc;
sa = p->p_sa;
vp = l->l_savp;
vp_lwp = vp->savp_lwp;
l2 = NULL;
KDASSERT(vp_lwp != NULL);
DPRINTFN(3,("sa_setwoken(%d.%d) woken, flags %x, vp %d\n",
l->l_proc->p_pid, l->l_lid, l->l_flag,
vp_lwp->l_lid));
#if notyet
if (vp_lwp->l_flag & L_SA_IDLE) {
KDASSERT((vp_lwp->l_flag & L_SA_UPCALL) == 0);
KDASSERT(vp->savp_wokenq_head == NULL);
DPRINTFN(3,("sa_setwoken(%d.%d) repossess: idle vp_lwp %d state %d\n",
l->l_proc->p_pid, l->l_lid,
vp_lwp->l_lid, vp_lwp->l_stat));
vp_lwp->l_flag &= ~L_SA_IDLE;
SCHED_UNLOCK(s);
return;
}
#endif
DPRINTFN(3,("sa_setwoken(%d.%d) put on wokenq: vp_lwp %d state %d\n",
l->l_proc->p_pid, l->l_lid, vp_lwp->l_lid,
vp_lwp->l_stat));
PHOLD(l);
if (vp->savp_wokenq_head == NULL)
vp->savp_wokenq_head = l;
else
*vp->savp_wokenq_tailp = l;
*(vp->savp_wokenq_tailp = &l->l_forw) = NULL;
switch (vp_lwp->l_stat) {
case LSONPROC:
if (vp_lwp->l_flag & L_SA_UPCALL)
break;
vp_lwp->l_flag |= L_SA_UPCALL;
if (vp_lwp->l_flag & L_SA_YIELD)
break;
/* XXX IPI vp_lwp->l_cpu */
break;
case LSSLEEP:
if (vp_lwp->l_flag & L_SA_IDLE) {
vp_lwp->l_flag &= ~L_SA_IDLE;
vp_lwp->l_flag |= L_SA_UPCALL;
setrunnable(vp_lwp);
break;
}
vp_lwp->l_flag |= L_SA_UPCALL;
break;
case LSSUSPENDED:
#ifdef DIAGNOSTIC
printf("sa_setwoken(%d.%d) vp lwp %d LSSUSPENDED\n",
l->l_proc->p_pid, l->l_lid, vp_lwp->l_lid);
#endif
break;
case LSSTOP:
vp_lwp->l_flag |= L_SA_UPCALL;
break;
case LSRUN:
if (vp_lwp->l_flag & L_SA_UPCALL)
break;
vp_lwp->l_flag |= L_SA_UPCALL;
if (vp_lwp->l_flag & L_SA_YIELD)
break;
if (vp_lwp->l_slptime > 1) {
void updatepri(struct lwp *);
updatepri(vp_lwp);
}
vp_lwp->l_slptime = 0;
if (vp_lwp->l_flag & L_INMEM) {
if (vp_lwp->l_cpu == curcpu())
l2 = vp_lwp;
else
need_resched(vp_lwp->l_cpu);
} else
sched_wakeup(&proc0);
break;
default:
panic("sa_vp LWP not sleeping/onproc/runnable");
}
l->l_stat = LSSUSPENDED;
p->p_nrlwps--;
mi_switch(l, l2);
/* maybe NOTREACHED */
SCHED_ASSERT_UNLOCKED();
splx(s);
if (p->p_flag & P_WEXIT)
lwp_exit(l);
}
static struct lwp *
sa_vp_repossess(struct lwp *l)
{
struct lwp *l2;
struct proc *p = l->l_proc;
struct sadata_vp *vp = l->l_savp;
SCHED_ASSERT_LOCKED();
/*
* Put ourselves on the virtual processor and note that the
* previous occupant of that position was interrupted.
*/
l2 = vp->savp_lwp;
vp->savp_lwp = l;
if (l2->l_flag & L_SA_YIELD)
l2->l_flag &= ~(L_SA_YIELD|L_SA_IDLE);
DPRINTFN(1,("sa_vp_repossess(%d.%d) vp lwp %d state %d\n",
p->p_pid, l->l_lid, l2->l_lid, l2->l_stat));
KDASSERT(l2 != l);
if (l2) {
switch (l2->l_stat) {
case LSRUN:
remrunqueue(l2);
p->p_nrlwps--;
break;
case LSSLEEP:
unsleep(l2);
l2->l_flag &= ~L_SINTR;
break;
case LSSUSPENDED:
#ifdef DIAGNOSTIC
printf("sa_vp_repossess(%d.%d) vp lwp %d LSSUSPENDED\n",
l->l_proc->p_pid, l->l_lid, l2->l_lid);
#endif
break;
#ifdef DIAGNOSTIC
default:
panic("SA VP %d.%d is in state %d, not running"
" or sleeping\n", p->p_pid, l2->l_lid,
l2->l_stat);
#endif
}
l2->l_stat = LSSUSPENDED;
}
return l2;
}
#ifdef DEBUG
int debug_print_sa(struct proc *);
int debug_print_lwp(struct lwp *);
int debug_print_proc(int);
int
debug_print_proc(int pid)
{
struct proc *p;
p = pfind(pid);
if (p == NULL)
printf("No process %d\n", pid);
else
debug_print_sa(p);
return 0;
}
int
debug_print_sa(struct proc *p)
{
struct lwp *l;
struct sadata *sa;
struct sadata_vp *vp;
printf("Process %d (%s), state %d, address %p, flags %x\n",
p->p_pid, p->p_comm, p->p_stat, p, p->p_flag);
printf("LWPs: %d (%d running, %d zombies)\n",
p->p_nlwps, p->p_nrlwps, p->p_nzlwps);
LIST_FOREACH(l, &p->p_lwps, l_sibling)
debug_print_lwp(l);
sa = p->p_sa;
if (sa) {
SLIST_FOREACH(vp, &sa->sa_vps, savp_next) {
if (vp->savp_lwp)
printf("SA VP: %d %s\n", vp->savp_lwp->l_lid,
vp->savp_lwp->l_flag & L_SA_YIELD ?
(vp->savp_lwp->l_flag & L_SA_IDLE ?
"idle" : "yielding") : "");
printf("SAs: %d cached LWPs\n", vp->savp_ncached);
LIST_FOREACH(l, &vp->savp_lwpcache, l_sibling)
debug_print_lwp(l);
}
}
return 0;
}
int
debug_print_lwp(struct lwp *l)
{
printf("LWP %d address %p ", l->l_lid, l);
printf("state %d flags %x ", l->l_stat, l->l_flag);
if (l->l_wchan)
printf("wait %p %s", l->l_wchan, l->l_wmesg);
printf("\n");
return 0;
}
#endif