summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
authordzwdz2023-01-08 21:14:03 +0100
committerdzwdz2023-01-08 21:14:03 +0100
commitc178ab5d5ca328d5b0072d54e3dc66d1b198df7b (patch)
tree0f9e3da6fc6133ab4bab052661bf8903e354d804 /src/kernel
parent5defe97cc8b870fa50ba01999f80974b8b21c5b5 (diff)
kernel: let parents kill their children again
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/proc.c56
-rw-r--r--src/kernel/proc.h6
-rw-r--r--src/kernel/syscalls.c5
3 files changed, 59 insertions, 8 deletions
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;