diff options
| -rw-r--r-- | arch/riscv64/kernel/vmem.c | 21 | ||||
| -rw-r--r-- | include/arch/tcb.h | 8 | ||||
| -rw-r--r-- | include/kmi/orphanage.h | 9 | ||||
| -rw-r--r-- | include/kmi/syscalls.h | 3 | ||||
| -rw-r--r-- | include/kmi/tcb.h | 20 | ||||
| -rw-r--r-- | include/kmi/uapi.h | 15 | ||||
| -rw-r--r-- | src/orphanage.c | 13 | ||||
| -rw-r--r-- | src/tcb.c | 12 | ||||
| -rw-r--r-- | src/uapi/dispatch.c | 1 | ||||
| -rw-r--r-- | src/uapi/ipc.c | 19 | ||||
| -rw-r--r-- | src/uapi/proc.c | 29 |
11 files changed, 110 insertions, 40 deletions
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c index f87c76d..d4ae937 100644 --- a/arch/riscv64/kernel/vmem.c +++ b/arch/riscv64/kernel/vmem.c @@ -396,11 +396,16 @@ static void __populate_dmap(struct vmem *branch) (struct vmem *)to_pte(TOP_PAGE_SIZE * i, flags); } +/** How many base pages we use for the rpc stack. Used fairly often so calculate + * it at the start and then reference it. */ +static size_t rpc_pages; + struct vmem *init_vmem(void *fdt) { UNUSED(fdt); struct vmem *b = create_vmem(); __populate_dmap(b); + rpc_pages = order_size(MM_O1) / BASE_PAGE_SIZE; /* update which memory branch to use */ use_vmem(b); return b; @@ -489,8 +494,7 @@ void setup_rpc_stack(struct tcb *t) * access so as to ease stack usage tracking */ vmflags_t flags = VM_V | VM_R | VM_W | VM_U; - size_t pages = order_size(MM_O1) / BASE_PAGE_SIZE; - for (size_t i = 0; i < pages; ++i) { + for (size_t i = 0; i < rpc_pages; ++i) { pm_t page = alloc_page(BASE_PAGE); map_vpage(t->rpc.vmem, page, RPC_STACK_BASE + BASE_PAGE_SIZE * i, @@ -503,19 +507,19 @@ void setup_rpc_stack(struct tcb *t) /* we allocated a second order page for rpc stack usage */ t->rpc_stack = RPC_STACK_BASE + order_size(MM_O1); + /* slightly hacky maybe but we know the first pte is at RPC_STACK_BASE, * which means that it must also be the leaf */ t->arch.rpc_leaf = (struct vmem *)__find_vmem(t->rpc.vmem, RPC_STACK_BASE, BASE_PAGE); - /* 'reserve' top page of stack for kernel use */ - t->arch.rpc_idx = 511; + /* we count downward in base pages */ + t->arch.rpc_idx = rpc_pages; } void destroy_rpc_stack(struct tcb *t) { - size_t pages = order_size(MM_O1) / BASE_PAGE_SIZE; - for (size_t i = 0; i < pages; ++i) { + for (size_t i = 0; i < rpc_pages; ++i) { pm_t page; enum mm_order order; stat_vpage(t->rpc.vmem, RPC_STACK_BASE + BASE_PAGE_SIZE * i, &page, &order, NULL); @@ -523,6 +527,11 @@ void destroy_rpc_stack(struct tcb *t) } } +bool rpc_stack_empty(pm_t addr) +{ + return addr == RPC_STACK_BASE + (BASE_PAGE_SIZE * rpc_pages); +} + vm_t rpc_position(struct tcb *t) { /** @todo we assume rpc_idx is updated on every segfault of the rpc stack */ diff --git a/include/arch/tcb.h b/include/arch/tcb.h index 115f49d..22d3b59 100644 --- a/include/arch/tcb.h +++ b/include/arch/tcb.h @@ -40,13 +40,17 @@ void setup_rpc_stack(struct tcb *t); void destroy_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(); /** + * @param addr Address of current top of stack, generally from `ctx->rpc_stack`. + * @return \ref true if next rpc return would be into the root process. + */ +bool rpc_stack_empty(pm_t addr); + +/** * 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. diff --git a/include/kmi/orphanage.h b/include/kmi/orphanage.h index 530d10c..09e36b3 100644 --- a/include/kmi/orphanage.h +++ b/include/kmi/orphanage.h @@ -23,6 +23,13 @@ bool orphan(struct tcb *t); /** + * Mark \p t orphaned. + * + * @param t Thread to orphanize. + */ +void orphanize(struct tcb *t); + +/** * Assign \p t to the init process and jump to it. * \p t must be an orphan! * \p t must be in the process of swapping to its root process, either by @@ -30,6 +37,6 @@ bool orphan(struct tcb *t); * * @param t Orphaned thread. */ -__noreturn void orphanize(struct tcb *t); +__noreturn void unorphanize(struct tcb *t); #endif /* KMI_OPRHANAGE_H */ diff --git a/include/kmi/syscalls.h b/include/kmi/syscalls.h index 3619469..93d0ac9 100644 --- a/include/kmi/syscalls.h +++ b/include/kmi/syscalls.h @@ -157,6 +157,9 @@ enum sys_code { /** Request a thread exits. */ SYS_EXIT, + /** Detach a thread from its root process, becoming an orphant. */ + SYS_DETACH, + /** @} */ SYS_NUM, diff --git a/include/kmi/tcb.h b/include/kmi/tcb.h index fac9df7..df161b3 100644 --- a/include/kmi/tcb.h +++ b/include/kmi/tcb.h @@ -72,16 +72,13 @@ struct tcb_ctx { struct vmem *vmem; }; -/** Enum for notification states. */ -enum tcb_notify { - /** Thread is free to be notified. */ - NOTIFY_WAITING = 0, +/** State of thread. */ +enum tcb_state { + /** Thread is a zombie. */ + TCB_ZOMBIE = (1 << 0), - /** Thread has notifcations queued. */ - NOTIFY_QUEUED, - - /** Thread is running notification handler. */ - NOTIFY_RUNNING, + /** Thread is an orphan. */ + TCB_ORPHAN = (1 << 1), }; /** Thread control block. Main way to handle threads. */ @@ -180,9 +177,8 @@ struct tcb { /** Queue that connects together threads waiting for an ipi */ struct queue_head ipi_queue; - /** Whether thread is dead. If thread is process, then corresponds to - * whole process. */ - bool dead; + /** Current state of thread. */ + enum tcb_state state; }; /** diff --git a/include/kmi/uapi.h b/include/kmi/uapi.h index 684f0da..76a759f 100644 --- a/include/kmi/uapi.h +++ b/include/kmi/uapi.h @@ -841,6 +841,21 @@ SYSCALL_DECLARE1(irq_req, id); */ SYSCALL_DECLARE1(exit, tid); +/** + * Request that a thread becomes orphant, i.e. eventually attached to the init + * process. Can be used to stop threads within a process by sending an + * appropriate signal to the troublesome thread which then detaches itself from + * its root process. + * + * @param t Current tcb. + * @param a Unused. + * @param b Unused. + * @param c Unused. + * @param d Unused. + * @param e Unused. + */ +SYSCALL_DECLARE0(detach); + /** @} */ /** diff --git a/src/orphanage.c b/src/orphanage.c index 7415e8b..4760f73 100644 --- a/src/orphanage.c +++ b/src/orphanage.c @@ -14,17 +14,17 @@ bool orphan(struct tcb *t) { - struct tcb *r = get_rproc(t); - return !r || r->dead; + return is_set(t->state, TCB_ORPHAN); } void orphanize(struct tcb *t) { - catastrophic_assert(!is_rpc(t)); + set_bit(t->state, TCB_ORPHAN); +} - struct tcb *r = get_tcb(t->rid); - if (r) - unreference_proc(r); +void unorphanize(struct tcb *t) +{ + catastrophic_assert(!is_rpc(t)); /* attach to init process */ struct tcb *init = get_tcb(1); @@ -35,6 +35,7 @@ void orphanize(struct tcb *t) t->eid = 1; t->proc = init->proc; + t->rpc_stack = RPC_STACK_BASE; use_vmem(t->proc.vmem); catastrophic_assert(init->callback); @@ -127,7 +127,7 @@ struct tcb *create_thread(struct tcb *p) id_t tid = __alloc_tid(t); tcbs[tid] = t; t->tid = tid; - t->dead = false; + t->state = 0; if (likely(p)) { t->pid = p->pid; @@ -235,6 +235,10 @@ stat_t destroy_thread(struct tcb *t) unqueue_ipi(t); + /** @todo timers, irqs? theoretically we could allow them to stay and + * let the handler check if the thread is still interested in the + * interrupt */ + /* someone still relies on us existing, don't actually free thread data * quite yet */ if (t->refcount) @@ -255,9 +259,7 @@ stat_t destroy_proc(struct tcb *p) * the segfault handler if the thread has become orphaned. * Currently no segfault handler exists, though. */ - p->dead = true; - /* unreference ourselves */ - unreference_proc(p); + set_bits(p->state, TCB_ZOMBIE); /* clear all privately owned memory regions, keep shared ones alive for * now */ @@ -284,7 +286,7 @@ void unreference_proc(struct tcb *p) hard_assert(is_proc(p), RETURN_VOID); p->refcount--; - if (p->dead && p->refcount == 0) { + if (zombie(p) && p->refcount == 0) { dbg("thread %d is completely destroyed\n", p->tid); __destroy_thread_data(p); } diff --git a/src/uapi/dispatch.c b/src/uapi/dispatch.c index 7cfce4c..c4ad811 100644 --- a/src/uapi/dispatch.c +++ b/src/uapi/dispatch.c @@ -81,6 +81,7 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b, case SYS_POWEROFF: sys_poweroff(t, a, b, c, d, e); break; case SYS_SLEEP: sys_sleep(t, a, b, c, d, e); break; case SYS_IRQ_REQ: sys_irq_req(t, a, b, c, d, e); break; + case SYS_DETACH: sys_detach(t, a, b, c, d, e); break; case SYS_EXIT: sys_exit(t, a, b, c, d, e); break; default: error("Syscall %zu outside allowed range [0 - %i]\n", syscall, diff --git a/src/uapi/ipc.c b/src/uapi/ipc.c index f3896f5..bc49dbb 100644 --- a/src/uapi/ipc.c +++ b/src/uapi/ipc.c @@ -83,7 +83,7 @@ static vm_t enter_rpc(struct tcb *t, struct sys_ret a, { /* reuse current rpc stack location if we're being kicked */ vm_t rpc_stack = (kind == IPC_KICK && - is_rpc(t)) ? t->rpc_stack :rpc_position(t); + is_rpc(t)) ? t->rpc_stack : rpc_position(t); struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1; ctx->regs = t->regs; @@ -179,8 +179,8 @@ void notify(struct tcb *t, enum notify_flag flag) return; struct tcb *r = get_tcb(t->notify_id); - if (!r || !r->callback) { - error("notify callback dead\n"); + if (!r || r->state || !r->callback) { + error("notify callback unavailable\n"); t->notify_flags = 0; return; } @@ -218,31 +218,34 @@ static void leave_rpc(struct tcb *t, struct sys_ret a) { vm_t rpc_stack = t->rpc_stack + BASE_PAGE_SIZE; struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1; - vm_t top = ctx->rpc_stack; t->regs = ctx->regs; /* again, get rid of args as fast as possible */ set_args(t, 6, a); struct tcb *r = get_tcb(ctx->pid); - while (!r || r->dead) { + while (!r || !is_proc(r) || zombie(r)) { /* we unwound back to our root process which is apparently dead, * we're orphaned :( */ - if (ctx->pid == t->rid) { + if (rpc_stack_empty(ctx->rpc_stack)) { orphanize(t); - return; + break; } rpc_stack = ctx->rpc_stack + BASE_PAGE_SIZE; ctx = (struct call_ctx *)(rpc_stack) - 1; r = get_tcb(ctx->pid); + set_args1(t, ERR_NF); } + if (orphan(t) && !is_rpc(t)) + unorphanize(t); + set_return(t, ctx->exec); /* if we're returning from a failed rpc, this should essentially be a * no-op */ - mark_rpc_valid(t, top); + mark_rpc_valid(t, ctx->rpc_stack); t->rpc_stack = ctx->rpc_stack; t->pid = ctx->pid; t->eid = ctx->eid; diff --git a/src/uapi/proc.c b/src/uapi/proc.c index 3ce808f..231cc32 100644 --- a/src/uapi/proc.c +++ b/src/uapi/proc.c @@ -235,6 +235,35 @@ SYSCALL_DEFINE1(exit)(struct tcb *t, sys_arg_t tid) } /** + * Syscall handler for orphanizing a thread. + * + * @param t Thread that wants to make itself an orphant. + * @return \ref OK on success, + * \ref ERR_PERM if current process missing \ref CAP_PROC, + * \ref ERR_INVAL if already an orphant. + */ +SYSCALL_DEFINE0(detach)(struct tcb *t) +{ + struct tcb *c = get_cproc(t); + if (!(has_cap(c->caps, CAP_PROC))) + return_args1(t, ERR_PERM); + + if (orphan(t)) + return_args1(t, ERR_INVAL); + + struct tcb *r = get_tcb(t->rid); + if (r) + unreference_proc(r); + + orphanize(t); + + if (!is_rpc(t)) + unorphanize(t); + + return_args1(t, OK); +} + +/** * Swap syscall handler. * * \todo Implement. |
