Add proof-of-concept code for enabling system calls to rump virtual
kernels running in other processes on the same machine or on an entirely different host. I wrote this a while ago and am now committing it mainly to avoid losing it. It works, but could do with a little tuning here and there. What this will hopefully eventually buy us is the ability to use standard userland tools to configure rump kernels, e.g. ifconfig(8) and route(8) could be used to configure the networking stack provided by a rump kernel. Also some distributed OS implications may apply. fun fact: a system call which just does copyin/copyout takes >1000x longer when made over the LAN as compared to when made on the same machine.
This commit is contained in:
parent
c51cd914ad
commit
a22ec3808b
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: rump.h,v 1.12 2009/03/27 13:47:53 pooka Exp $ */
|
||||
/* $NetBSD: rump.h,v 1.13 2009/04/29 18:00:49 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
|
||||
|
@ -127,6 +127,7 @@ int rump_vfs_fhtovp(struct mount *, struct fid *, struct vnode **);
|
|||
int rump_vfs_vptofh(struct vnode *, struct fid *, size_t *);
|
||||
void rump_vfs_syncwait(struct mount *);
|
||||
|
||||
struct lwp *rump_newproc_switch(void);
|
||||
struct lwp *rump_setup_curlwp(pid_t, lwpid_t, int);
|
||||
struct lwp *rump_get_curlwp(void);
|
||||
void rump_clear_curlwp(void);
|
||||
|
@ -141,6 +142,8 @@ int rump_virtif_create(int);
|
|||
|
||||
typedef int (*rump_sysproxy_t)(int, void *, uint8_t *, size_t, register_t *);
|
||||
int rump_sysproxy_set(rump_sysproxy_t, void *);
|
||||
int rump_sysproxy_socket_setup_client(int);
|
||||
int rump_sysproxy_socket_setup_server(int);
|
||||
|
||||
/*
|
||||
* Begin rump syscall conditionals. Yes, something a little better
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: Makefile.rumpkern,v 1.43 2009/04/16 14:07:17 pooka Exp $
|
||||
# $NetBSD: Makefile.rumpkern,v 1.44 2009/04/29 17:51:47 pooka Exp $
|
||||
#
|
||||
|
||||
.include "${RUMPTOP}/Makefile.rump"
|
||||
|
@ -14,7 +14,8 @@ LIB= rump
|
|||
#
|
||||
# Source modules, first the ones specifically implemented for librump.
|
||||
#
|
||||
SRCS= rump.c emul.c intr.c locks.c ltsleep.c percpu.c pool.c sleepq.c vm.c
|
||||
SRCS= rump.c emul.c intr.c locks.c ltsleep.c percpu.c pool.c \
|
||||
sleepq.c sysproxy_socket.c vm.c
|
||||
|
||||
# stubs
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: emul.c,v 1.87 2009/04/26 20:41:24 pooka Exp $ */
|
||||
/* $NetBSD: emul.c,v 1.88 2009/04/29 17:51:47 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
|
||||
|
@ -28,7 +28,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: emul.c,v 1.87 2009/04/26 20:41:24 pooka Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: emul.c,v 1.88 2009/04/29 17:51:47 pooka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/malloc.h>
|
||||
|
@ -125,7 +125,10 @@ int
|
|||
copyin(const void *uaddr, void *kaddr, size_t len)
|
||||
{
|
||||
|
||||
if (curproc->p_vmspace == &rump_vmspace)
|
||||
memcpy(kaddr, uaddr, len);
|
||||
else
|
||||
rump_sysproxy_copyin(uaddr, kaddr, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -133,7 +136,10 @@ int
|
|||
copyout(const void *kaddr, void *uaddr, size_t len)
|
||||
{
|
||||
|
||||
if (curproc->p_vmspace == &rump_vmspace)
|
||||
memcpy(uaddr, kaddr, len);
|
||||
else
|
||||
rump_sysproxy_copyout(kaddr, uaddr, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -148,7 +154,10 @@ int
|
|||
copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done)
|
||||
{
|
||||
|
||||
if (curproc->p_vmspace == &rump_vmspace)
|
||||
strlcpy(kaddr, uaddr, len);
|
||||
else
|
||||
rump_sysproxy_copyin(uaddr, kaddr, len);
|
||||
if (done)
|
||||
*done = strlen(kaddr)+1; /* includes termination */
|
||||
return 0;
|
||||
|
@ -158,7 +167,10 @@ int
|
|||
copyoutstr(const void *kaddr, void *uaddr, size_t len, size_t *done)
|
||||
{
|
||||
|
||||
if (curproc->p_vmspace == &rump_vmspace)
|
||||
strlcpy(uaddr, kaddr, len);
|
||||
else
|
||||
rump_sysproxy_copyout(kaddr, uaddr, len);
|
||||
if (done)
|
||||
*done = strlen(uaddr)+1; /* includes termination */
|
||||
return 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: rump.c,v 1.103 2009/04/29 15:49:28 pooka Exp $ */
|
||||
/* $NetBSD: rump.c,v 1.104 2009/04/29 17:51:47 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
|
||||
|
@ -28,7 +28,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: rump.c,v 1.103 2009/04/29 15:49:28 pooka Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: rump.c,v 1.104 2009/04/29 17:51:47 pooka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/atomic.h>
|
||||
|
@ -303,6 +303,25 @@ rump_uio_free(struct uio *uio)
|
|||
return resid;
|
||||
}
|
||||
|
||||
/* public interface */
|
||||
static pid_t nextpid = 1;
|
||||
struct lwp *
|
||||
rump_newproc_switch()
|
||||
{
|
||||
struct lwp *oldlwp = curlwp;
|
||||
pid_t mypid;
|
||||
|
||||
mypid = atomic_inc_uint_nv(&nextpid);
|
||||
if (__predict_false(mypid == 0))
|
||||
mypid = atomic_inc_uint_nv(&nextpid);
|
||||
|
||||
rumpuser_set_curlwp(NULL);
|
||||
rump_setup_curlwp(mypid, 0, 1);
|
||||
|
||||
return oldlwp;
|
||||
}
|
||||
|
||||
/* rump private */
|
||||
struct lwp *
|
||||
rump_setup_curlwp(pid_t pid, lwpid_t lid, int set)
|
||||
{
|
||||
|
@ -335,17 +354,28 @@ rump_setup_curlwp(pid_t pid, lwpid_t lid, int set)
|
|||
return l;
|
||||
}
|
||||
|
||||
/* rump private. NEEDS WORK! */
|
||||
void
|
||||
rump_set_vmspace(struct vmspace *vm)
|
||||
{
|
||||
struct proc *p = curproc;
|
||||
|
||||
p->p_vmspace = vm;
|
||||
}
|
||||
|
||||
void
|
||||
rump_clear_curlwp(void)
|
||||
{
|
||||
struct lwp *l;
|
||||
struct proc *p;
|
||||
|
||||
l = rumpuser_get_curlwp();
|
||||
if (l->l_proc->p_pid != 0) {
|
||||
p = l->l_proc;
|
||||
if (p->p_pid != 0) {
|
||||
fd_free();
|
||||
rump_proc_vfs_release(l->l_proc);
|
||||
rump_proc_vfs_release(p);
|
||||
rump_cred_destroy(l->l_cred);
|
||||
kmem_free(l->l_proc, sizeof(*l->l_proc));
|
||||
kmem_free(p, sizeof(*p));
|
||||
}
|
||||
kmem_free(l, sizeof(*l));
|
||||
rumpuser_set_curlwp(NULL);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: rump_private.h,v 1.27 2009/04/26 20:41:24 pooka Exp $ */
|
||||
/* $NetBSD: rump_private.h,v 1.28 2009/04/29 17:51:47 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
|
||||
|
@ -31,6 +31,7 @@
|
|||
#define _SYS_RUMP_PRIVATE_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/lwp.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/systm.h>
|
||||
|
@ -54,6 +55,8 @@ extern struct rumpuser_mtx *rump_giantlock;
|
|||
|
||||
extern int rump_threads;
|
||||
|
||||
extern struct sysent rump_sysent[];
|
||||
|
||||
void rumpvm_init(void);
|
||||
void rump_sleepers_init(void);
|
||||
struct vm_page *rumpvm_makepage(struct uvm_object *, voff_t);
|
||||
|
@ -65,6 +68,7 @@ void rump_gettime(struct timespec *);
|
|||
void rump_getuptime(struct timespec *);
|
||||
|
||||
lwpid_t rump_nextlid(void);
|
||||
void rump_set_vmspace(struct vmspace *);
|
||||
|
||||
typedef void (*rump_proc_vfs_init_fn)(struct proc *);
|
||||
typedef void (*rump_proc_vfs_release_fn)(struct proc *);
|
||||
|
@ -73,8 +77,10 @@ rump_proc_vfs_release_fn rump_proc_vfs_release;
|
|||
|
||||
extern struct cpu_info rump_cpu;
|
||||
|
||||
extern struct sysent rump_sysent[];
|
||||
extern rump_sysproxy_t rump_sysproxy;
|
||||
extern void *rump_sysproxy_arg;
|
||||
|
||||
int rump_sysproxy_copyout(const void *, void *, size_t);
|
||||
int rump_sysproxy_copyin(const void *, void *, size_t);
|
||||
|
||||
#endif /* _SYS_RUMP_PRIVATE_H_ */
|
||||
|
|
|
@ -0,0 +1,614 @@
|
|||
/* $NetBSD: sysproxy_socket.c,v 1.1 2009/04/29 17:51:47 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Antti Kantee. All Rights Reserved.
|
||||
*
|
||||
* Development of this software was supported by The Nokia Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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: sysproxy_socket.c,v 1.1 2009/04/29 17:51:47 pooka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <rump/rump.h>
|
||||
#include <rump/rumpuser.h>
|
||||
|
||||
#include "rump_private.h"
|
||||
|
||||
/*
|
||||
* This is a very simple RPC mechanism. It defines:
|
||||
*
|
||||
* 1) system call
|
||||
* 2) copyin
|
||||
* 3) copyout
|
||||
*
|
||||
* Currently all the data is in host format, so this can't really be
|
||||
* used to make cross-site calls. Extending to something like XMLRPC
|
||||
* is possible, but takes some amount of programming effort, since all
|
||||
* the kernel data structures and types are defined in C.
|
||||
*
|
||||
* XXX: yes, this is overall implemented in a very lazy fashion
|
||||
* currently. Hopefully it will get better. And hopefully the
|
||||
* global "sock" will go away, it's quite disgusting.
|
||||
*/
|
||||
|
||||
enum rumprpc { RUMPRPC_SYSCALL, RUMPRPC_SYSCALL_RESP,
|
||||
RUMPRPC_COPYIN, RUMPRPC_COPYIN_RESP,
|
||||
RUMPRPC_COPYOUT, RUMPRPC_COPYOUT_RESP };
|
||||
|
||||
struct rumprpc_head {
|
||||
uint64_t rpch_flen;
|
||||
uint32_t rpch_reqno;
|
||||
int rpch_type;
|
||||
};
|
||||
|
||||
struct rumprpc_sysreq {
|
||||
struct rumprpc_head rpc_head;
|
||||
int rpc_sysnum;
|
||||
uint8_t rpc_data[0];
|
||||
};
|
||||
|
||||
struct rumprpc_sysresp {
|
||||
struct rumprpc_head rpc_head;
|
||||
int rpc_error;
|
||||
register_t rpc_retval;
|
||||
};
|
||||
|
||||
struct rumprpc_copydata {
|
||||
struct rumprpc_head rpc_head;
|
||||
void *rpc_addr;
|
||||
size_t rpc_len;
|
||||
uint8_t rpc_data[0];
|
||||
};
|
||||
|
||||
struct sysproxy_qent {
|
||||
uint32_t reqno;
|
||||
struct rumprpc_head *rpch_resp;
|
||||
|
||||
kcondvar_t cv;
|
||||
TAILQ_ENTRY(sysproxy_qent) entries;
|
||||
};
|
||||
|
||||
static TAILQ_HEAD(, sysproxy_qent) sendq = TAILQ_HEAD_INITIALIZER(sendq);
|
||||
static TAILQ_HEAD(, sysproxy_qent) recvq = TAILQ_HEAD_INITIALIZER(recvq);
|
||||
static bool sendavail = true;
|
||||
static kmutex_t sendmtx, recvmtx;
|
||||
static unsigned reqno;
|
||||
|
||||
static struct sysproxy_qent *
|
||||
get_qent(void)
|
||||
{
|
||||
struct sysproxy_qent *qent;
|
||||
unsigned myreq = atomic_inc_uint_nv(&reqno);
|
||||
|
||||
qent = kmem_alloc(sizeof(*qent), KM_SLEEP);
|
||||
|
||||
qent->reqno = myreq;
|
||||
qent->rpch_resp = NULL;
|
||||
cv_init(&qent->cv, "sproxyq");
|
||||
|
||||
return qent;
|
||||
}
|
||||
|
||||
static void
|
||||
put_qent(struct sysproxy_qent *qent)
|
||||
{
|
||||
|
||||
cv_destroy(&qent->cv);
|
||||
kmem_free(qent, sizeof(*qent));
|
||||
}
|
||||
|
||||
static int
|
||||
write_n(int s, uint8_t *data, size_t dlen)
|
||||
{
|
||||
ssize_t n;
|
||||
size_t done;
|
||||
int error;
|
||||
|
||||
error = 0;
|
||||
for (done = 0; done < dlen; done += n) {
|
||||
n = rumpuser_write(s, data + done, dlen - done, &error);
|
||||
if (n <= 0) {
|
||||
if (n == -1)
|
||||
return error;
|
||||
return ECONNRESET;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
read_n(int s, uint8_t *data, size_t dlen)
|
||||
{
|
||||
ssize_t n;
|
||||
size_t done;
|
||||
int error;
|
||||
|
||||
error = 0;
|
||||
for (done = 0; done < dlen; done += n) {
|
||||
n = rumpuser_read(s, data + done, dlen - done, &error);
|
||||
if (n <= 0) {
|
||||
if (n == -1)
|
||||
return error;
|
||||
return ECONNRESET;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
dosend(int s, struct sysproxy_qent *qent, uint8_t *data, size_t len, bool rq)
|
||||
{
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Send. If the sendq is empty, we send in current thread context.
|
||||
* Otherwise we enqueue ourselves and block until we are able to
|
||||
* send in current thread context, i.e. we are the first in the queue.
|
||||
*/
|
||||
mutex_enter(&sendmtx);
|
||||
if (!sendavail) {
|
||||
TAILQ_INSERT_TAIL(&sendq, qent, entries);
|
||||
while (qent != TAILQ_FIRST(&sendq))
|
||||
cv_wait(&qent->cv, &sendmtx);
|
||||
KASSERT(qent == TAILQ_FIRST(&sendq));
|
||||
TAILQ_REMOVE(&sendq, qent, entries);
|
||||
}
|
||||
sendavail = false;
|
||||
mutex_exit(&sendmtx);
|
||||
|
||||
/*
|
||||
* Put ourselves onto the receive queue already now in case
|
||||
* the response arrives "immediately" after sending.
|
||||
*/
|
||||
if (rq) {
|
||||
mutex_enter(&recvmtx);
|
||||
TAILQ_INSERT_TAIL(&recvq, qent, entries);
|
||||
mutex_exit(&recvmtx);
|
||||
}
|
||||
|
||||
/* XXX: need better error recovery */
|
||||
if ((error = write_n(s, data, len)) != 0)
|
||||
panic("unrecoverable error %d (sloppy)", error);
|
||||
}
|
||||
|
||||
struct wrk {
|
||||
void (*wfn)(void *arg);
|
||||
void *arg;
|
||||
kcondvar_t wrkcv;
|
||||
LIST_ENTRY(wrk) entries;
|
||||
};
|
||||
|
||||
static LIST_HEAD(, wrk) idlewrker = LIST_HEAD_INITIALIZER(idlewrker);
|
||||
|
||||
#define NIDLE_MAX 5
|
||||
|
||||
static kmutex_t wrkmtx;
|
||||
static unsigned nwrk, nidle;
|
||||
|
||||
/*
|
||||
* workers for handling requests. comes with simple little pooling
|
||||
* for threads.
|
||||
*/
|
||||
static void
|
||||
wrkthread(void *arg)
|
||||
{
|
||||
struct wrk *wrk = arg;
|
||||
|
||||
mutex_enter(&wrkmtx);
|
||||
nidle++;
|
||||
for (;;) {
|
||||
while (wrk->wfn == NULL)
|
||||
cv_wait(&wrk->wrkcv, &wrkmtx);
|
||||
nidle--;
|
||||
mutex_exit(&wrkmtx);
|
||||
|
||||
wrk->wfn(wrk->arg);
|
||||
wrk->wfn = NULL;
|
||||
wrk->arg = NULL;
|
||||
|
||||
mutex_enter(&wrkmtx);
|
||||
if (++nidle > NIDLE_MAX) {
|
||||
nidle--;
|
||||
break;
|
||||
}
|
||||
LIST_INSERT_HEAD(&idlewrker, wrk, entries);
|
||||
}
|
||||
nwrk--;
|
||||
mutex_exit(&wrkmtx);
|
||||
|
||||
cv_destroy(&wrk->wrkcv);
|
||||
kmem_free(wrk, sizeof(*wrk));
|
||||
kthread_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue work into a separate thread context. Will create a new
|
||||
* thread if there are none available.
|
||||
*/
|
||||
static void
|
||||
wrkenqueue(void (*wfn)(void *), void *arg)
|
||||
{
|
||||
struct wrk *wrk;
|
||||
int error;
|
||||
|
||||
mutex_enter(&wrkmtx);
|
||||
if (nidle == 0) {
|
||||
nwrk++;
|
||||
if (nwrk > 30)
|
||||
printf("syscall proxy warning: over 30 workers\n");
|
||||
mutex_exit(&wrkmtx);
|
||||
wrk = kmem_zalloc(sizeof(*wrk), KM_SLEEP);
|
||||
cv_init(&wrk->wrkcv, "sproxywrk");
|
||||
retry:
|
||||
error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
|
||||
wrkthread, wrk, NULL, "spw_%d", nwrk);
|
||||
if (error) {
|
||||
printf("kthread_create failed: %d. retry.\n", error);
|
||||
kpause("eagain", false, hz, NULL);
|
||||
goto retry;
|
||||
}
|
||||
} else {
|
||||
wrk = LIST_FIRST(&idlewrker);
|
||||
LIST_REMOVE(wrk, entries);
|
||||
mutex_exit(&wrkmtx);
|
||||
}
|
||||
|
||||
wrk->wfn = wfn;
|
||||
wrk->arg = arg;
|
||||
|
||||
mutex_enter(&wrkmtx);
|
||||
cv_signal(&wrk->wrkcv);
|
||||
mutex_exit(&wrkmtx);
|
||||
}
|
||||
|
||||
static int sock; /* XXXXXX */
|
||||
|
||||
int
|
||||
rump_sysproxy_copyout(const void *kaddr, void *uaddr, size_t len)
|
||||
{
|
||||
struct rumprpc_copydata *req;
|
||||
struct sysproxy_qent *qent = get_qent();
|
||||
size_t totlen = sizeof(*req) + len;
|
||||
|
||||
req = kmem_alloc(totlen, KM_SLEEP);
|
||||
|
||||
req->rpc_head.rpch_flen = totlen;
|
||||
req->rpc_head.rpch_reqno = qent->reqno;
|
||||
req->rpc_head.rpch_type = RUMPRPC_COPYOUT;
|
||||
req->rpc_addr = uaddr;
|
||||
req->rpc_len = len;
|
||||
memcpy(req->rpc_data, kaddr, len);
|
||||
|
||||
/* XXX: handle async? */
|
||||
dosend(sock, qent, (uint8_t *)req, totlen, false);
|
||||
kmem_free(req, totlen);
|
||||
put_qent(qent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rump_sysproxy_copyin(const void *uaddr, void *kaddr, size_t len)
|
||||
{
|
||||
struct sysproxy_qent *qent = get_qent();
|
||||
struct rumprpc_copydata req, *resp;
|
||||
|
||||
/* build request */
|
||||
req.rpc_head.rpch_flen = sizeof(req);
|
||||
req.rpc_head.rpch_reqno = qent->reqno;
|
||||
req.rpc_head.rpch_type = RUMPRPC_COPYIN;
|
||||
|
||||
req.rpc_addr = __UNCONST(uaddr);
|
||||
req.rpc_len = len;
|
||||
|
||||
dosend(sock, qent, (uint8_t *)&req, sizeof(req), true);
|
||||
|
||||
/*
|
||||
* Wake up next sender or just toggle availability
|
||||
*/
|
||||
mutex_enter(&sendmtx);
|
||||
if (TAILQ_EMPTY(&sendq)) {
|
||||
sendavail = true;
|
||||
} else {
|
||||
cv_signal(&TAILQ_FIRST(&sendq)->cv);
|
||||
}
|
||||
mutex_exit(&sendmtx);
|
||||
|
||||
/* Wait for response */
|
||||
mutex_enter(&recvmtx);
|
||||
while (qent->rpch_resp == NULL)
|
||||
cv_wait(&qent->cv, &recvmtx);
|
||||
mutex_exit(&recvmtx);
|
||||
|
||||
resp = (struct rumprpc_copydata *)qent->rpch_resp;
|
||||
/* we trust our kernel */
|
||||
KASSERT(resp->rpc_head.rpch_type == RUMPRPC_COPYIN_RESP);
|
||||
|
||||
memcpy(kaddr, resp->rpc_data, len);
|
||||
kmem_free(resp, resp->rpc_head.rpch_flen);
|
||||
put_qent(qent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vmspace rump_sysproxy_vmspace;
|
||||
|
||||
static void
|
||||
handle_syscall(void *arg)
|
||||
{
|
||||
struct rumprpc_sysreq *req = arg;
|
||||
struct sysproxy_qent *qent = get_qent();
|
||||
struct rumprpc_sysresp resp;
|
||||
struct sysent *callp;
|
||||
struct lwp *mylwp, *l;
|
||||
|
||||
resp.rpc_head.rpch_flen = sizeof(resp);
|
||||
resp.rpc_head.rpch_reqno = req->rpc_head.rpch_reqno;
|
||||
resp.rpc_head.rpch_type = RUMPRPC_SYSCALL_RESP;
|
||||
|
||||
if (__predict_false(req->rpc_sysnum >= SYS_NSYSENT)) {
|
||||
resp.rpc_error = ENOSYS;
|
||||
dosend(sock, qent, (uint8_t *)&resp, sizeof(resp), false);
|
||||
kmem_free(req, req->rpc_head.rpch_flen);
|
||||
put_qent(qent);
|
||||
return;
|
||||
}
|
||||
|
||||
callp = rump_sysent + req->rpc_sysnum;
|
||||
mylwp = rump_newproc_switch();
|
||||
rump_set_vmspace(&rump_sysproxy_vmspace);
|
||||
l = curlwp;
|
||||
|
||||
resp.rpc_retval = 0; /* default */
|
||||
resp.rpc_error = callp->sy_call(l, (void *)req->rpc_data,
|
||||
&resp.rpc_retval);
|
||||
rump_clear_curlwp();
|
||||
rumpuser_set_curlwp(mylwp);
|
||||
kmem_free(req, req->rpc_head.rpch_flen);
|
||||
|
||||
dosend(sock, qent, (uint8_t *)&resp, sizeof(resp), false);
|
||||
put_qent(qent);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_copyin(void *arg)
|
||||
{
|
||||
struct rumprpc_copydata *req = arg;
|
||||
struct sysproxy_qent *qent = get_qent();
|
||||
struct rumprpc_copydata *resp;
|
||||
size_t totlen = sizeof(*resp) + req->rpc_len;
|
||||
|
||||
resp = kmem_alloc(totlen, KM_SLEEP);
|
||||
resp->rpc_head.rpch_flen = totlen;
|
||||
resp->rpc_head.rpch_reqno = req->rpc_head.rpch_reqno;
|
||||
resp->rpc_head.rpch_type = RUMPRPC_COPYIN_RESP;
|
||||
memcpy(resp->rpc_data, req->rpc_addr, req->rpc_len);
|
||||
kmem_free(req, req->rpc_head.rpch_flen);
|
||||
|
||||
dosend(sock, qent, (uint8_t *)resp, totlen, false);
|
||||
kmem_free(resp, totlen);
|
||||
put_qent(qent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Client side. We can either get the results of an earlier syscall or
|
||||
* get a request for a copyin/out.
|
||||
*/
|
||||
static void
|
||||
recvthread(void *arg)
|
||||
{
|
||||
struct rumprpc_head rpch;
|
||||
uint8_t *rpc;
|
||||
int s = (uintptr_t)arg;
|
||||
int error;
|
||||
|
||||
for (;;) {
|
||||
error = read_n(s, (uint8_t *)&rpch, sizeof(rpch));
|
||||
if (error)
|
||||
panic("%d", error);
|
||||
|
||||
rpc = kmem_alloc(rpch.rpch_flen , KM_SLEEP);
|
||||
error = read_n(s, rpc + sizeof(struct rumprpc_head),
|
||||
rpch.rpch_flen - sizeof(struct rumprpc_head));
|
||||
if (error)
|
||||
panic("%d", error);
|
||||
memcpy(rpc, &rpch, sizeof(rpch));
|
||||
|
||||
switch (rpch.rpch_type) {
|
||||
case RUMPRPC_SYSCALL:
|
||||
/* assert server */
|
||||
wrkenqueue(handle_syscall, rpc);
|
||||
break;
|
||||
|
||||
case RUMPRPC_SYSCALL_RESP:
|
||||
/* assert client */
|
||||
{
|
||||
struct sysproxy_qent *qent;
|
||||
|
||||
mutex_enter(&recvmtx);
|
||||
TAILQ_FOREACH(qent, &recvq, entries)
|
||||
if (qent->reqno == rpch.rpch_reqno)
|
||||
break;
|
||||
if (!qent) {
|
||||
mutex_exit(&recvmtx);
|
||||
kmem_free(rpc, rpch.rpch_flen);
|
||||
break;
|
||||
}
|
||||
TAILQ_REMOVE(&recvq, qent, entries);
|
||||
qent->rpch_resp = (void *)rpc;
|
||||
cv_signal(&qent->cv);
|
||||
mutex_exit(&recvmtx);
|
||||
|
||||
break;
|
||||
}
|
||||
case RUMPRPC_COPYIN:
|
||||
/* assert client */
|
||||
wrkenqueue(handle_copyin, rpc);
|
||||
break;
|
||||
|
||||
case RUMPRPC_COPYIN_RESP:
|
||||
/* assert server */
|
||||
{
|
||||
struct sysproxy_qent *qent;
|
||||
|
||||
mutex_enter(&recvmtx);
|
||||
TAILQ_FOREACH(qent, &recvq, entries)
|
||||
if (qent->reqno == rpch.rpch_reqno)
|
||||
break;
|
||||
if (!qent) {
|
||||
mutex_exit(&recvmtx);
|
||||
kmem_free(rpc, rpch.rpch_flen);
|
||||
break;
|
||||
}
|
||||
TAILQ_REMOVE(&recvq, qent, entries);
|
||||
qent->rpch_resp = (void *)rpc;
|
||||
cv_signal(&qent->cv);
|
||||
mutex_exit(&recvmtx);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RUMPRPC_COPYOUT:
|
||||
{
|
||||
struct rumprpc_copydata *req = (void *)rpc;
|
||||
|
||||
memcpy(req->rpc_addr, req->rpc_data, req->rpc_len);
|
||||
kmem_free(req, req->rpc_head.rpch_flen);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
printf("invalid type %d\n", rpch.rpch_type);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a syscall to a remote site over a socket. I'm not really sure
|
||||
* if this should use kernel or user networking. Currently it uses
|
||||
* user networking, but could be changed.
|
||||
*/
|
||||
static int
|
||||
rump_sysproxy_socket(int num, void *arg, uint8_t *data, size_t dlen,
|
||||
register_t *retval)
|
||||
{
|
||||
struct sysproxy_qent *qent;
|
||||
struct rumprpc_sysreq *call;
|
||||
struct rumprpc_sysresp *resp;
|
||||
size_t totlen = sizeof(*call) + dlen;
|
||||
int s = (uintptr_t)arg;
|
||||
int error;
|
||||
|
||||
qent = get_qent();
|
||||
|
||||
/* build request */
|
||||
/* should we prefer multiple writes if dlen > magic_constant? */
|
||||
call = kmem_alloc(totlen, KM_SLEEP);
|
||||
call->rpc_head.rpch_flen = totlen;
|
||||
call->rpc_head.rpch_reqno = qent->reqno;
|
||||
call->rpc_head.rpch_type = RUMPRPC_SYSCALL;
|
||||
call->rpc_sysnum = num;
|
||||
memcpy(call->rpc_data, data, dlen);
|
||||
|
||||
dosend(s, qent, (uint8_t *)call, totlen, true);
|
||||
kmem_free(call, totlen);
|
||||
|
||||
/*
|
||||
* Wake up next sender or just toggle availability
|
||||
*/
|
||||
mutex_enter(&sendmtx);
|
||||
if (TAILQ_EMPTY(&sendq)) {
|
||||
sendavail = true;
|
||||
} else {
|
||||
cv_signal(&TAILQ_FIRST(&sendq)->cv);
|
||||
}
|
||||
mutex_exit(&sendmtx);
|
||||
|
||||
/* Wait for response */
|
||||
mutex_enter(&recvmtx);
|
||||
while (qent->rpch_resp == NULL)
|
||||
cv_wait(&qent->cv, &recvmtx);
|
||||
mutex_exit(&recvmtx);
|
||||
|
||||
resp = (struct rumprpc_sysresp *)qent->rpch_resp;
|
||||
/* we trust our kernel */
|
||||
KASSERT(resp->rpc_head.rpch_type == RUMPRPC_SYSCALL_RESP);
|
||||
|
||||
*retval = resp->rpc_retval;
|
||||
error = resp->rpc_error;
|
||||
|
||||
kmem_free(resp, resp->rpc_head.rpch_flen);
|
||||
put_qent(qent);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
rump_sysproxy_socket_setup_client(int s)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
|
||||
recvthread, (void *)(uintptr_t)s, NULL, "sysproxy_recv");
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_init(&wrkmtx, MUTEX_DEFAULT, IPL_NONE);
|
||||
mutex_init(&sendmtx, MUTEX_DEFAULT, IPL_NONE);
|
||||
mutex_init(&recvmtx, MUTEX_DEFAULT, IPL_NONE);
|
||||
error = rump_sysproxy_set(rump_sysproxy_socket, (void *)(uintptr_t)s);
|
||||
/* XXX: handle */
|
||||
|
||||
sock = s;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
rump_sysproxy_socket_setup_server(int s)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
|
||||
recvthread, (void *)(uintptr_t)s, NULL, "sysproxy_recv");
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_init(&wrkmtx, MUTEX_DEFAULT, IPL_NONE);
|
||||
mutex_init(&recvmtx, MUTEX_DEFAULT, IPL_NONE);
|
||||
mutex_init(&sendmtx, MUTEX_DEFAULT, IPL_NONE);
|
||||
|
||||
sock = s;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
# $NetBSD: Makefile,v 1.1 2009/04/29 17:51:47 pooka Exp $
|
||||
#
|
||||
|
||||
PROG= sysproxy_client
|
||||
NOMAN=
|
||||
|
||||
LDADD+= -lrumpvfs -lrump -lrumpuser -lpthread
|
||||
|
||||
.include <bsd.prog.mk>
|
|
@ -0,0 +1,65 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <rump/rump.h>
|
||||
#include <rump/rump_syscalls.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SERVPATH "/tmp/rump_sysproxy_test"
|
||||
#define USE_UN
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
struct stat sb[3];
|
||||
int s, error;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
#ifdef USE_UN
|
||||
struct sockaddr_un sun;
|
||||
|
||||
s = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
if (s == -1)
|
||||
err(1, "socket");
|
||||
|
||||
memset(&sun, 0, sizeof(sun));
|
||||
sun.sun_family = AF_LOCAL;
|
||||
strcpy(sun.sun_path, SERVPATH);
|
||||
if (connect(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) == -1)
|
||||
err(1, "connect");
|
||||
#else
|
||||
struct sockaddr_in sin;
|
||||
int x = 1;
|
||||
|
||||
s = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (s == -1)
|
||||
err(1, "socket");
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(12345);
|
||||
sin.sin_addr.s_addr = inet_addr("10.181.181.1");
|
||||
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
|
||||
err(1, "connect");
|
||||
|
||||
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &x, sizeof(x)) == -1)
|
||||
err(1, "setsockopt");
|
||||
#endif
|
||||
|
||||
rump_init();
|
||||
rump_sysproxy_socket_setup_client(s);
|
||||
|
||||
if (rump_sys_stat("/", &sb[1]) == -1)
|
||||
err(1, "stat");
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
# $NetBSD: Makefile,v 1.1 2009/04/29 17:51:47 pooka Exp $
|
||||
#
|
||||
|
||||
PROG= sysproxy_serv
|
||||
NOMAN=
|
||||
|
||||
LDADD+= -lrump -lrumpuser -lpthread
|
||||
LDADD+= -lrumpvfs
|
||||
|
||||
.include <bsd.prog.mk>
|
|
@ -0,0 +1,95 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <rump/rump.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SERVPATH "/tmp/rump_sysproxy_test"
|
||||
#define USE_UN
|
||||
|
||||
static void
|
||||
cleanup()
|
||||
{
|
||||
|
||||
unlink(SERVPATH);
|
||||
}
|
||||
|
||||
static void
|
||||
sigint(int sig)
|
||||
{
|
||||
|
||||
cleanup();
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
sigabrt(int sig)
|
||||
{
|
||||
|
||||
cleanup();
|
||||
abort();
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
socklen_t slen;
|
||||
int s, s2;
|
||||
|
||||
#ifdef USE_UN
|
||||
struct sockaddr_un sun;
|
||||
|
||||
s = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
if (s == -1)
|
||||
err(1, "socket");
|
||||
|
||||
memset(&sun, 0, sizeof(sun));
|
||||
sun.sun_family = AF_LOCAL;
|
||||
strcpy(sun.sun_path, SERVPATH);
|
||||
if (bind(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) == -1)
|
||||
err(1, "bind");
|
||||
atexit(cleanup);
|
||||
signal(SIGINT, sigint);
|
||||
signal(SIGSEGV, sigabrt);
|
||||
if (listen(s, 1) == -1)
|
||||
err(1, "listen");
|
||||
slen = sizeof(sun);
|
||||
s2 = accept(s, (struct sockaddr *)&sun, &slen);
|
||||
if (s2 == -1)
|
||||
err(1, "accept");
|
||||
#else
|
||||
struct sockaddr_in sin;
|
||||
|
||||
s = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (s == -1)
|
||||
err(1, "socket");
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(12345);
|
||||
sin.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
|
||||
err(1, "bind");
|
||||
if (listen(s, 1) == -1)
|
||||
err(1, "listen");
|
||||
slen = sizeof(sin);
|
||||
s2 = accept(s, (struct sockaddr *)&sin, &slen);
|
||||
if (s2 == -1)
|
||||
err(1, "accept");
|
||||
#endif
|
||||
|
||||
rump_init();
|
||||
rump_sysproxy_socket_setup_server(s2);
|
||||
|
||||
pause();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue