From 2cb94c2c2e3b68b10a0766d5a05b125159a81e50 Mon Sep 17 00:00:00 2001 From: Matheus Date: Sun, 14 Apr 2024 14:00:11 -0300 Subject: [PATCH] scheduler: handle userland thread exit --- kernel-src/arch/x86-64/context.asm | 3 ++ kernel-src/arch/x86-64/syscall.asm | 5 ++ kernel-src/include/x86-64/arch/context.h | 1 + kernel-src/sys/scheduler.c | 60 +++++++++++++++++++++++- kernel-src/sys/syscalls/exit.c | 36 -------------- 5 files changed, 68 insertions(+), 37 deletions(-) diff --git a/kernel-src/arch/x86-64/context.asm b/kernel-src/arch/x86-64/context.asm index c0f08553..8bfedf64 100644 --- a/kernel-src/arch/x86-64/context.asm +++ b/kernel-src/arch/x86-64/context.asm @@ -1,6 +1,9 @@ section .text global arch_context_switch arch_context_switch: + extern sched_userspacecheck + call sched_userspacecheck ; compiled with no_caller_saved_registers + ; rdi is the pointer to the context struct mov rsp, rdi diff --git a/kernel-src/arch/x86-64/syscall.asm b/kernel-src/arch/x86-64/syscall.asm index d5fb4ebc..89b04804 100644 --- a/kernel-src/arch/x86-64/syscall.asm +++ b/kernel-src/arch/x86-64/syscall.asm @@ -195,6 +195,11 @@ arch_syscall_entry: extern arch_syscall_log_return call arch_syscall_log_return ; compiled with __attribute__((no_caller_saved_registers)) + mov rdi, rsp + add rdi, 8 ; context pointer is after r9 argument + extern sched_userspacecheck + call sched_userspacecheck ; compiled with __attribute__((no_caller_saved_registers)) + ; restore context add rsp, 32 ; r9 argument, cr2, gs, and fs are not popped. pop rbx diff --git a/kernel-src/include/x86-64/arch/context.h b/kernel-src/include/x86-64/arch/context.h index 02e4b1c9..081049ed 100644 --- a/kernel-src/include/x86-64/arch/context.h +++ b/kernel-src/include/x86-64/arch/context.h @@ -98,5 +98,6 @@ void arch_context_saveandcall(void (*fn)(context_t *context), void *stack); asm("ldmxcsr (%%rax)" : : "a"(&(t)->extracontext.mxcsr)); #define ARCH_CONTEXT_INTSTATUS(x) ((x)->rflags & 0x200 ? true : false) +#define ARCH_CONTEXT_ISUSER(x) ((x)->cs == 0x23) #endif diff --git a/kernel-src/sys/scheduler.c b/kernel-src/sys/scheduler.c index 677e483b..23f93e03 100644 --- a/kernel-src/sys/scheduler.c +++ b/kernel-src/sys/scheduler.c @@ -229,6 +229,47 @@ __attribute__((noreturn)) void sched_stopcurrentthread() { switchthread(next); } +void sched_procexit() { + proc_t *proc = _cpu()->thread->proc; + + // close fds + for (int fd = 0; fd < proc->fdcount; ++fd) + fd_close(fd); + + // zombify the proc + spinlock_acquire(&proc->lock); + + VOP_RELEASE(proc->root); + VOP_RELEASE(proc->cwd); + + proc->state = SCHED_PROC_STATE_ZOMBIE; + proc_t *lastchild = proc->child; + + spinlock_acquire(&sched_initproc->lock); + + bool haszombie = false; + + while (lastchild && lastchild->sibling) { + if (lastchild->state == SCHED_PROC_STATE_ZOMBIE) + haszombie = true; + + lastchild->parent = sched_initproc; + lastchild = lastchild->sibling; + } + + if (lastchild) { + lastchild->sibling = sched_initproc->child; + sched_initproc->child = proc->child; + } + + spinlock_release(&sched_initproc->lock); + spinlock_release(&proc->lock); + + semaphore_signal(&proc->parent->waitsem); + if (haszombie) + semaphore_signal(&sched_initproc->waitsem); +} + __attribute__((noreturn)) void sched_threadexit() { thread_t *thread = _cpu()->thread; proc_t *proc = thread->proc; @@ -239,8 +280,13 @@ __attribute__((noreturn)) void sched_threadexit() { if (proc) { __atomic_fetch_sub(&proc->runningthreadcount, 1, __ATOMIC_SEQ_CST); __assert(oldctx != &vmm_kernelctx); - if (proc->runningthreadcount == 0) + if (proc->runningthreadcount == 0) { + if (thread->shouldexit) + proc->status = -1; + + sched_procexit(); vmm_destroycontext(oldctx); + } } interrupt_set(false); @@ -253,6 +299,18 @@ __attribute__((noreturn)) void sched_threadexit() { sched_stopcurrentthread(); } +// called right before going back to userspace in places like the syscall handler or arch_context_switch +__attribute__((no_caller_saved_registers)) void sched_userspacecheck(context_t *context) { + thread_t *thread = _cpu()->thread; + if (thread == NULL || ARCH_CONTEXT_ISUSER(context) == false) + return; + + if (thread->shouldexit) { + interrupt_set(true); + sched_threadexit(); + } +} + void sched_stopotherthreads() { thread_t *thread = _cpu()->thread; proc_t *proc = thread->proc; diff --git a/kernel-src/sys/syscalls/exit.c b/kernel-src/sys/syscalls/exit.c index ab7d4d3c..9558fb25 100644 --- a/kernel-src/sys/syscalls/exit.c +++ b/kernel-src/sys/syscalls/exit.c @@ -13,43 +13,7 @@ __attribute__((noreturn)) void syscall_exit(context_t *context, int status) { sched_stopotherthreads(); - // close fds - for (int fd = 0; fd < proc->fdcount; ++fd) - fd_close(fd); - - // zombify the proc - spinlock_acquire(&proc->lock); - - VOP_RELEASE(proc->root); - VOP_RELEASE(proc->cwd); - proc->status = status; - proc->state = SCHED_PROC_STATE_ZOMBIE; - proc_t *lastchild = proc->child; - - spinlock_acquire(&sched_initproc->lock); - - bool haszombie = false; - - while (lastchild && lastchild->sibling) { - if (lastchild->state == SCHED_PROC_STATE_ZOMBIE) - haszombie = true; - - lastchild->parent = sched_initproc; - lastchild = lastchild->sibling; - } - - if (lastchild) { - lastchild->sibling = sched_initproc->child; - sched_initproc->child = proc->child; - } - - spinlock_release(&sched_initproc->lock); - spinlock_release(&proc->lock); - - semaphore_signal(&proc->parent->waitsem); - if (haszombie) - semaphore_signal(&sched_initproc->waitsem); sched_threadexit(); }