aboutsummaryrefslogtreecommitdiff
path: root/src/uapi
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-07-02 21:40:10 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-07-02 21:40:10 +0300
commit8a3452098267e1af127d6c4f1836a9d82cd23f38 (patch)
treec9fad842a8903f64a57027b66ef54bba0dee2928 /src/uapi
parent7ad9c01dad7b1b46eced8290b8b991383d648188 (diff)
downloadkmi-8a3452098267e1af127d6c4f1836a9d82cd23f38.tar.gz
kmi-8a3452098267e1af127d6c4f1836a9d82cd23f38.zip
more complete interrupt handling outline
+ Still largely untested, should really try to come up with a proper testsuite, at the moment it's mostly me trying things out in the (as of yet unreleased) kmx repo
Diffstat (limited to 'src/uapi')
-rw-r--r--src/uapi/dispatch.c3
-rw-r--r--src/uapi/ipc.c114
-rw-r--r--src/uapi/irq.c12
-rw-r--r--src/uapi/proc.c9
4 files changed, 104 insertions, 34 deletions
diff --git a/src/uapi/dispatch.c b/src/uapi/dispatch.c
index a7f0322..f0ff724 100644
--- a/src/uapi/dispatch.c
+++ b/src/uapi/dispatch.c
@@ -56,7 +56,8 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b,
case SYS_FREE_MEM: sys_free_mem(t, a, b, c, d, e); break;
case SYS_TIMEBASE: sys_timebase(t, a, b, c, d, e); break;
case SYS_TICKS: sys_ticks(t, a, b, c, d, e); break;
- case SYS_REQ_NOTIFICATION: sys_req_notification(t, a, b, c, d, e); break;
+ case SYS_REQ_NOTIFICATION: sys_req_notification(t, a, b, c, d, e);
+ break;
case SYS_REQ_REL_TIMER: sys_req_rel_timer(t, a, b, c, d, e); break;
case SYS_REQ_ABS_TIMER: sys_req_abs_timer(t, a, b, c, d, e); break;
case SYS_IPC_SERVER: sys_ipc_server(t, a, b, c, d, e); break;
diff --git a/src/uapi/ipc.c b/src/uapi/ipc.c
index 8df0e3f..a020dc0 100644
--- a/src/uapi/ipc.c
+++ b/src/uapi/ipc.c
@@ -6,6 +6,7 @@
* Interprocess communication syscall implementations.
*/
+#include <kmi/debug.h>
#include <kmi/uapi.h>
#include <kmi/tcb.h>
#include <kmi/ipi.h>
@@ -127,7 +128,7 @@ static vm_t enter_rpc(struct tcb *t, struct sys_ret a,
* @return \c true if there's enough stack left to safely do migration,
* \c false otherwise.
*/
-static bool enough_rpc_stack(struct tcb *t)
+static bool __enough_rpc_stack(struct tcb *t)
{
/* get top of call stack */
vm_t top = rpc_position(t);
@@ -137,27 +138,71 @@ static bool enough_rpc_stack(struct tcb *t)
return top - BASE_PAGE_SIZE - __rpc_stack_size >= RPC_STACK_BASE;
}
-void notify(struct tcb *t)
+/**
+ * Actually run notification handler, no ifs or buts.
+ *
+ * @param t Previous thread.
+ * @param r Next thread.
+ *
+ * \p t and \p r may be the same thread.
+ */
+static __noreturn void __run_notify(struct tcb *t, struct tcb *r)
{
- /* some duplication from do_ipc, but not too bad I guess */
- struct tcb *notify = get_tcb(t->notify_id);
- if (!notify || !notify->callback) {
+ /* signal to whoever is receiving us that we're from the kernel
+ * ("pid 0"), and we are notifying the current thread */
+ vm_t s = enter_rpc(t,
+ SYS_RET5(0, SYS_USER_NOTIFY,
+ t->notify_flags, t->eid, t->tid),
+ IPC_REQ);
+
+ finalize_rpc(t, r, s);
+ t->notify_state = NOTIFY_RUNNING;
+ if (is_set(t->notify_flags, NOTIFY_IRQ | NOTIFY_TIMER))
+ disable_irqs();
+
+ t->notify_flags = 0;
+ ret_userspace_fast();
+ unreachable();
+}
+
+void notify(struct tcb *t, enum notify_flag flag)
+{
+ set_bits(t->notify_flags, flag);
+
+ if (t->notify_state == NOTIFY_RUNNING) {
+ t->notify_state = NOTIFY_QUEUED;
+ return;
+ }
+
+ if (t == cur_tcb())
+ __run_notify(t, t);
+
+ struct tcb *r = get_tcb(t->notify_id);
+ if (!r || !r->callback) {
+ error("notify callback dead\n");
t->notify_state = NOTIFY_WAITING;
+ t->notify_flags = 0;
return;
}
- if (unlikely(!enough_rpc_stack(t))) {
+ if (is_rpc(r)) {
+ t->notify_state = NOTIFY_QUEUED;
+ return;
+ }
+
+ if (unlikely(!__enough_rpc_stack(r))) {
+ bug("not enough rpc stack in root process?");
t->notify_state = NOTIFY_WAITING;
+ t->notify_flags = 0;
return;
}
- /* signal to whoever is receiving us that we're from the kernel
- * ("pid 0"), and we are notifying the current thread */
- vm_t s = enter_rpc(t, SYS_RET4(0, SYS_USER_NOTIFY, t->eid, t->tid), IPC_REQ);
- finalize_rpc(t, notify, s);
- t->notify_state = NOTIFY_RUNNING;
- ret_userspace_fast();
- unreachable();
+ if (running(r)) {
+ send_ipi(r);
+ return;
+ }
+
+ __run_notify(t, r);
}
/**
@@ -204,10 +249,12 @@ static void leave_rpc(struct tcb *t, struct sys_ret a)
return;
}
- notify(t);
+ /* notification queued, try to run it */
+ notify(t, 0);
- /* we failed in notifying the thread, so just return back to the
- * process normally */
+ /* we failed notifying the thread, shouldn't happen but just return
+ * back to process normally */
+ bug("failed notifying from ipc_resp\n");
use_vmem(t->proc.vmem);
}
@@ -251,7 +298,7 @@ static void do_ipc(struct tcb *t,
sys_arg_t d3,
enum ipc_kind kind)
{
- if (unlikely(!enough_rpc_stack(t)))
+ if (unlikely(!__enough_rpc_stack(t)))
return_args1(t, ERR_OOMEM);
vm_t s = enter_rpc(t, SYS_RET6(t->eid, t->tid, d0, d1, d2, d3), kind);
@@ -358,6 +405,12 @@ SYSCALL_DEFINE4(ipc_resp)(struct tcb *t, sys_arg_t d0, sys_arg_t d1,
leave_rpc(t, SYS_RET6(OK, t->pid, d0, d1, d2, d3));
}
+/**
+ * Ghost return, resetting register state.
+ *
+ * @param t Current tcb.
+ * @return The previous registers of thread.
+ */
SYSCALL_DEFINE0(ipc_ghost)(struct tcb *t)
{
if (unlikely(!is_rpc(t)))
@@ -379,25 +432,22 @@ SYSCALL_DEFINE0(ipc_ghost)(struct tcb *t)
*/
SYSCALL_DEFINE1(ipc_notify)(struct tcb *t, sys_arg_t tid){
if (t->tid == tid) {
- /** @todo notify self */
+ set_args1(t, OK);
+ notify(t, NOTIFY_SIGNAL);
+ /* notify is guaranteed to run the current thread */
+ unreachable();
+ return;
}
- if (!has_cap(t->caps, CAP_CALL))
+ if (!has_cap(t->caps, CAP_NOTIFY))
return_args1(t, ERR_PERM);
struct tcb *r = get_tcb(tid);
- if (r->notify_state == NOTIFY_QUEUED)
- return_args1(t, OK);
+ if (!r)
+ return_args1(t, ERR_INVAL);
- if (r->notify_state == NOTIFY_RUNNING) {
- t->notify_state = NOTIFY_QUEUED;
- return_args1(t, OK);
- }
-
- r->notify_state = NOTIFY_QUEUED;
- /* only interrupt if thread is in owning process */
- if (running(r) && r->rid == r->pid)
- send_ipi(r);
-
- return_args1(t, OK);
+ /* set args, if notify swaps us out we pick them up the next time this
+ * thread is scheduled */
+ set_args1(t, OK);
+ notify(r, NOTIFY_SIGNAL);
}
diff --git a/src/uapi/irq.c b/src/uapi/irq.c
index 9bd8ec1..29da614 100644
--- a/src/uapi/irq.c
+++ b/src/uapi/irq.c
@@ -13,6 +13,7 @@
* Actual IRQ handling request syscall handler.
*
* @param t Current tcb.
+ * @param id Which IRQ number to register.
*
* @return OK on success, non-zero otherwise.
*/
@@ -27,9 +28,18 @@ SYSCALL_DEFINE1(irq_req)(struct tcb *t, sys_arg_t id)
return_args1(t, register_irq(t, id));
}
+/**
+ * Actual notification handler setter.
+ *
+ * @param t Current tcb.
+ * @param tid Thread whose handler to set.
+ * @param pid Process that is willing to handle notifications for the thread.
+ *
+ * @return OK on success, non-zero otherwise.
+ */
SYSCALL_DEFINE2(req_notification)(struct tcb *t, sys_arg_t tid, sys_arg_t pid)
{
- if (!has_cap(t->caps, CAP_NOTIFICATION))
+ if (!has_cap(t->caps, CAP_SIGNAL))
return_args1(t, ERR_PERM);
struct tcb *r = get_tcb(tid);
diff --git a/src/uapi/proc.c b/src/uapi/proc.c
index 153b183..e87a2ec 100644
--- a/src/uapi/proc.c
+++ b/src/uapi/proc.c
@@ -10,6 +10,7 @@
#include <kmi/uapi.h>
#include <kmi/proc.h>
#include <kmi/bits.h>
+#include <kmi/notify.h>
#include <kmi/mem_regions.h>
/**
@@ -186,6 +187,14 @@ SYSCALL_DEFINE1(swap)(struct tcb *t, sys_arg_t tid){
/* set return value for current thread */
set_args1(t, OK);
+ /* not running anymore lol */
+ if (t->notify_state == NOTIFY_RUNNING)
+ t->notify_state = NOTIFY_WAITING;
+
+ /* handle possible queued notification */
+ if (s->notify_state == NOTIFY_QUEUED)
+ notify(s, 0);
+
/* get register state for new thread */
return_args(s, get_args(s));
}