aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv64/include/tcb.h4
-rw-r--r--arch/riscv64/kernel/vmem.c22
-rw-r--r--common/dmem.c11
-rw-r--r--common/tcb.c97
-rw-r--r--common/uapi/ipc.c12
-rw-r--r--common/vmem.c43
-rw-r--r--include/arch/tcb.h32
-rw-r--r--include/kmi/tcb.h104
8 files changed, 118 insertions, 207 deletions
diff --git a/arch/riscv64/include/tcb.h b/arch/riscv64/include/tcb.h
index a804175..f0cce37 100644
--- a/arch/riscv64/include/tcb.h
+++ b/arch/riscv64/include/tcb.h
@@ -16,7 +16,11 @@ struct arch_tcbd {
/** Extra scratch register. */
long scratch;
+ /** RPC stack page table leaf node. */
struct vmem *rpc_leaf;
+
+ /** Index into \p rpc_leaf with the lowest accessed page so far in a
+ * certain context. */
int rpc_idx;
};
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c
index 7d1d3cc..37b9040 100644
--- a/arch/riscv64/kernel/vmem.c
+++ b/arch/riscv64/kernel/vmem.c
@@ -177,11 +177,7 @@ stat_t set_vpage_flags(struct vmem *branch, vm_t vaddr, vmflags_t flags)
pm_t *pte = __find_vmem(branch, vaddr, &order);
if (pte) {
set_bits(*pte, vp_flags(flags));
-
- if (order == __mm_max_order)
- return INFO_SEFF;
- else
- return OK;
+ return OK;
}
return ERR_NF;
@@ -193,11 +189,7 @@ stat_t clear_vpage_flags(struct vmem *branch, vm_t vaddr, vmflags_t flags)
pm_t *pte = __find_vmem(branch, vaddr, &order);
if (pte) {
clear_bits(*pte, vp_flags(flags));
-
- if (order == __mm_max_order)
- return INFO_SEFF;
- else
- return OK;
+ return OK;
}
return ERR_NF;
@@ -211,13 +203,7 @@ stat_t mod_vpage(struct vmem *branch, vm_t vaddr, pm_t paddr, vmflags_t flags)
pm_t *pte = __find_vmem(branch, vaddr, &order);
if (pte) {
*pte = to_pte((pm_t)__pa(paddr), vp_flags(flags));
- /* if we're modifying a top level mapping, we will have to
- * update the same one for all the other threads in this process
- * */
- if (order == __mm_max_order)
- return INFO_SEFF;
- else
- return OK;
+ return OK;
}
return ERR_NF;
@@ -323,7 +309,7 @@ stat_t map_vpage(struct vmem *branch, pm_t paddr, vm_t vaddr, vmflags_t flags,
(struct vmem *)to_pte((pm_t)__pa(paddr), vp_flags(flags));
__add_graves(root, vm_to_index(vaddr, __mm_max_order));
- return top == __mm_max_order ? INFO_SEFF : OK;
+ return OK;
}
/**
diff --git a/common/dmem.c b/common/dmem.c
index b903cbe..bbabe09 100644
--- a/common/dmem.c
+++ b/common/dmem.c
@@ -106,14 +106,15 @@ vm_t alloc_devmem(struct tcb *t, pm_t dev_start, size_t bytes, vmflags_t flags)
region = alloc_region(&post_ram, bytes, 0, flags);
if (!region)
- return 0;
+ return NULL;
stat_t status = OK;
const vm_t w = map_fill_region(t->proc.vmem, &dev_alloc_wrapper,
dev_start, region,
bytes, flags, &status);
- if (is_rpc(t) && status == INFO_SEFF)
- clone_rpc_maps(t);
+
+ if (status)
+ return NULL;
return w;
}
@@ -142,8 +143,6 @@ stat_t free_devmem(struct tcb *t, vm_t dev_start)
stat_t status = OK;
map_fill_region(t->proc.vmem, &dev_free_wrapper, dev_paddr, dev_start,
region_size, 0, &status);
- if (is_rpc(t) && status == INFO_SEFF)
- clone_rpc_maps(t);
if (dev_paddr < __pre_top)
free_region(&pre_ram, dev_paddr);
@@ -151,5 +150,5 @@ stat_t free_devmem(struct tcb *t, vm_t dev_start)
if (dev_paddr > __post_base)
free_region(&post_ram, dev_paddr);
- return OK;
+ return status;
}
diff --git a/common/tcb.c b/common/tcb.c
index e69f2f0..4b57c06 100644
--- a/common/tcb.c
+++ b/common/tcb.c
@@ -125,6 +125,7 @@ struct tcb *create_thread(struct tcb *p)
id_t tid = __alloc_tid(t);
tcbs[tid] = t;
t->tid = tid;
+ t->dead = false;
if (likely(p)) {
t->pid = p->pid;
@@ -144,6 +145,7 @@ struct tcb *create_thread(struct tcb *p)
t->rid = p->rid;
t->rpc.vmem = create_vmem();
setup_rpc_stack(t);
+ reference_proc(p);
t->regs = (vm_t)t;
@@ -215,10 +217,12 @@ stat_t destroy_thread(struct tcb *t)
hard_assert(!is_proc(t), ERR_INVAL);
/* remove thread id from list */
+ /** @todo what about if thread is in rpc? should it rather just be
+ * marked dead? */
tcbs[t->tid] = 0;
- /* remove thread from process list */
- detach_proc(get_rproc(t), t);
+ /* remove reference to root process */
+ unreference_proc(get_rproc(t));
return __destroy_thread_data(t);
}
@@ -228,67 +232,28 @@ stat_t destroy_proc(struct tcb *p)
hard_assert(tcbs, ERR_NOINIT);
hard_assert(is_proc(p), ERR_INVAL);
- for (struct tcb *iter = p; (iter = iter->proc.next);)
- destroy_thread(iter);
+ p->dead = true;
+ /* unreference ourselves */
+ unreference_proc(p);
catastrophic_assert(destroy_uvmem(p));
return __destroy_thread_data(p);
}
-stat_t attach_rpc(struct tcb *r, struct tcb *t)
+void reference_proc(struct tcb *p)
{
- hard_assert(r != t, ERR_INVAL);
- struct tcb *next = r->server.next;
- t->rpc.next = next;
-
- if (next) { next->rpc.prev = t; }
-
- t->rpc.prev = r;
- r->server.next = t;
- return OK;
-}
-
-stat_t attach_proc(struct tcb *r, struct tcb *t)
-{
- hard_assert(r != t, ERR_INVAL);
- struct tcb *next = r->proc.next;
- t->proc.next = next;
-
- if (next) { next->proc.prev = t; }
-
- t->proc.prev = r;
- r->proc.next = t;
- return OK;
-}
-
-stat_t detach_rpc(struct tcb *r, struct tcb *t)
-{
- /* rpc handling is slightly more complex since we have separate members
- * for server and rpc contexts, where server is the server that
- * currently hosts some number of rpc guests. */
- hard_assert(r != t, ERR_INVAL);
- struct tcb *prev = t->rpc.prev;
- struct tcb *next = t->rpc.next;
-
- if (prev == r) { r->server.next = next; }
- else if (prev) { prev->rpc.next = next; }
-
- if (next) { next->rpc.prev = prev; }
-
- return OK;
+ hard_assert(is_proc(p), RETURN_VOID);
+ p->refcount++;
}
-stat_t detach_proc(struct tcb *r, struct tcb *t)
+void unreference_proc(struct tcb *p)
{
- MAYBE_UNUSED(r);
- hard_assert(r != t, ERR_INVAL);
- struct tcb *prev = t->proc.prev;
- struct tcb *next = t->proc.next;
-
- if (prev) { prev->proc.next = next; }
- if (next) { next->proc.prev = prev; }
-
- return OK;
+ hard_assert(is_proc(p), RETURN_VOID);
+ p->refcount--;
+ if (p->dead && p->refcount == 0) {
+ dbg("thread %d is completely destroyed\n", p->tid);
+ /** @todo actually destroy */
+ }
}
/* weak to allow optimisation on risc-v, but provide fallback for future */
@@ -333,30 +298,6 @@ struct tcb *get_tcb(id_t tid)
return tcbs[tid];
}
-stat_t clone_rpc_maps(struct tcb *r)
-{
- hard_assert(r && is_proc(r), ERR_INVAL);
- struct tcb *t = r->server.next;
- if (!t)
- return OK;
-
- do {
- clone_uvmem(r->proc.vmem, t->rpc.vmem);
- } while ((t = t->rpc.next));
-
- return OK;
-}
-
-stat_t clone_proc_maps(struct tcb *r)
-{
- hard_assert(r && is_proc(r), ERR_INVAL);
- struct tcb *t = r;
- while ((t = t->proc.next))
- clone_uvmem(r->proc.vmem, t->proc.vmem);
-
- return OK;
-}
-
void set_return(struct tcb *t, vm_t v)
{
t->exec = v;
diff --git a/common/uapi/ipc.c b/common/uapi/ipc.c
index 13682c7..b9a435b 100644
--- a/common/uapi/ipc.c
+++ b/common/uapi/ipc.c
@@ -54,13 +54,13 @@ enum ipc_kind {
*
* @param t Thread to migrate.
* @param r Process to migrate to.
- * @param sd RPC stack regions to mark inaccessible.
+ * @param s RPC stack regions to mark inaccessible.
*/
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);
- attach_rpc(r, t);
+ reference_proc(r);
t->pid = r->rid;
/* make sure updates are visible when swapping to the new virtual memory */
@@ -135,6 +135,7 @@ static void leave_rpc(struct tcb *t, struct sys_ret a)
while (ctx->kick) {
rpc_stack = ctx->rpc_stack + BASE_PAGE_SIZE;
ctx = (struct call_ctx *)(rpc_stack) - 1;
+ unreference_proc(get_tcb(ctx->pid));
}
t->regs = ctx->regs;
@@ -224,6 +225,10 @@ static void do_ipc(struct tcb *t,
}
r = get_rproc(r);
+ if (unlikely(r->dead)) {
+ leave_rpc(t, SYS_RET1(ERR_INVAL));
+ return;
+ }
if (unlikely(!r->callback)) {
leave_rpc(t, SYS_RET1(ERR_NOINIT));
@@ -310,10 +315,7 @@ SYSCALL_DEFINE4(ipc_resp)(struct tcb *t, sys_arg_t d0, sys_arg_t d1,
if (unlikely(!is_rpc(t)))
return_args1(t, ERR_MISC);
- /* we need the current proc before leaving the rpc */
- struct tcb *r = get_cproc(t);
leave_rpc(t, SYS_RET6(OK, t->tid, d0, d1, d2, d3));
- detach_rpc(r, t);
}
/**
diff --git a/common/vmem.c b/common/vmem.c
index e90d27b..f52f43e 100644
--- a/common/vmem.c
+++ b/common/vmem.c
@@ -124,8 +124,7 @@ static stat_t __free_mapped_shared_region(struct tcb *t, struct mem_region *m)
*
* @param t Thread to work in.
* @param m Memory region to free.
- * @return \ref INFO_SEFF if other thread in process should be synced, \ref OK
- * otherwise.
+ * @return \ref OK
*/
static stat_t __free_mapped_region(struct tcb *t, struct mem_region *m)
{
@@ -190,13 +189,6 @@ vm_t alloc_uvmem(struct tcb *t, size_t size, vmflags_t flags)
stat_t status = OK;
const vm_t v = alloc_region(&t->sp_r, size, &size, flags);
const vm_t w = map_allocd_region(t->proc.vmem, v, size, flags, &status);
- /** \todo this could be changed so that each thread allocated the memory
- * region for itself to start with, and only when someone tries to
- * access it from some other thread, is it actually cloned. Would likely
- * need some major reworkings, so this is good enough for now. */
- if (is_rpc(t) && status == INFO_SEFF)
- clone_rpc_maps(t);
-
return w;
}
@@ -218,8 +210,8 @@ vm_t alloc_uvpage(struct tcb *t, size_t size, vmflags_t flags, size_t *asize,
return NULL;
status = map_vpage(t->proc.vmem, addr, w, flags, order);
- if (is_rpc(t) && status == INFO_SEFF)
- clone_rpc_maps(t);
+ if (status)
+ return NULL;
if (asize)
*asize = actual_size;
@@ -237,12 +229,6 @@ vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t flags)
stat_t status = OK;
const vm_t v = alloc_fixed_region(&t->sp_r, start, size, &size, flags);
const vm_t w = map_allocd_region(t->proc.vmem, v, size, flags, &status);
-
- /** @todo should probably update rpc maps even if the thread that does
- * the allocation isn't in an ipc? */
- if (is_rpc(t) && status == INFO_SEFF)
- clone_rpc_maps(t);
-
return w;
}
@@ -281,15 +267,15 @@ stat_t alloc_shared_uvmem(struct tcb *s, struct tcb *c,
BASE_PAGE);
}
- if (cstatus == INFO_SEFF)
- clone_rpc_maps(c);
-
- if (sstatus == INFO_SEFF)
- clone_rpc_maps(s);
-
*sstart = sv;
*cstart = cv;
+ if (sstatus)
+ return sstatus;
+
+ if (cstatus)
+ return cstatus;
+
return OK;
}
@@ -301,8 +287,8 @@ stat_t free_uvmem(struct tcb *r, vm_t va)
return ERR_NF;
stat_t status = __free_mapped_region(r, m);
- if (is_rpc(r) && status == INFO_SEFF)
- return clone_rpc_maps(r);
+ if (status)
+ return ERR_MISC;
return free_known_region(&r->sp_r, m);
}
@@ -319,7 +305,7 @@ stat_t alloc_uvmem_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
if (status)
*status = ret;
- return (ret == INFO_SEFF) ? OK : ret;
+ return ret;
}
stat_t alloc_shared_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
@@ -334,7 +320,8 @@ stat_t alloc_shared_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
ret = map_vpage(b, *offset, vaddr, flags, order);
if (status)
*status = ret;
- return (ret == INFO_SEFF) ? OK : ret;
+
+ return ret;
}
stat_t copy_allocd_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
@@ -387,5 +374,5 @@ stat_t free_uvmem_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
free_page(order, paddr);
- return (ret == INFO_SEFF) ? OK : ret;
+ return ret;
}
diff --git a/include/arch/tcb.h b/include/arch/tcb.h
index af12e0b..4e82ab1 100644
--- a/include/arch/tcb.h
+++ b/include/arch/tcb.h
@@ -25,11 +25,43 @@
*/
void tcb_assign(struct tcb *t);
+/**
+ * Set up RPC stack in a way that is convenient for the underlying architecture.
+ *
+ * @param t Thread whose RPC stack should be set up.
+ */
void setup_rpc_stack(struct tcb *t);
+
+/**
+ * Maximum size of one individual RPC stack instance.
+ *
+ * @return Max size of one individual RPC stack instance.
+ */
size_t max_rpc_size();
+/**
+ * Current highest address in RPC stack. Allowed to be inaccurate to one base page.
+ *
+ * @param t Thread whose position in the RPC stack is to be determined.
+ * @return Virtual address corresponding to the current RPC stack position.
+ */
vm_t rpc_position(struct tcb *t);
+
+/**
+ * Mark RPC stack up to \p top accessible from userspace.
+ *
+ * @param t Thread whose RPC stack is being modified.
+ * @param top Address up to where stack should be accessible from userspace.
+ */
void mark_rpc_valid(struct tcb *t, vm_t top);
+
+/**
+ * Mark RPC stack down to \p bottom inaccessible from userspace.
+ *
+ * @param t Thread whose RPC stack is being modified.
+ * @param bottom Address down to where stack should be inaccessible from
+ * userspace.
+ */
void mark_rpc_invalid(struct tcb *t, vm_t bottom);
#endif /* KMI_ARCH_TCB_H */
diff --git a/include/kmi/tcb.h b/include/kmi/tcb.h
index ba71dd3..6614d62 100644
--- a/include/kmi/tcb.h
+++ b/include/kmi/tcb.h
@@ -13,6 +13,7 @@
struct tcb;
#include <kmi/mem_regions.h>
+#include <kmi/atomic.h>
#include <kmi/caps.h>
#include <kmi/types.h>
#include <arch/tcb.h> /* arch-specific data */
@@ -67,12 +68,6 @@ struct tcb;
struct tcb_ctx {
/** Virtual address space of context. */
struct vmem *vmem;
-
- /** Next thread in context. */
- struct tcb *next;
-
- /** Previous thread in context. */
- struct tcb *prev;
};
/** Enum for notification states. */
@@ -121,6 +116,9 @@ struct tcb {
/** Possible thread local storage. */
vm_t thread_storage;
+ /** Reference count to process. */
+ atomic_int_fast32_t refcount;
+
/** Process context of thread. */
struct tcb_ctx proc;
@@ -184,6 +182,10 @@ struct tcb {
/** Whether thread has gotten an IPI */
bool ipi;
+
+ /** Whether thread is dead. If thread is process, then corresponds to
+ * whole process. */
+ bool dead;
};
/**
@@ -254,49 +256,6 @@ stat_t destroy_thread(struct tcb *t);
stat_t destroy_proc(struct tcb *p);
/**
- * Attach a thread to an RPC context.
- *
- * Essentially inserts thread \c t into the process \c r, with access to the
- * same memory except for the RPC stack.
- *
- * @param r Process to attach to.
- * @param t Thread to attach.
- * @return \ref OK on success, \ref ERR_INVAL if pointers are the same.
- */
-stat_t attach_rpc(struct tcb *r, struct tcb *t);
-
-/**
- * Detach a thread from an RPC context.
- *
- * \see attach_rpc().
- *
- * @param r Process to detach from.
- * @param t Thread to detach.
- * @return \ref OK on success, \ref ERR_INVAL if pointers are the same.
- *
- * \todo Should probably check that thread exists in the process?
- */
-stat_t detach_rpc(struct tcb *r, struct tcb *t);
-
-/**
- * Attach a thread in a process context.
- *
- * @param r Process to attach to.
- * @param t Thread to attach.
- * @return \ref OK on success, \ref ERR_INVAL if pointers are the same.
- */
-stat_t attach_proc(struct tcb *r, struct tcb *t);
-
-/**
- * Detach a thread from a process context.
- *
- * @param r Process to detach from.
- * @param t Thread to detach.
- * @return \ref OK on success, \ref ERR_INVAL if pointers are the same.
- */
-stat_t detach_proc(struct tcb *r, struct tcb *t);
-
-/**
* Get currently executing thread.
*
* @return Current \ref tcb.
@@ -343,29 +302,6 @@ void use_tcb(struct tcb *t);
struct tcb *get_tcb(id_t tid);
/**
- * Clone process context memory mappings.
- *
- * Essentially make sure all threads in the process have identical memory
- * mappings.
- *
- * @param p Process whose memory mappings to clone.
- * @return \ref OK on success, something else otherwise.
- * \todo Check up on return codes.
- */
-stat_t clone_proc_maps(struct tcb *p);
-
-/**
- * Clone RPC context memory mappings.
- *
- * \see clone_proc_maps().
- *
- * @param r Server whose memory mappings to clone to threads in RPC to it.
- * @return \ref OK on success, something else otherwise.
- * \todo Check up on return codes.
- */
-stat_t clone_rpc_maps(struct tcb *r);
-
-/**
* Allocate stacks for thread.
*
* Both user stack and RPC stack.
@@ -391,4 +327,28 @@ void set_return(struct tcb *t, vm_t r);
*/
bool running(struct tcb *t);
+/**
+ * Add a reference to a process.
+ * Instead of lists of threads that belong to a process, we give the process'
+ * owning thread a reference counter. When a process is killed, a 'dead' bit is
+ * set, and the thread that owns the process is unreferenced. All data
+ * associated with the process can immediately be freed, but the tid is still
+ * reserved until the reference count reaches zero.
+ *
+ * We have to make sure that all ways a process might be entered check that the
+ * process is still alive, and unmapping pages causes other threads to update
+ * their page tables as well. Then if a segfault happens, we can check if it was
+ * due to being in a dead process. This is still largely TODO.
+ *
+ * @param p Process to reference.
+ */
+void reference_proc(struct tcb *p);
+
+/**
+ * Unreference a process.
+ *
+ * @param p Process to unreference.
+ */
+void unreference_proc(struct tcb *p);
+
#endif /* KMI_TCB_H */