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 +++++ 3 files changed, 59 insertions(+), 8 deletions(-) (limited to 'src/kernel') 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; -- cgit v1.2.3