From c178ab5d5ca328d5b0072d54e3dc66d1b198df7b Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sun, 8 Jan 2023 21:14:03 +0100 Subject: kernel: let parents kill their children again --- src/kernel/proc.c | 56 ++++++++++++++++++++++++++++----- src/kernel/proc.h | 6 +++- src/kernel/syscalls.c | 5 +++ src/shared/include/camellia/syscalls.h | 9 ++---- src/user/app/init/init.c | 1 + src/user/app/tests/kernel/miscsyscall.c | 16 ++++++++++ src/user/app/tests/libc/esemaphore.c | 1 + src/user/app/tests/tests.c | 5 +-- src/user/lib/syscall.c | 4 +++ 9 files changed, 87 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/kernel/proc.c b/src/kernel/proc.c index 374a031..36e6004 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -162,8 +162,9 @@ void process_kill(struct process *p, int ret) { *iter = p->waits4pipe.next; } - if (p->state == PS_WAITS4TIMER) + if (p->state == PS_WAITS4TIMER) { timer_deschedule(p); + } if (unref(p->handles_refcount)) { for (handle_t hid = 0; hid < HANDLE_MAX; hid++) @@ -174,9 +175,18 @@ void process_kill(struct process *p, int ret) { vfs_mount_remref(p->mount); p->mount = NULL; - process_transition(p, PS_TOREAP); + process_transition(p, PS_DYING); p->death_msg = ret; + /* tombstone TOREAP children */ + for (struct process *it = p->child; it; ) { + struct process *sibling = it->sibling; + if (it->state == PS_TOREAP) { + process_tryreap(it); + } + it = sibling; + } + if (p->execbuf.buf) { kfree(p->execbuf.buf); p->execbuf.buf = NULL; @@ -187,7 +197,9 @@ void process_kill(struct process *p, int ret) { } } - assert(!proc_alive(p)); + if (p->state == PS_DYING) { + process_transition(p, PS_TOREAP); + } process_tryreap(p); if (p == process_first) { @@ -195,13 +207,34 @@ void process_kill(struct process *p, int ret) { } } +void process_filicide(struct process *parent, int ret) { + // O(n^2), but doable in linear time + for (;;) { + struct process *p = parent->child; + while (p && p->state == PS_TOREAP) { + p = p->sibling; + } + if (!p) return; /* no more killable children */ + while (p->child) { + p = p->child; + } + if (p->parent != parent) { + /* prevent infinite loop */ + p->noreap = true; + } + process_kill(p, ret); + } +} + void process_tryreap(struct process *dead) { + // TODO low try writing a linter that prevents other functions from using *dead afterwards struct process *parent; assert(dead && !proc_alive(dead)); parent = dead->parent; if (dead->state == PS_TOREAP) { - if (!dead->noreap && parent && proc_alive(parent)) { /* reapable? */ + if (!dead->noreap && parent && proc_alive(parent) && parent->state != PS_DYING) { + /* reapable? */ if (parent->state != PS_WAITS4CHILDDEATH) { return; /* don't reap yet */ } @@ -210,6 +243,7 @@ void process_tryreap(struct process *dead) { } /* can't be reaped anymore */ process_transition(dead, PS_TOMBSTONE); + dead->noreap = true; } assert(dead->state == PS_TOMBSTONE); @@ -218,13 +252,18 @@ void process_tryreap(struct process *dead) { } if (dead->child) { + struct process *p = dead->child; + while (p->child) p = p->child; + assert(p->state != PS_TOREAP); + assert(p->state != PS_TOMBSTONE); + assert(proc_alive(p)); + return; /* keep the tombstone */ } handle_close(dead->specialh.procfs); assert(dead->refcount == 0); - if (parent) { - /* not applicable to init */ + if (parent) { /* not applicable to init */ process_forget(dead); kfree(dead); @@ -238,6 +277,7 @@ void process_tryreap(struct process *dead) { /** Removes a process from the process tree. */ static void process_forget(struct process *p) { assert(p->parent); + assert(p->parent->child); if (p->parent->child == p) { p->parent->child = p->sibling; @@ -383,7 +423,9 @@ void process_transition(struct process *p, enum process_state state) { assert(p->state != PS_TOMBSTONE); if (state == PS_TOMBSTONE) { assert(p->state == PS_TOREAP); - } else if (state != PS_RUNNING && state != PS_TOREAP) { + } else if (state == PS_TOREAP) { + assert(p->state == PS_DYING); + } else if (state != PS_RUNNING && state != PS_DYING) { assert(p->state == PS_RUNNING); } p->state = state; diff --git a/src/kernel/proc.h b/src/kernel/proc.h index 135baad..fa50520 100644 --- a/src/kernel/proc.h +++ b/src/kernel/proc.h @@ -9,6 +9,7 @@ struct vfs_mount; /* legal transitions described by process_transition */ enum process_state { PS_RUNNING, + PS_DYING, /* during process_kill - mostly treated as alive */ PS_TOREAP, /* return message not collected */ PS_TOMBSTONE, /* fully dead, supports alive children */ @@ -85,7 +86,10 @@ struct process *process_seed(void *data, size_t datalen); struct process *process_fork(struct process *parent, int flags); void process_kill(struct process *proc, int ret); -/** Tries to reap a dead process / free a tombstone. */ +/** Kills all descendants. */ +void process_filicide(struct process *proc, int ret); +/** Tries to reap a dead process / free a tombstone. + * Can also free all dead parents of *dead. Be careful. */ void process_tryreap(struct process *dead); /** Switches execution to any running process. */ diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 8e42f8e..8bd7de3 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -367,6 +367,10 @@ void _syscall_sleep(long ms) { timer_schedule(process_current, uptime_ms() + ms); } +void _syscall_filicide(void) { + process_filicide(process_current, -1); +} + long _syscall_execbuf(void __user *ubuf, size_t len) { if (len == 0) SYSCALL_RETURN(0); if (len > EXECBUF_MAX_LEN) @@ -416,6 +420,7 @@ long _syscall(long num, long a, long b, long c, long d, long e) { break; case _SYSCALL_MEMFLAG: _syscall_memflag((userptr_t)a, b, c); break; case _SYSCALL_PIPE: _syscall_pipe((userptr_t)a, b); break; case _SYSCALL_SLEEP: _syscall_sleep(a); + break; case _SYSCALL_FILICIDE: _syscall_filicide(); break; case _SYSCALL_EXECBUF: _syscall_execbuf((userptr_t)a, b); break; case _SYSCALL_DEBUG_KLOG: _syscall_debug_klog((userptr_t)a, b); break; diff --git a/src/shared/include/camellia/syscalls.h b/src/shared/include/camellia/syscalls.h index f9e3832..690e928 100644 --- a/src/shared/include/camellia/syscalls.h +++ b/src/shared/include/camellia/syscalls.h @@ -3,27 +3,22 @@ #define _SYSCALL_EXIT 0 #define _SYSCALL_AWAIT 1 #define _SYSCALL_FORK 2 - #define _SYSCALL_OPEN 3 #define _SYSCALL_MOUNT 4 #define _SYSCALL_DUP 5 - #define _SYSCALL_READ 6 #define _SYSCALL_WRITE 7 #define _SYSCALL_GETSIZE 8 #define _SYSCALL_REMOVE 9 #define _SYSCALL_CLOSE 10 - #define _SYSCALL_FS_WAIT 11 #define _SYSCALL_FS_RESPOND 12 - #define _SYSCALL_MEMFLAG 13 #define _SYSCALL_PIPE 14 - #define _SYSCALL_SLEEP 15 +#define _SYSCALL_FILICIDE 16 #define _SYSCALL_EXECBUF 100 - #define _SYSCALL_DEBUG_KLOG 101 #ifndef ASM_FILE @@ -78,6 +73,8 @@ long _syscall_pipe(handle_t __user user_ends[2], int flags); void _syscall_sleep(long ms); +void _syscall_filicide(void); + /* see shared/execbuf.h */ long _syscall_execbuf(void __user *buf, size_t len); diff --git a/src/user/app/init/init.c b/src/user/app/init/init.c index 96b2eb7..0af0150 100644 --- a/src/user/app/init/init.c +++ b/src/user/app/init/init.c @@ -104,5 +104,6 @@ int main(void) { } _syscall_read(killswitch_pipe[0], NULL, 0, 0); + _syscall_filicide(); return 0; } diff --git a/src/user/app/tests/kernel/miscsyscall.c b/src/user/app/tests/kernel/miscsyscall.c index 66899b1..58a9afb 100644 --- a/src/user/app/tests/kernel/miscsyscall.c +++ b/src/user/app/tests/kernel/miscsyscall.c @@ -26,6 +26,20 @@ static void test_await(void) { test(counts[i] == 1); } +static void test_await2(void) { + /* hangs on failure */ + if (!fork()) { + if (!fork()) { + for (;;) _syscall_sleep(1000000000); + } else { + exit(123); + } + } + test(_syscall_await() == 123); + test(_syscall_await() == ~0); /* don't wait for tombstone */ + _syscall_filicide(); +} + static void test_pipe(void) { const char *pipe_msgs[2] = {"hello", "world"}; handle_t ends[2]; @@ -246,6 +260,7 @@ static void test_sleep(void) { for (;;) _syscall_sleep(1000000000); } _syscall_await(); + _syscall_filicide(); _syscall_exit(0); } @@ -277,6 +292,7 @@ static void test_badopen(void) { void r_k_miscsyscall(void) { run_test(test_await); + run_test(test_await2); run_test(test_pipe); run_test(test_memflag); run_test(test_dup); diff --git a/src/user/app/tests/libc/esemaphore.c b/src/user/app/tests/libc/esemaphore.c index c78dd56..f1856ff 100644 --- a/src/user/app/tests/libc/esemaphore.c +++ b/src/user/app/tests/libc/esemaphore.c @@ -68,6 +68,7 @@ static void test_semaphore(void) { esem_free(sem1); esem_free(sem2); + _syscall_filicide(); exit(0); } else { close(pipe[1]); diff --git a/src/user/app/tests/tests.c b/src/user/app/tests/tests.c index 90a4978..f2f9aa5 100644 --- a/src/user/app/tests/tests.c +++ b/src/user/app/tests/tests.c @@ -10,6 +10,7 @@ FILE *fail_trig; void run_test(void (*fn)()) { if (!fork()) { fn(); + _syscall_filicide(); exit(0); } else { /* successful tests must return 0 */ @@ -41,10 +42,10 @@ int forkpipe(FILE **f, handle_t *h) { int main(void) { handle_t reader; if (!forkpipe(&fail_trig, &reader)) { - r_k_fdlimit(); + r_k_miscsyscall(); r_k_fs(); + r_k_fdlimit(); r_k_misc(); - r_k_miscsyscall(); r_k_path(); r_k_threads(); r_libc_esemaphore(); diff --git a/src/user/lib/syscall.c b/src/user/lib/syscall.c index 50c71a8..d6ed0af 100644 --- a/src/user/lib/syscall.c +++ b/src/user/lib/syscall.c @@ -70,6 +70,10 @@ void _syscall_sleep(long ms) { return (void)_syscall(_SYSCALL_SLEEP, ms, 0, 0, 0, 0); } +void _syscall_filicide(void) { + return (void)_syscall(_SYSCALL_FILICIDE, 0, 0, 0, 0, 0); +} + long _syscall_execbuf(void __user *buf, size_t len) { return _syscall(_SYSCALL_EXECBUF, (long)buf, (long)len, 0, 0, 0); } -- cgit v1.2.3