version 1.19, 2008/01/17 17:43:14 |
version 1.20, 2008/01/28 18:35:49 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
|
|
/* |
/* |
* Copyright (c) 2006 Antti Kantee. All Rights Reserved. |
* Copyright (c) 2006, 2007, 2008 Antti Kantee. All Rights Reserved. |
|
* |
|
* Development of this software was supported by the |
|
* Research Foundation of Helsinki University of Technology |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
Line 35 __RCSID("$NetBSD$"); |
|
Line 38 __RCSID("$NetBSD$"); |
|
|
|
#include <assert.h> |
#include <assert.h> |
#include <errno.h> |
#include <errno.h> |
#ifdef PUFFS_WITH_THREADS |
|
#include <pthread.h> |
|
#endif |
|
#include <puffs.h> |
#include <puffs.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
Line 48 __RCSID("$NetBSD$"); |
|
Line 48 __RCSID("$NetBSD$"); |
|
#include "puffs_priv.h" |
#include "puffs_priv.h" |
|
|
/* |
/* |
|
* Set the following to 1 to not handle each request on a separate |
|
* stack. This is highly volatile kludge, therefore no external |
|
* interface. |
|
*/ |
|
int puffs_fakecc; |
|
|
|
/* |
* user stuff |
* user stuff |
*/ |
*/ |
|
|
|
/* |
|
* So, we need to get back to where we came from. This can happen in two |
|
* different ways: |
|
* 1) PCC_MLCONT is set, in which case we need to go to the mainloop |
|
* 2) It is not set, and we simply jump to pcc_uc_ret. |
|
*/ |
void |
void |
puffs_cc_yield(struct puffs_cc *pcc) |
puffs_cc_yield(struct puffs_cc *pcc) |
{ |
{ |
|
struct puffs_cc *jumpcc; |
|
int rv; |
|
|
|
assert(puffs_fakecc == 0); |
|
|
assert(pcc->pcc_flags & PCC_REALCC); |
|
pcc->pcc_flags &= ~PCC_BORROWED; |
pcc->pcc_flags &= ~PCC_BORROWED; |
|
|
/* romanes eunt domus */ |
/* romanes eunt domus */ |
swapcontext(&pcc->pcc_uc, &pcc->pcc_uc_ret); |
if ((pcc->pcc_flags & PCC_MLCONT) == 0) { |
|
swapcontext(&pcc->pcc_uc, &pcc->pcc_uc_ret); |
|
} else { |
|
pcc->pcc_flags &= ~PCC_MLCONT; |
|
rv = puffs__cc_create(pcc->pcc_pu, puffs__theloop, &jumpcc); |
|
if (rv) |
|
abort(); /* p-p-p-pa-pa-panic (XXX: fixme) */ |
|
swapcontext(&pcc->pcc_uc, &jumpcc->pcc_uc); |
|
} |
} |
} |
|
|
|
/* |
|
* Internal continue routine. This has slightly different semantics. |
|
* We simply make our cc available in the freelist and jump to the |
|
* indicated pcc. |
|
*/ |
void |
void |
puffs_cc_continue(struct puffs_cc *pcc) |
puffs__cc_cont(struct puffs_cc *pcc) |
{ |
{ |
|
struct puffs_cc *mycc; |
|
|
assert(pcc->pcc_flags & PCC_REALCC); |
mycc = puffs_cc_getcc(pcc->pcc_pu); |
|
|
|
/* |
|
* XXX: race between setcontenxt() and recycle if |
|
* we go multithreaded |
|
*/ |
|
puffs__cc_destroy(mycc, 1); |
|
pcc->pcc_flags |= PCC_MLCONT; |
|
setcontext(&pcc->pcc_uc); |
|
} |
|
|
|
void |
|
puffs_cc_continue(struct puffs_cc *pcc) |
|
{ |
|
|
/* ramble on */ |
/* ramble on */ |
swapcontext(&pcc->pcc_uc_ret, &pcc->pcc_uc); |
if (puffs_fakecc) |
|
pcc->pcc_func(pcc->pcc_farg); |
|
else |
|
swapcontext(&pcc->pcc_uc_ret, &pcc->pcc_uc); |
} |
} |
|
|
/* |
/* |
Line 80 puffs_cc_continue(struct puffs_cc *pcc) |
|
Line 126 puffs_cc_continue(struct puffs_cc *pcc) |
|
* yield again). |
* yield again). |
*/ |
*/ |
void |
void |
puffs_goto(struct puffs_cc *loanpcc) |
puffs__goto(struct puffs_cc *loanpcc) |
{ |
{ |
|
|
assert(loanpcc->pcc_flags & PCC_REALCC); |
|
loanpcc->pcc_flags |= PCC_BORROWED; |
loanpcc->pcc_flags |= PCC_BORROWED; |
|
|
swapcontext(&loanpcc->pcc_uc_ret, &loanpcc->pcc_uc); |
swapcontext(&loanpcc->pcc_uc_ret, &loanpcc->pcc_uc); |
Line 114 puffs_cc_getcaller(struct puffs_cc *pcc, |
|
Line 159 puffs_cc_getcaller(struct puffs_cc *pcc, |
|
return 0; |
return 0; |
} |
} |
|
|
#ifdef PUFFS_WITH_THREADS |
|
int pthread__stackid_setup(void *, size_t, pthread_t *); |
|
#endif |
|
|
|
/* for fakecc-users, need only one */ |
|
static struct puffs_cc fakecc; |
static struct puffs_cc fakecc; |
|
|
int |
static struct puffs_cc * |
puffs_cc_create(struct puffs_usermount *pu, struct puffs_framebuf *pb, |
slowccalloc(struct puffs_usermount *pu) |
int type, struct puffs_cc **pccp) |
|
{ |
{ |
struct puffs_cc *volatile pcc; |
struct puffs_cc *volatile pcc; |
|
void *sp; |
size_t stacksize = 1<<pu->pu_cc_stackshift; |
size_t stacksize = 1<<pu->pu_cc_stackshift; |
long psize = sysconf(_SC_PAGESIZE); |
long psize = sysconf(_SC_PAGESIZE); |
stack_t *st; |
|
void *volatile sp; |
|
|
|
#ifdef PUFFS_WITH_THREADS |
|
extern size_t pthread__stacksize; |
|
stacksize = pthread__stacksize; |
|
#endif |
|
|
|
/* Do we have a cached copy? */ |
|
if (pu->pu_cc_nstored) { |
|
pcc = LIST_FIRST(&pu->pu_ccmagazin); |
|
assert(pcc != NULL); |
|
|
|
LIST_REMOVE(pcc, pcc_rope); |
if (puffs_fakecc) |
pu->pu_cc_nstored--; |
return &fakecc; |
goto finalize; |
|
} |
|
|
|
/* |
|
* There are two paths and in the long run we don't have time to |
|
* change the one we're on. For non-real cc's, we just simply use |
|
* a static copy. For the other cases, we mmap the stack and |
|
* manually reserve a bit from the top for the data structure |
|
* (or, well, the bottom). |
|
* |
|
* XXX: threaded mode doesn't work very well now. Not that it's |
|
* supported anyhow. |
|
*/ |
|
if (type == PCC_FAKECC) { |
|
pcc = &fakecc; |
|
sp = NULL; |
|
} else { |
|
sp = mmap(NULL, stacksize, PROT_READ|PROT_WRITE, |
|
MAP_ANON|MAP_PRIVATE|MAP_ALIGNED(pu->pu_cc_stackshift), |
|
-1, 0); |
|
if (sp == MAP_FAILED) |
|
return -1; |
|
|
|
pcc = sp; |
sp = mmap(NULL, stacksize, PROT_READ|PROT_WRITE, |
mprotect((uint8_t *)sp + psize, (size_t)psize, PROT_NONE); |
MAP_ANON|MAP_PRIVATE|MAP_ALIGNED(pu->pu_cc_stackshift), -1, 0); |
} |
if (sp == MAP_FAILED) |
|
return NULL; |
|
|
|
pcc = sp; |
memset(pcc, 0, sizeof(struct puffs_cc)); |
memset(pcc, 0, sizeof(struct puffs_cc)); |
pcc->pcc_pu = pu; |
|
|
|
/* Not a real cc? Don't need to init more */ |
mprotect((uint8_t *)sp + psize, (size_t)psize, PROT_NONE); |
if (type != PCC_REALCC) |
|
goto out; |
|
|
|
/* initialize both ucontext's */ |
/* initialize both ucontext's */ |
if (getcontext(&pcc->pcc_uc) == -1) { |
if (getcontext(&pcc->pcc_uc) == -1) { |
munmap(pcc, stacksize); |
munmap(pcc, stacksize); |
return -1; |
return NULL; |
} |
} |
if (getcontext(&pcc->pcc_uc_ret) == -1) { |
if (getcontext(&pcc->pcc_uc_ret) == -1) { |
munmap(pcc, stacksize); |
munmap(pcc, stacksize); |
return -1; |
return NULL; |
} |
} |
|
|
#ifdef PUFFS_WITH_THREADS |
return pcc; |
{ |
} |
pthread_t pt; |
|
extern int __isthreaded; |
|
if (__isthreaded) |
|
pthread__stackid_setup(sp, stacksize /* XXXb0rked */, &pt); |
|
} |
|
#endif |
|
|
|
finalize: |
int |
assert(pcc->pcc_pu == pu); |
puffs__cc_create(struct puffs_usermount *pu, puffs_ccfunc func, |
|
struct puffs_cc **pccp) |
|
{ |
|
struct puffs_cc *pcc; |
|
size_t stacksize = 1<<pu->pu_cc_stackshift; |
|
stack_t *st; |
|
|
/* return here. it won't actually be "here" due to swapcontext() */ |
/* Do we have a cached copy? */ |
pcc->pcc_uc.uc_link = &pcc->pcc_uc_ret; |
if (pu->pu_cc_nstored == 0) { |
|
pcc = slowccalloc(pu); |
|
if (pcc == NULL) |
|
return -1; |
|
pcc->pcc_pu = pu; |
|
} else { |
|
pcc = LIST_FIRST(&pu->pu_ccmagazin); |
|
assert(pcc != NULL); |
|
|
/* setup stack |
LIST_REMOVE(pcc, pcc_rope); |
* |
pu->pu_cc_nstored--; |
* XXX: I guess this should theoretically be preserved by |
} |
* swapcontext(). However, it gets lost. So reinit it. |
assert(pcc->pcc_pu == pu); |
*/ |
|
st = &pcc->pcc_uc.uc_stack; |
|
st->ss_sp = pcc; |
|
st->ss_size = stacksize; |
|
st->ss_flags = 0; |
|
|
|
/* |
if (puffs_fakecc) { |
* Give us an initial context to jump to. |
pcc->pcc_func = func; |
* |
pcc->pcc_farg = pcc; |
* XXX: Our manual page says that portable code shouldn't rely on |
} else { |
* being able to pass pointers through makecontext(). kjk says |
/* link context */ |
* that NetBSD code doesn't need to worry about this. uwe says |
pcc->pcc_uc.uc_link = &pcc->pcc_uc_ret; |
* it would be like putting a "keep away from children" sign on a |
|
* box of toys. |
|
*/ |
|
makecontext(&pcc->pcc_uc, (void *)puffs_calldispatcher, |
|
1, (uintptr_t)pcc); |
|
|
|
out: |
/* setup stack |
pcc->pcc_pb = pb; |
* |
pcc->pcc_flags = type; |
* XXX: I guess this should theoretically be preserved by |
|
* swapcontext(). However, it gets lost. So reinit it. |
|
*/ |
|
st = &pcc->pcc_uc.uc_stack; |
|
st->ss_sp = pcc; |
|
st->ss_size = stacksize; |
|
st->ss_flags = 0; |
|
|
|
/* |
|
* Give us an initial context to jump to. |
|
* |
|
* Our manual page says that portable code shouldn't |
|
* rely on being able to pass pointers through makecontext(). |
|
* kjk says that NetBSD code doesn't need to worry about this. |
|
* uwe says it would be like putting a "keep away from |
|
* children" sign on a box of toys. |
|
*/ |
|
makecontext(&pcc->pcc_uc, (void *)func, 1, (uintptr_t)pcc); |
|
} |
|
|
*pccp = pcc; |
*pccp = pcc; |
return 0; |
return 0; |
} |
} |
|
|
void |
void |
puffs_cc_setcaller(struct puffs_cc *pcc, pid_t pid, lwpid_t lid) |
puffs__cc_setcaller(struct puffs_cc *pcc, pid_t pid, lwpid_t lid) |
{ |
{ |
|
|
pcc->pcc_pid = pid; |
pcc->pcc_pid = pid; |
Line 242 puffs_cc_setcaller(struct puffs_cc *pcc, |
|
Line 261 puffs_cc_setcaller(struct puffs_cc *pcc, |
|
} |
} |
|
|
void |
void |
puffs_cc_destroy(struct puffs_cc *pcc) |
puffs__cc_destroy(struct puffs_cc *pcc, int nonuke) |
{ |
{ |
struct puffs_usermount *pu = pcc->pcc_pu; |
struct puffs_usermount *pu = pcc->pcc_pu; |
size_t stacksize = 1<<pu->pu_cc_stackshift; |
size_t stacksize = 1<<pu->pu_cc_stackshift; |
|
|
/* not over limit? stuff away in the store */ |
assert(pcc->pcc_flags = 0); |
if (pu->pu_cc_nstored < PUFFS_CCMAXSTORE |
|
&& pcc->pcc_flags & PCC_REALCC) { |
/* not over limit? stuff away in the store, otherwise nuke */ |
pcc->pcc_flags &= ~PCC_DONE; |
if (nonuke || pu->pu_cc_nstored < PUFFS_CCMAXSTORE) { |
|
pcc->pcc_pb = NULL; |
LIST_INSERT_HEAD(&pu->pu_ccmagazin, pcc, pcc_rope); |
LIST_INSERT_HEAD(&pu->pu_ccmagazin, pcc, pcc_rope); |
pu->pu_cc_nstored++; |
pu->pu_cc_nstored++; |
return; |
} else { |
} |
assert(!puffs_fakecc); |
|
|
/* else: just dump it */ |
|
|
|
#ifdef PUFFS_WITH_THREADS |
|
extern size_t pthread__stacksize; |
|
stacksize = pthread__stacksize; |
|
#endif |
|
|
|
if ((pcc->pcc_flags & PCC_FAKECC) == 0) |
|
munmap(pcc, stacksize); |
munmap(pcc, stacksize); |
|
} |
} |
} |
|
|
struct puffs_cc * |
struct puffs_cc * |
puffs_cc_getcc(struct puffs_usermount *pu) |
puffs_cc_getcc(struct puffs_usermount *pu) |
{ |
{ |
extern int puffs_fakecc, puffs_usethreads; |
|
size_t stacksize = 1<<pu->pu_cc_stackshift; |
size_t stacksize = 1<<pu->pu_cc_stackshift; |
uintptr_t bottom; |
uintptr_t bottom; |
|
|
if (puffs_fakecc) |
if (puffs_fakecc) |
return &fakecc; |
return &fakecc; |
#ifndef PUFFS_WITH_THREADS |
|
if (puffs_usethreads) |
|
return &fakecc; |
|
#endif |
|
|
|
bottom = ((uintptr_t)&bottom) & ~(stacksize-1); |
bottom = ((uintptr_t)&bottom) & ~(stacksize-1); |
return (struct puffs_cc *)bottom; |
return (struct puffs_cc *)bottom; |