aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv64/conf/init.c8
-rw-r--r--arch/riscv64/kernel/csr.h2
-rw-r--r--arch/riscv64/kernel/irq.c8
-rw-r--r--include/kmi/caps.h4
-rw-r--r--include/kmi/ipi.h8
-rw-r--r--include/kmi/list.h156
-rw-r--r--include/kmi/notify.h30
-rw-r--r--include/kmi/queue.h56
-rw-r--r--include/kmi/syscalls.h16
-rw-r--r--include/kmi/tcb.h17
-rw-r--r--include/kmi/timer.h1
-rw-r--r--include/kmi/uapi.h8
-rw-r--r--src/ipi.c26
-rw-r--r--src/irq.c6
-rw-r--r--src/proc.c3
-rw-r--r--src/timer.c2
-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
20 files changed, 411 insertions, 78 deletions
diff --git a/arch/riscv64/conf/init.c b/arch/riscv64/conf/init.c
index 80bb68a..010267e 100644
--- a/arch/riscv64/conf/init.c
+++ b/arch/riscv64/conf/init.c
@@ -36,7 +36,8 @@ char *strcpy(char * restrict dst, const char * restrict src)
}
static inline struct sys_ret ecall(size_t n,
- sys_arg_t arg0, sys_arg_t arg1, sys_arg_t arg2, sys_arg_t arg3,
+ sys_arg_t arg0, sys_arg_t arg1,
+ sys_arg_t arg2, sys_arg_t arg3,
sys_arg_t arg4, sys_arg_t arg5)
{
/* here a static assert of n <= 6 && n >= 1 would be ideal */
@@ -186,8 +187,9 @@ static void sys_ipc_server(void *f)
}
static inline struct sys_ret sys_ipc_req(sys_arg_t pid,
- sys_arg_t d0, sys_arg_t d1, sys_arg_t d2,
- sys_arg_t d3)
+ sys_arg_t d0, sys_arg_t d1,
+ sys_arg_t d2,
+ sys_arg_t d3)
{
struct sys_ret r = ecall6(SYS_IPC_REQ, pid, d0, d1, d2, d3);
diff --git a/arch/riscv64/kernel/csr.h b/arch/riscv64/kernel/csr.h
index bc48464..9b7b78a 100644
--- a/arch/riscv64/kernel/csr.h
+++ b/arch/riscv64/kernel/csr.h
@@ -40,8 +40,6 @@
/** Address of \c sstatus CSR. */
#define CSR_SSTATUS 0x100
-#define SR_SPIE 0b10
-
/** Address of \c sie CSR. */
#define CSR_SIE 0x104
diff --git a/arch/riscv64/kernel/irq.c b/arch/riscv64/kernel/irq.c
index 2ef15cc..0c129b9 100644
--- a/arch/riscv64/kernel/irq.c
+++ b/arch/riscv64/kernel/irq.c
@@ -38,16 +38,18 @@ stat_t deactivate_irq(irq_t id)
return OK;
}
+/** Bit pattern representing supervisor external, timer and software irqs. */
+#define SR_IRQS (1 << 9) | (1 << 5) | (1 << 3)
+
/* very simple for now */
void enable_irqs()
{
- /* should be supervisor external interrupt */
- csr_set(CSR_SIE, 1 << 9);
+ csr_set(CSR_SIE, SR_IRQS);
}
void disable_irqs()
{
- csr_clear(CSR_SIE, 1 << 9);
+ csr_clear(CSR_SIE, SR_IRQS);
}
irq_t get_irq()
diff --git a/include/kmi/caps.h b/include/kmi/caps.h
index 8af9293..158845d 100644
--- a/include/kmi/caps.h
+++ b/include/kmi/caps.h
@@ -27,7 +27,7 @@ enum {
CAP_PROC = (1 << 1),
/** Thread is allowed to force notification in other thread. */
- CAP_CALL = (1 << 2),
+ CAP_NOTIFY = (1 << 2),
/** Thread is allowed to shut down system. */
CAP_POWER = (1 << 3),
@@ -39,7 +39,7 @@ enum {
CAP_IRQ = (1 << 5),
/** Thread is allowed to request notification handler. */
- CAP_NOTIFICATION = (1 << 6),
+ CAP_SIGNAL = (1 << 6),
};
/**
diff --git a/include/kmi/ipi.h b/include/kmi/ipi.h
index 72b54b9..7a0760a 100644
--- a/include/kmi/ipi.h
+++ b/include/kmi/ipi.h
@@ -15,14 +15,6 @@
#include <kmi/tcb.h>
/**
- * Clear potential IPI in \p t, and return its value.
- *
- * @param t \ref tcb to clear possible IPI status of.
- * @return \ref true if \p was interrupted by IPI, \ref false otherwise.
- */
-bool clear_ipi(struct tcb *t);
-
-/**
* Send IPI to \p t. Assumes \c running(t).
*
* @param t \ref tcb to send IPI to.
diff --git a/include/kmi/list.h b/include/kmi/list.h
new file mode 100644
index 0000000..07cddd1
--- /dev/null
+++ b/include/kmi/list.h
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: copyleft-next-0.3.1 */
+/* Copyright 2024, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
+
+#ifndef KMI_LIST_H
+#define KMI_LIST_H
+
+/**
+ * @file list.h
+ *
+ * Relatively simple intrusive doubly-linked list implementation,
+ * more or less directly lifted from Linux. In Linux, the implementation also
+ * includes some circular features that I dropped because I don't really need
+ * them at the moment, but can just be copy-pasted back in (I think? Not a
+ * license lawyer)
+ *
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/list.h
+ */
+
+/** An entry in a list. Generally there's a head somewhere that is more like
+ * just 'metadata' about the list. */
+struct list_head {
+ /** Previous entry. */
+ struct list_head *next;
+
+ /** Next entry. */
+ struct list_head *prev;
+};
+
+/**
+ * Initialized a static list.
+ *
+ * @param name Name of list we're initializing, important because an empty list
+ * should just point to itself.
+ */
+#define INIT_LIST(name) {.next = &(name), .prev = &(name)}
+
+/**
+ * Add \p new between \p prev and \p next.
+ *
+ * @param new New entry to add to list.
+ * @param prev Previous entry.
+ * @param next Next entry.
+ */
+static inline void __list_add(struct list_head *new, struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * Add an entry to the front of the list.
+ *
+ * @param new Entry to add.
+ * @param head Head of list.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * Add an entry to the back of the list.
+ *
+ * @param new Entry to add.
+ * @param head Head of list.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/**
+ * Link \p prev to be directly behind \p next and vice versa.
+ *
+ * @param prev Previous entry.
+ * @param next Next entry.
+ */
+static inline void __list_link(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * Delete an entry from a list by linking the previous and next nodes together.
+ *
+ * @param entry Entry to delete from list. Whatever list that is, doesn't
+ * matter.
+ */
+static inline void __list_del_entry(struct list_head *entry)
+{
+ __list_link(entry->next, entry->prev);
+}
+
+/**
+ * Delete an entry from a list. Sets next and prev pointers to be NULL.
+ *
+ * @param entry Entry to delete from list.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del_entry(entry);
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+/**
+ * Check if entry is part of some list.
+ *
+ * @param entry Entry whose list occupancy should be checked.
+ * @return \ref true if entry is in some list, \ref false otherwise.
+ */
+static inline bool in_list(struct list_head *entry)
+{
+ return entry->next != NULL;
+}
+
+/**
+ * Check if list is empty.
+ *
+ * @param head Head of list.
+ * @return \ref true if list is empty, \ref false otherwise.
+ */
+static inline bool list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * @param head Head of list.
+ * @return First entry in list or NULL if empty.
+ */
+static inline struct list_head *list_front(struct list_head *head)
+{
+ if (list_empty(head))
+ return NULL;
+
+ return head->next;
+}
+
+/**
+ * @param head Head of list.
+ * @return First entry in list or NULL if empty.
+ */
+static inline struct list_head *list_back(struct list_head *head)
+{
+ if (list_empty(head))
+ return NULL;
+
+ return head->prev;
+}
+
+#endif /* KMI_LIST_H */
diff --git a/include/kmi/notify.h b/include/kmi/notify.h
index 0a17654..554758c 100644
--- a/include/kmi/notify.h
+++ b/include/kmi/notify.h
@@ -1,9 +1,35 @@
+/* SPDX-License-Identifier: copyleft-next-0.3.1 */
+/* Copyright 2024, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
+
#ifndef KMI_NOTIFY_H
#define KMI_NOTIFY_H
+/**
+ * @file notify.h
+ *
+ * Notification handling stuff.
+ */
+
+#include <kmi/syscalls.h>
#include <kmi/tcb.h>
-/* Implemented in ipc.c */
-void notify(struct tcb *t);
+/**
+ * Try to send a notification to a thread. Sets up a new rpc call, should be
+ * returned from with \ref ipc_ghost() to restore register state.
+ *
+ * If the thread is idle, immediately swap to it.
+ *
+ * If the thread is doing an rpc, queue the notification to be executed when the
+ * thread returns to the parent process.
+ *
+ * If the thread is currently executing on another core, send an ipi to
+ * the core to switch threads.
+ *
+ * Implemented in uapi/ipc.c.
+ *
+ * @param t Thread to send notification to.
+ * @param flag Which type of notification to send.
+ */
+void notify(struct tcb *t, enum notify_flag flag);
#endif /* KMI_NOTIFY_H */
diff --git a/include/kmi/queue.h b/include/kmi/queue.h
new file mode 100644
index 0000000..534318a
--- /dev/null
+++ b/include/kmi/queue.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: copyleft-next-0.3.1 */
+/* Copyright 2024, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
+
+#ifndef KMI_QUEUE_H
+#define KMI_QUEUE_H
+
+/**
+ * @file queue.h
+ *
+ * Wrapper around lists to make things a bit more readable.
+ */
+
+#include <kmi/list.h>
+
+/** A queue. */
+struct queue_head {
+ /** A queue is pretty much just a list. */
+ struct list_head l;
+};
+
+/** Initialize static queue. */
+#define INIT_QUEUE(name) {.l = INIT_LIST((name).l)}
+
+/**
+ * Push an entry to the back of the queue.
+ * If \p new already exists in some queue, nothing is done.
+ *
+ * @param new Entry to push.
+ * @param head Head of queue.
+ */
+static inline void queue_push(struct queue_head *new, struct queue_head *head)
+{
+ /* if new is already in list, don't change its relative positioning */
+ if (in_list(&new->l))
+ return;
+
+ list_add_tail(&new->l, &head->l);
+}
+
+/**
+ * Pop an entry from the front of the queue.
+ *
+ * @param head Head of queue.
+ * @return Front of queue or NULL if empty.
+ */
+static inline struct queue_head *queue_pop(struct queue_head *head)
+{
+ struct list_head *e = list_front(&head->l);
+ if (!e)
+ return NULL;
+
+ list_del(e);
+ return container_of(e, struct queue_head, l);
+}
+
+#endif /* QUEUE_H */
diff --git a/include/kmi/syscalls.h b/include/kmi/syscalls.h
index 23fc799..92ea264 100644
--- a/include/kmi/syscalls.h
+++ b/include/kmi/syscalls.h
@@ -159,10 +159,26 @@ enum sys_code {
SYS_NUM,
};
+/** Operation codes for reverse requests, i.e. where the kernel wants userspace
+ * to do something. */
enum sys_user {
+ /** Thread has received one or several notifications, please handle
+ * them. */
SYS_USER_NOTIFY,
};
+/** Which notifications have arrived. */
+enum notify_flag {
+ /** A signal (\ref ipc_notify()). */
+ NOTIFY_SIGNAL = (1 << 0),
+
+ /** A timer has expired. */
+ NOTIFY_TIMER = (1 << 1),
+
+ /** An interrupt. */
+ NOTIFY_IRQ = (1 << 2),
+};
+
/* function declarations should be somewhere else, this file could be used in
* userspace applications as well */
diff --git a/include/kmi/tcb.h b/include/kmi/tcb.h
index 999b6a8..1209a79 100644
--- a/include/kmi/tcb.h
+++ b/include/kmi/tcb.h
@@ -13,9 +13,11 @@
struct tcb;
#include <kmi/mem_regions.h>
+#include <kmi/syscalls.h>
#include <kmi/atomic.h>
-#include <kmi/caps.h>
+#include <kmi/queue.h>
#include <kmi/types.h>
+#include <kmi/caps.h>
#include <arch/tcb.h> /* arch-specific data */
/**
@@ -135,9 +137,6 @@ struct tcb {
*/
struct tcb_ctx server;
- /** Notifcation state of thread. */
- enum tcb_notify notify_state;
-
/**
* Effective process ID.
*
@@ -182,11 +181,17 @@ struct tcb {
* this id, might change in the future. */
id_t notify_id;
+ /** Notifcation state of thread. */
+ enum tcb_notify notify_state;
+
+ /** Currently waiting notifications. */
+ enum notify_flag notify_flags;
+
/** Capabilities of thread. */
capflags_t caps;
- /** Whether thread has gotten an IPI */
- bool ipi;
+ /** Queue that connects together threads waiting for an ipi */
+ struct queue_head ipi_queue;
/** Whether thread is dead. If thread is process, then corresponds to
* whole process. */
diff --git a/include/kmi/timer.h b/include/kmi/timer.h
index 7c16f36..4493699 100644
--- a/include/kmi/timer.h
+++ b/include/kmi/timer.h
@@ -133,6 +133,7 @@ static inline ticks_t secs_to_ticks(tunit_t secs)
return msecs_to_ticks(secs * 1000);
}
+/** Callback for timer IRQs. */
void handle_timer();
#endif /* KMI_TIMER_H */
diff --git a/include/kmi/uapi.h b/include/kmi/uapi.h
index 1631500..00fb45a 100644
--- a/include/kmi/uapi.h
+++ b/include/kmi/uapi.h
@@ -561,8 +561,14 @@ SYSCALL_DECLARE4(ipc_resp, d0, d1, d2, d3);
* returning from interrupt handlers, like notifications, timers or external
* devices.
*
- * @param r Current tcb.
+ * @param t Current tcb.
+ * @param a Unused.
+ * @param b Unused.
+ * @param c Unused.
+ * @param d Unused.
+ * @param e Unused.
*
+ * Return OK.
*/
SYSCALL_DECLARE0(ipc_ghost);
diff --git a/src/ipi.c b/src/ipi.c
index 79b363f..e9bc4d3 100644
--- a/src/ipi.c
+++ b/src/ipi.c
@@ -2,27 +2,27 @@
/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
#include <kmi/notify.h>
+#include <kmi/queue.h>
#include <kmi/ipi.h>
#include <arch/proc.h>
#include <arch/cpu.h>
+/** List for keeping track of which threads have an ipi queued. */
+static struct queue_head fifo = INIT_QUEUE(fifo);
+
/**
* @file ipi.c
*
* IPI function implementations.
*/
-bool clear_ipi(struct tcb *t)
-{
- bool r = t->ipi;
- t->ipi = false;
- return r;
-}
-
void send_ipi(struct tcb *t)
{
- t->ipi = true;
+ /** @todo this should probably have a spinlock guard */
+ /** @todo killing a thread should make sure it gets removed from ipi
+ * queue */
+ queue_push(&fifo, &t->ipi_queue);
cpu_send_ipi(t->cpu_id);
}
@@ -31,9 +31,11 @@ void handle_ipi()
struct tcb *t = cur_tcb();
adjust_ipi(t);
- /** @todo how do we get the target thread? What if multiple threads send
- * ipis at the same time? */
- struct tcb *r = NULL;
- notify(r);
+ struct queue_head *q = queue_pop(&fifo);
+ if (!q)
+ return;
+
+ struct tcb *r = container_of(q, struct tcb, ipi_queue);
+ notify(r, 0);
/* notify didn't take for whatever reason so return whence we came from */
}
diff --git a/src/irq.c b/src/irq.c
index 8d01fbd..73ae88d 100644
--- a/src/irq.c
+++ b/src/irq.c
@@ -67,12 +67,12 @@ void handle_irq()
struct tcb *t = get_tcb(tid);
if (!t) {
error("tcb %llu dead at irq %llu\n",
- (unsigned long long)tid,
- (unsigned long long)id);
+ (unsigned long long)tid,
+ (unsigned long long)id);
return;
}
disable_irqs();
- notify(t);
+ notify(t, NOTIFY_IRQ);
error("misc error when trying to notify irq");
}
diff --git a/src/proc.c b/src/proc.c
index 7bfab0a..45d1e5e 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -46,7 +46,8 @@ stat_t init_proc(void *fdt)
use_tcb(t);
/* init process has all capabilities */
- set_caps(t->caps, 0, CAP_CAPS | CAP_PROC | CAP_CALL | CAP_POWER);
+ set_caps(t->caps, 0,
+ CAP_CAPS | CAP_PROC | CAP_SIGNAL | CAP_POWER | CAP_NOTIFY);
t->notify_id = t->tid;
diff --git a/src/timer.c b/src/timer.c
index 5af03e8..2607584 100644
--- a/src/timer.c
+++ b/src/timer.c
@@ -204,5 +204,5 @@ void handle_timer()
if (!r)
return;
- notify(r);
+ notify(r, NOTIFY_TIMER);
}
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));
}