aboutsummaryrefslogtreecommitdiff
path: root/src/uapi
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-07-04 19:25:12 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-07-04 19:25:12 +0300
commit07c2376702fb3d508d6ffad6b0ce93b83972ad6e (patch)
tree9ce46b000f8411fa86118645063b4df05d018dbf /src/uapi
parent66e7f184a925247bac51aae02d01483faa5454fc (diff)
downloadkmi-07c2376702fb3d508d6ffad6b0ce93b83972ad6e.tar.gz
kmi-07c2376702fb3d508d6ffad6b0ce93b83972ad6e.zip
add zombie and orphan threads
+ Should write this down somewhere but the idea is that when a process gets killed, it frees all the memory it can, making all threads within that process orphans. Orphaned threads are assigned to the init process, which will generally call exit() on each one. Zombie threads are threads that own some bit of shared data, and whose reference count is above zero. They may not be swapped to or called, even though they take up space in thread map and reserve their thread ID.
Diffstat (limited to 'src/uapi')
-rw-r--r--src/uapi/conf.c1
-rw-r--r--src/uapi/dispatch.c3
-rw-r--r--src/uapi/ipc.c40
-rw-r--r--src/uapi/proc.c100
4 files changed, 105 insertions, 39 deletions
diff --git a/src/uapi/conf.c b/src/uapi/conf.c
index 242239c..685d294 100644
--- a/src/uapi/conf.c
+++ b/src/uapi/conf.c
@@ -12,6 +12,7 @@
#include <kmi/sizes.h>
#include <kmi/uapi.h>
#include <kmi/conf.h>
+#include <arch/irq.h>
#include <arch/proc.h>
diff --git a/src/uapi/dispatch.c b/src/uapi/dispatch.c
index f0ff724..7cfce4c 100644
--- a/src/uapi/dispatch.c
+++ b/src/uapi/dispatch.c
@@ -66,7 +66,7 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b,
case SYS_IPC_KICK: sys_ipc_kick(t, a, b, c, d, e); break;
case SYS_IPC_RESP: sys_ipc_resp(t, a, b, c, d, e); break;
case SYS_IPC_GHOST: sys_ipc_ghost(t, a, b, c, d, e); break;
- case SYS_IPC_NOTIFY: sys_ipc_notify(t, a, b, c, d, e); break;
+ case SYS_NOTIFY: sys_notify(t, a, b, c, d, e); break;
case SYS_CREATE: sys_create(t, a, b, c, d, e); break;
case SYS_FORK: sys_fork(t, a, b, c, d, e); break;
case SYS_EXEC: sys_exec(t, a, b, c, d, e); break;
@@ -81,6 +81,7 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b,
case SYS_POWEROFF: sys_poweroff(t, a, b, c, d, e); break;
case SYS_SLEEP: sys_sleep(t, a, b, c, d, e); break;
case SYS_IRQ_REQ: sys_irq_req(t, a, b, c, d, e); break;
+ case SYS_EXIT: sys_exit(t, a, b, c, d, e); break;
default:
error("Syscall %zu outside allowed range [0 - %i]\n", syscall,
SYS_NUM - 1);
diff --git a/src/uapi/ipc.c b/src/uapi/ipc.c
index 069ca59..f3896f5 100644
--- a/src/uapi/ipc.c
+++ b/src/uapi/ipc.c
@@ -6,6 +6,7 @@
* Interprocess communication syscall implementations.
*/
+#include <kmi/orphanage.h>
#include <kmi/debug.h>
#include <kmi/uapi.h>
#include <kmi/tcb.h>
@@ -29,9 +30,6 @@ struct call_ctx {
/** Current process ID. */
id_t pid;
-
- /** Whether this context should be skipped when responding. */
- bool kick;
};
/**
@@ -83,7 +81,9 @@ static void finalize_rpc(struct tcb *t, struct tcb *r, vm_t s)
static vm_t enter_rpc(struct tcb *t, struct sys_ret a,
enum ipc_kind kind)
{
- vm_t rpc_stack = rpc_position(t);
+ /* reuse current rpc stack location if we're being kicked */
+ vm_t rpc_stack = (kind == IPC_KICK &&
+ is_rpc(t)) ? t->rpc_stack :rpc_position(t);
struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1;
ctx->regs = t->regs;
@@ -98,9 +98,6 @@ static vm_t enter_rpc(struct tcb *t, struct sys_ret a,
ctx->eid = t->eid;
ctx->rpc_stack = rpc_stack;
- /* only rpcs can be kicked forward */
- ctx->kick = kind == IPC_KICK && is_rpc(t);
-
/** @todo if we run out of rpc_stack space we should just stop, likely
* return a status? except it shouldn't happen after we've run
* enough_rpc_stack(). */
@@ -223,17 +220,25 @@ static void leave_rpc(struct tcb *t, struct sys_ret a)
struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1;
vm_t top = ctx->rpc_stack;
- /* find first instance of not kicked context */
- while (ctx->kick) {
- rpc_stack = ctx->rpc_stack + BASE_PAGE_SIZE;
- ctx = (struct call_ctx *)(rpc_stack) - 1;
- unreference_proc(get_tcb(ctx->pid));
- }
-
t->regs = ctx->regs;
/* again, get rid of args as fast as possible */
set_args(t, 6, a);
+ struct tcb *r = get_tcb(ctx->pid);
+ while (!r || r->dead) {
+ /* we unwound back to our root process which is apparently dead,
+ * we're orphaned :( */
+ if (ctx->pid == t->rid) {
+ orphanize(t);
+ return;
+ }
+
+ rpc_stack = ctx->rpc_stack + BASE_PAGE_SIZE;
+ ctx = (struct call_ctx *)(rpc_stack) - 1;
+
+ r = get_tcb(ctx->pid);
+ }
+
set_return(t, ctx->exec);
/* if we're returning from a failed rpc, this should essentially be a
* no-op */
@@ -301,13 +306,12 @@ static void do_ipc(struct tcb *t,
vm_t s = enter_rpc(t, SYS_RET6(t->eid, t->tid, d0, d1, d2, d3), kind);
struct tcb *r = get_tcb(pid);
- if (unlikely(!r)) {
+ if (unlikely(!r || !is_proc(r))) {
leave_rpc(t, SYS_RET1(ERR_INVAL));
return;
}
- r = get_rproc(r);
- if (unlikely(r->dead)) {
+ if (unlikely(zombie(r))) {
leave_rpc(t, SYS_RET1(ERR_INVAL));
return;
}
@@ -430,7 +434,7 @@ SYSCALL_DEFINE0(ipc_ghost)(struct tcb *t)
* @param tid Thread ID to notify.
* @return \ref OK and 0.
*/
-SYSCALL_DEFINE1(ipc_notify)(struct tcb *t, sys_arg_t tid){
+SYSCALL_DEFINE1(notify)(struct tcb *t, sys_arg_t tid){
if (t->tid != tid && !has_cap(t->caps, CAP_NOTIFY))
return_args1(t, ERR_PERM);
diff --git a/src/uapi/proc.c b/src/uapi/proc.c
index 1b672ce..3ce808f 100644
--- a/src/uapi/proc.c
+++ b/src/uapi/proc.c
@@ -10,7 +10,9 @@
#include <kmi/uapi.h>
#include <kmi/proc.h>
#include <kmi/bits.h>
+#include <kmi/power.h>
#include <kmi/notify.h>
+#include <kmi/orphanage.h>
#include <kmi/mem_regions.h>
#include <arch/irq.h>
@@ -145,24 +147,94 @@ SYSCALL_DEFINE2(spawn)(struct tcb *t, sys_arg_t bin, sys_arg_t interp)
* Kill syscall handler.
*
* @param t Current tcb.
- * @param tid Thread to kill.
+ * @param pid Process to kill.
* \todo Implement.
*
* @return ERR_PERM if not capable to kill, otherwise OK.
*/
-SYSCALL_DEFINE1(kill)(struct tcb *t, sys_arg_t tid)
+SYSCALL_DEFINE1(kill)(struct tcb *t, sys_arg_t pid)
{
struct tcb *c = get_cproc(t);
if (!(has_cap(c->caps, CAP_PROC)))
return_args1(t, ERR_PERM);
- /** @todo implement */
- /** @todo remember to unregister IRQ handlers */
+ struct tcb *r = get_tcb(pid);
+ if (is_proc(r))
+ return_args1(t, ERR_INVAL);
+ destroy_proc(r);
return_args1(t, OK);
}
/**
+ * Actual worker of swapping between threads.
+ * Assumes that both \p t and \p s exist and that \p s isn't a zombie or
+ * currently running.
+ *
+ * @param t Current tcb.
+ * @param s Thread to swap to.
+ */
+static void swap(struct tcb *t, struct tcb *s)
+{
+ /* switch over to new thread */
+ use_tcb(s);
+
+ /* if an irq handler is directly swapping to some other thread,
+ * interpret it as the thread being finished with its critical section */
+ enable_irqs();
+
+ if (!is_rpc(s) && orphan(s)) {
+ orphanize(s);
+ return;
+ }
+
+ /* set return value for current thread */
+ set_args1(t, OK);
+
+ /* handle possible queued notification */
+ if (s->notify_flags)
+ notify(s, 0);
+
+ /* no notifications, so get register state for new thread */
+ return_args(s, get_args(s));
+}
+
+/**
+ * Syscall handler for exit syscall.
+ *
+ * @param t Thread that is exiting.
+ * @param tid Thread to swap to.
+ *
+ * @return \ref OK or \ref ERR_INVAL is \p tid is not a thread
+ * or \ref ERR_NF if \p tid is a zombie
+ * or \ref ERR_EXT if \p tid is currently running.
+ */
+SYSCALL_DEFINE1(exit)(struct tcb *t, sys_arg_t tid)
+{
+ if (tid != 0) {
+ struct tcb *s = get_tcb(tid);
+ if (!s)
+ return_args1(t, ERR_INVAL);
+
+ if (zombie(s))
+ return_args1(t, ERR_NF);
+
+ if (running(s))
+ return_args1(t, ERR_EXT);
+
+
+ swap(t, s);
+ }
+
+ destroy_thread(t);
+
+ if (tid == 0) {
+ enable_irqs();
+ sleep();
+ }
+}
+
+/**
* Swap syscall handler.
*
* \todo Implement.
@@ -183,23 +255,11 @@ SYSCALL_DEFINE1(swap)(struct tcb *t, sys_arg_t tid){
if (!s)
return_args1(t, ERR_INVAL);
+ if (zombie(s))
+ return_args1(t, ERR_NF);
+
if (running(s))
return_args1(t, ERR_EXT);
- /* switch over to new thread */
- use_tcb(s);
-
- /* set return value for current thread */
- set_args1(t, OK);
-
- /* if an irq handler is directly swapping to some other thread,
- * interpret it as the thread being finished with its critical section */
- enable_irqs();
-
- /* handle possible queued notification */
- if (s->notify_flags)
- notify(s, 0);
-
- /* no notifications, so get register state for new thread */
- return_args(s, get_args(s));
+ return swap(t, s);
}