summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordzwdz2023-01-08 21:14:03 +0100
committerdzwdz2023-01-08 21:14:03 +0100
commitc178ab5d5ca328d5b0072d54e3dc66d1b198df7b (patch)
tree0f9e3da6fc6133ab4bab052661bf8903e354d804 /src
parent5defe97cc8b870fa50ba01999f80974b8b21c5b5 (diff)
kernel: let parents kill their children again
Diffstat (limited to 'src')
-rw-r--r--src/kernel/proc.c56
-rw-r--r--src/kernel/proc.h6
-rw-r--r--src/kernel/syscalls.c5
-rw-r--r--src/shared/include/camellia/syscalls.h9
-rw-r--r--src/user/app/init/init.c1
-rw-r--r--src/user/app/tests/kernel/miscsyscall.c16
-rw-r--r--src/user/app/tests/libc/esemaphore.c1
-rw-r--r--src/user/app/tests/tests.c5
-rw-r--r--src/user/lib/syscall.c4
9 files changed, 87 insertions, 16 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;
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);
}