aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2023-10-09 14:57:18 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2023-10-09 16:15:06 +0300
commit9c9d589ea310f1290e4d6d2019fc8b51d9a86e42 (patch)
tree81694631f4e848728774c22256baf6cded4f910d /common
parentb2f0f82e18665cb754b93d9795a6f1fb9dfc7c93 (diff)
downloadkmi-9c9d589ea310f1290e4d6d2019fc8b51d9a86e42.tar.gz
kmi-9c9d589ea310f1290e4d6d2019fc8b51d9a86e42.zip
move rpc stack handling to arch-specific code
+ Fairly considerable speedup, as we don't have to look up the rpc pte every time separately, instead cacheing them. Adds an architecture specific limitation to total rpc stack size, though.
Diffstat (limited to 'common')
-rw-r--r--common/tcb.c38
-rw-r--r--common/uapi/conf.c16
-rw-r--r--common/uapi/ipc.c69
3 files changed, 12 insertions, 111 deletions
diff --git a/common/tcb.c b/common/tcb.c
index d6194e8..e69f2f0 100644
--- a/common/tcb.c
+++ b/common/tcb.c
@@ -84,42 +84,6 @@ static id_t __alloc_tid(struct tcb *t)
}
/**
- * Setup RPC stack.
- *
- * RPC stack is local to each thread, and should not be visible to other threads
- * in the same process. Currently maps the RPC stack in BASE_PAGE increments, to
- * hopefully allow us to later quickly disallow access to programs lower down in
- * the RPC call chain by turning off all stack pages lower than the current
- * stack pointer. We shall see if this actually works or not.
- *
- * @param t Thread to setup RPC stack for.
- * @param bytes Minimum size of RPC stack.
- * @return Base of allocated RPC stack.
- *
- * \todo add error checking */
-static vm_t __setup_rpc_stack(struct tcb *t, size_t bytes)
-{
- pm_t offset = 0;
- size_t pages = __pages(bytes);
- vmflags_t flags = VM_V | VM_R | VM_W | VM_U;
- for (size_t i = 1; i <= pages; ++i) {
- offset = alloc_page(BASE_PAGE);
- map_vpage(t->rpc.vmem, offset,
- RPC_STACK_TOP - BASE_PAGE_SIZE * i,
- flags, BASE_PAGE);
-
- /* map stack into both process and rpc vmem since we want to
- * optimistically write data into it during initialization of an
- * rpc. */
- map_vpage(t->proc.vmem, offset,
- RPC_STACK_TOP - BASE_PAGE_SIZE * i,
- flags, BASE_PAGE);
- }
- t->rpc_stack = RPC_STACK_TOP;
- return RPC_STACK_TOP - BASE_PAGE_SIZE * pages;
-}
-
-/**
* Setup thread stack.
*
* @param t Thread to setup stack for.
@@ -179,7 +143,7 @@ struct tcb *create_thread(struct tcb *p)
t->eid = t->pid;
t->rid = p->rid;
t->rpc.vmem = create_vmem();
- __setup_rpc_stack(t, __call_stack_size);
+ setup_rpc_stack(t);
t->regs = (vm_t)t;
diff --git a/common/uapi/conf.c b/common/uapi/conf.c
index 13be3ee..894b98a 100644
--- a/common/uapi/conf.c
+++ b/common/uapi/conf.c
@@ -18,14 +18,12 @@
/** \todo stack size should really be set on a per-thread basis, and are the
* conf*-syscalls even necessary? */
size_t __thread_stack_size = SZ_2M;
-size_t __call_stack_size = SZ_2M;
size_t __rpc_stack_size = SZ_512K;
/** IDs for configuration parameters. */
/** @todo should probably be moved somewhere so it can be shared with userspace */
enum conf_param {
CONF_THREAD_STACK = 0,
- CONF_CALL_STACK,
CONF_RPC_STACK,
};
@@ -49,10 +47,6 @@ SYSCALL_DEFINE1(conf_get)(struct tcb *t, sys_arg_t param)
val = __thread_stack_size;
break;
- case CONF_CALL_STACK:
- val = __call_stack_size;
- break;
-
case CONF_RPC_STACK:
val = __rpc_stack_size;
break;
@@ -85,17 +79,9 @@ SYSCALL_DEFINE2(conf_set)(struct tcb *t, sys_arg_t param, sys_arg_t val)
__thread_stack_size = align_up(val, BASE_PAGE_SIZE);
break;
- case CONF_CALL_STACK:
- size = align_up(val, BASE_PAGE_SIZE);
- if (size < __rpc_stack_size)
- return_args(t, SYS_RET1(ERR_MISC));
-
- __call_stack_size = size;
- break;
-
case CONF_RPC_STACK:
size = align_up(val, BASE_PAGE_SIZE);
- if (size > __call_stack_size)
+ if (size > max_rpc_size())
return_args1(t, ERR_MISC);
__rpc_stack_size = size;
diff --git a/common/uapi/ipc.c b/common/uapi/ipc.c
index f9b2817..13682c7 100644
--- a/common/uapi/ipc.c
+++ b/common/uapi/ipc.c
@@ -11,43 +11,6 @@
#include <kmi/ipi.h>
#include <kmi/conf.h>
-/**
- * Mark rpc stack between \p start and \p end inaccessible.
- *
- * @param t Thread whose rpc stack to modify.
- * @param start Start address of rpc stack to mark inaccessible.
- * @param end End address of rpc stack to mark inaccessible.
- */
-static void mark_rpc_inaccessible(struct tcb *t, vm_t start, vm_t end)
-{
- size_t page_size = BASE_PAGE_SIZE;
- size_t size = end - start;
- size_t pages = size / page_size;
- while (pages--)
- clear_vpage_flags(t->rpc.vmem, start + pages * page_size, VM_U);
-}
-
-/**
- * Mark rpc stack between \p start and \p end accessible.
- *
- * @param t Thread whose rpc stack to modify.
- * @param start Start address of rpc stack to mark accessible.
- * @param end End address of rpc stack to mark accessible.
- */
-static void mark_rpc_accessible(struct tcb *t, vm_t start, vm_t end)
-{
- /** @todo this could still be optimized with arch-specific stuff I would
- * imagine, as set_vpage_flags() has to traverse the whole tree for each
- * page to mark. Instead it should be possible to mark the pages
- * continuously once they've been traversed once. Or maybe even keep
- * around a pointer to where the last stack left off? */
- size_t page_size = BASE_PAGE_SIZE;
- size_t size = end - start;
- size_t pages = size / page_size;
- while (pages--)
- set_vpage_flags(t->rpc.vmem, start + pages * page_size, VM_U);
-}
-
/** Structure for maintaining the required context data for an rpc call. */
struct call_ctx {
/** Execution continuation point. */
@@ -93,7 +56,7 @@ enum ipc_kind {
* @param r Process to migrate to.
* @param sd RPC stack regions to mark inaccessible.
*/
-static void finalize_rpc(struct tcb *t, struct tcb *r, struct stack_diff sd)
+static void finalize_rpc(struct tcb *t, struct tcb *r, vm_t s)
{
clone_uvmem(r->proc.vmem, t->rpc.vmem);
set_return(t, r->callback);
@@ -101,7 +64,7 @@ static void finalize_rpc(struct tcb *t, struct tcb *r, struct stack_diff sd)
t->pid = r->rid;
/* make sure updates are visible when swapping to the new virtual memory */
- mark_rpc_inaccessible(t, sd.start, sd.end);
+ mark_rpc_invalid(t, s);
use_vmem(t->rpc.vmem);
}
@@ -115,18 +78,10 @@ static void finalize_rpc(struct tcb *t, struct tcb *r, struct stack_diff sd)
* @param kind Kind of IPC we're doing. Essentially toggles kick boolean.
* @return RPC stack difference that should be passed to finalize_rpc().
*/
-static struct stack_diff enter_rpc(struct tcb *t, struct sys_ret a,
+static vm_t enter_rpc(struct tcb *t, struct sys_ret a,
enum ipc_kind kind)
{
- vm_t rpc_stack = t->rpc_stack;
- if (is_rpc(t))
- /** @todo what if user uses their own stack? Or is a dick and
- * sets the stack pointer to RPC_STACK_TOP or something? It'll
- * likely only cause a fuckup in the process who did the dumb
- * thing, so maybe just consider it user error? Except by
- * causing the stack of the next rpc to run out of memory... */
- rpc_stack = align_down(get_stack(t), BASE_PAGE_SIZE);
-
+ vm_t rpc_stack = rpc_position(t);
struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1;
ctx->regs = t->regs;
@@ -148,7 +103,6 @@ static struct stack_diff enter_rpc(struct tcb *t, struct sys_ret a,
* return a status? except it shouldn't happen after we've run
* enough_rpc_stack(). */
vm_t new_stack = rpc_stack - BASE_PAGE_SIZE;
- struct stack_diff sd = {new_stack, t->rpc_stack};
/** @todo what if each stack is only some number of pages, and if a proc
* goes over the limit is is seen as programming error? Possibly user
@@ -162,7 +116,7 @@ static struct stack_diff enter_rpc(struct tcb *t, struct sys_ret a,
* */
t->rpc_stack = new_stack;
set_stack(t, new_stack);
- return sd;
+ return new_stack;
}
/**
@@ -190,7 +144,7 @@ static void leave_rpc(struct tcb *t, struct sys_ret a)
set_return(t, ctx->exec);
/* if we're returning from a failed rpc, this should essentially be a
* no-op */
- mark_rpc_accessible(t, t->rpc_stack, top);
+ mark_rpc_valid(t, top);
t->rpc_stack = ctx->rpc_stack;
t->pid = ctx->pid;
t->eid = ctx->eid;
@@ -211,13 +165,11 @@ static void leave_rpc(struct tcb *t, struct sys_ret a)
static bool enough_rpc_stack(struct tcb *t)
{
/* get top of call stack */
- vm_t top = RPC_STACK_BASE + __call_stack_size;
- /* get start of the next rpc stack instance */
- vm_t rpc_stack = t->rpc_stack + BASE_PAGE_SIZE;
+ vm_t top = rpc_position(t);
/* if we can still fit an rpc stack into the call stack, we can safely
* do the migration. */
- return top - rpc_stack >= __rpc_stack_size;
+ return (top - BASE_PAGE_SIZE) >= (t->rpc_stack - __rpc_stack_size);
}
/**
@@ -263,8 +215,7 @@ static void do_ipc(struct tcb *t,
if (unlikely(!enough_rpc_stack(t)))
return_args1(t, ERR_OOMEM);
- struct stack_diff sd =
- enter_rpc(t, SYS_RET6(OK, t->eid, d0, d1, d2, d3), kind);
+ vm_t s = enter_rpc(t, SYS_RET6(OK, t->eid, d0, d1, d2, d3), kind);
struct tcb *r = get_tcb(pid);
if (unlikely(!r)) {
@@ -282,7 +233,7 @@ static void do_ipc(struct tcb *t,
if (kind != IPC_REQ)
t->eid = t->pid;
- finalize_rpc(t, r, sd);
+ finalize_rpc(t, r, s);
/* I tested out passing the return values as arguments to
* ret_userspace_fast, but apparently that causes enough stack shuffling
* to be slower overall. */