diff options
Diffstat (limited to 'src/uapi')
| -rw-r--r-- | src/uapi/dispatch.c | 3 | ||||
| -rw-r--r-- | src/uapi/ipc.c | 114 | ||||
| -rw-r--r-- | src/uapi/irq.c | 12 | ||||
| -rw-r--r-- | src/uapi/proc.c | 9 |
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)); } |
