summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/syscall.h10
-rw-r--r--arch/x86_64/proc/switch.s45
-rw-r--r--doc/syscall.md6
-rw-r--r--proc/process.c67
-rw-r--r--proc/process.h11
-rw-r--r--proc/scheduler.c117
-rw-r--r--proc/scheduler.h5
-rw-r--r--sys/objdir.c7
-rw-r--r--sys/objdir.h1
-rw-r--r--sys/syscall_pcb.c74
10 files changed, 323 insertions, 20 deletions
diff --git a/api/syscall.h b/api/syscall.h
index 88aad81..6b10569 100644
--- a/api/syscall.h
+++ b/api/syscall.h
@@ -182,19 +182,15 @@ enum
SYSCALL_PCB_YIELD,
SYSCALL_PCB_SEND,
SYSCALL_PCB_RECV,
- SYSCALL_PCB_SET_RDIR,
- SYSCALL_PCB_SET_PDIR,
- SYSCALL_PCB_SET_READY,
+ SYSCALL_PCB_READY,
};
#define SYSCALL_PERM_PCB_YIELD (JOVE_PERM_WRITE)
#define SYSCALL_PERM_PCB_SEND (JOVE_PERM_WRITE)
#define SYSCALL_PERM_PCB_RECV (JOVE_PERM_WRITE)
-#define SYSCALL_PERM_PCB_SET_RDIR (JOVE_PERM_WRITE | JOVE_PERM_MUTATE)
-#define SYSCALL_PERM_PCB_SET_PDIR (JOVE_PERM_WRITE | JOVE_PERM_MUTATE)
-#define SYSCALL_PERM_PCB_SET_READY (JOVE_PERM_WRITE | JOVE_PERM_MUTATE)
+#define SYSCALL_PERM_PCB_READY (JOVE_PERM_WRITE | JOVE_PERM_MUTATE)
-#define SYSCALL_PERM_PCB_SEND_TGT (JOVE_PERM_MUTATE)
+#define SYSCALL_PERM_PCB_SEND_OBJDIR (JOVE_PERM_WRITE)
#define SYSCALL_PERM_PCB_RECV_DST (JOVE_PERM_MUTATE)
struct syscall_pcb_yield
diff --git a/arch/x86_64/proc/switch.s b/arch/x86_64/proc/switch.s
index d571c66..16b2198 100644
--- a/arch/x86_64/proc/switch.s
+++ b/arch/x86_64/proc/switch.s
@@ -7,3 +7,48 @@ scheduler_switch:
retq
.size scheduler_switch, . - scheduler_switch
+
+.global scheduler_savestate
+.type scheduler_savestate @function
+
+scheduler_savestate:
+ pushq %r15
+ pushq %r14
+ pushq %r13
+ pushq %r12
+ pushq %r11
+ pushq %r10
+ pushq %r9
+ pushq %r8
+ pushq %rbp
+ pushq %rdi
+ pushq %rsi
+ pushq %rdx
+ pushq %rcx
+ pushq %rbx
+ pushq %rax
+
+.size scheduler_savestate, . - scheduler_savestate
+
+.global scheduler_restorestate
+.type scheduler_restorestate @function
+
+scheduler_restorestate:
+ popq %rax
+ popq %rbx
+ popq %rcx
+ popq %rdx
+ popq %rsi
+ popq %rdi
+ popq %rbp
+ popq %r8
+ popq %r9
+ popq %r10
+ popq %r11
+ popq %r12
+ popq %r13
+ popq %r14
+ popq %r15
+
+.size scheduler_restorestate, . - scheduler_restorestate
+
diff --git a/doc/syscall.md b/doc/syscall.md
index 1e03425..53626c8 100644
--- a/doc/syscall.md
+++ b/doc/syscall.md
@@ -305,9 +305,9 @@ If blocking timeout is zero, then the process will wait for the destination proc
If the blocking timeout is non-zero, then the process will wait for the destination process to recieve the object for the specified number of scheduler ticks.
-| Data | Destination Process | Blocking Timeout | Send Path Length | Send Path |
-|--------------|---------------------|-------------------|------------------|----------------------|
-| Size (bytes) | sizeof(pid_t) | sizeof(uintmax_t) | 2 | 2 * send_path_length |
+| Data | Destination Process | Blocking Timeout | Send Path Length | Send Path |
+|--------------|---------------------|-------------------|------------------------|-------------------------------------------|
+| Size (bytes) | sizeof(pid_t) | sizeof(uintmax_t) | sizeof(objpath_part_t) | sizeof(objpath_part_t) * send_path_length |
While the object is being sent, the kernel will lock the object and copy a pointer to it into the send mailbox.
diff --git a/proc/process.c b/proc/process.c
index 137e35c..62c0269 100644
--- a/proc/process.c
+++ b/proc/process.c
@@ -1,6 +1,8 @@
#include "process.h"
+#include "api/error.h"
#include "api/process.h"
#include "hal/smp.h"
+#include "proc/scheduler.h"
#include "sys/objdir.h"
#include <string.h>
@@ -26,6 +28,71 @@ pcb_new(jove_owned_obj_t *pcb_obj, size_t size, jove_owned_obj_t *rootdir_obj)
return _pcb_new_arch((pcb_t*)pcb_header);
}
+int
+pcb_send(pcb_t *pcb, pid_t target, jove_owned_obj_t *subject, size_t timeout)
+{
+ struct pcb_header *pcb_header = (struct pcb_header*)pcb;
+ struct pcb_header *target_pcb = (struct pcb_header*)rbtree_search(pcb_tree, target);
+ if(target_pcb == 0) {
+ return -EBADPROC;
+ }
+
+ if(target_pcb->recv.obj == NULL &&
+ (target_pcb->recv.pid == pcb_getpid(pcb) || target_pcb->recv.pid == 0))
+ {
+ target_pcb->recv.obj = subject;
+ target_pcb->recv.pid = pcb_getpid(pcb);
+ return scheduler_yield(target);
+ }
+
+ if(timeout == 0) {
+ return -EFULL;
+ }
+
+ pcb_header->send.obj = subject;
+ pcb_header->send.pid = target;
+
+ scheduler_block_for_mail(pcb_header, timeout, target);
+
+ if(pcb_header->send.obj) return -ETIMEOUT;
+ return 0;
+}
+
+int
+pcb_recv(pcb_t *pcb, pid_t target, jove_owned_obj_t *dest_obj, size_t timeout)
+{
+ struct pcb_header *pcb_header = (struct pcb_header*)pcb;
+ struct pcb_header *target_pcb = (struct pcb_header*)rbtree_search(pcb_tree, target);
+ if(target_pcb == 0) {
+ return -EBADPROC;
+ }
+
+ if(pcb_header->recv.obj) {
+ goto recv_success;
+ return 0;
+ }
+
+ if(timeout == 0) {
+ return -EEMPTY;
+ }
+
+ pcb_header->recv.pid = target;
+ scheduler_block_for_mail(pcb_header, timeout, target);
+
+ if(!pcb_header->recv.obj) {
+ pcb_header->recv.pid = -1;
+ return -ETIMEOUT;
+ }
+
+recv_success:
+ objdir_swapobj(pcb_header->recv.obj, dest_obj);
+ obj_release(pcb_header->recv.obj);
+ pcb_header->recv.obj = 0;
+ pcb_header->recv.pid = -1;
+
+ return 0;
+}
+
pcb_t *pcb_frompid(pid_t pid)
{
return (pcb_t*)rbtree_search((struct rbtree_node*)pcb_tree, pid);
diff --git a/proc/process.h b/proc/process.h
index 950f63e..21dbf8d 100644
--- a/proc/process.h
+++ b/proc/process.h
@@ -7,14 +7,14 @@
struct pcb_mailbox
{
- jove_obj_t *obj;
+ jove_owned_obj_t *obj;
pid_t pid;
};
struct pcb_header
{
struct rbtree_node tree_node;
- struct pcb_header *sched_next;
+ struct pcb_header *sched_prev, *sched_next;
size_t size;
pid_t pid;
@@ -22,6 +22,7 @@ struct pcb_header
enum pcb_state state;
intmax_t state_code;
+ size_t block_until;
struct pcb_mailbox send, recv;
@@ -42,6 +43,9 @@ jove_obj_t *pcb_getrootdir(pcb_t *pcb);
jove_owned_obj_t *pcb_getpagedir(jove_owned_obj_t *rootdir_obj);
jove_owned_obj_t *pcb_getself(jove_owned_obj_t *rootdir_obj);
+struct pcb_mailbox *pcb_getmail_send(pcb_t *pcb);
+struct pcb_mailbox *pcb_getmail_recv(pcb_t *pcb);
+
enum pcb_state pcb_getstate(pcb_t *pcb);
intmax_t pcb_getstatecode(pcb_t *pcb);
@@ -50,6 +54,9 @@ void **pcb_getksp_ref(pcb_t *pcb);
pcb_t *pcb_frompid(pid_t pid);
+int pcb_send(pcb_t *pcb, pid_t target, jove_owned_obj_t *subject, size_t timeout);
+int pcb_recv(pcb_t *pcb, pid_t target, jove_owned_obj_t *dest_obj, size_t timeout);
+
extern struct rbtree_node *pcb_tree;
#endif
diff --git a/proc/scheduler.c b/proc/scheduler.c
index 875bcc4..29f45b5 100644
--- a/proc/scheduler.c
+++ b/proc/scheduler.c
@@ -5,24 +5,27 @@
#include "proc/init.h"
#include "proc/process.h"
+//TODO: Better scheduling algorithm than round robin.
+static struct pcb_header *s_running_list;
+
static struct pcb_header *s_ready_queue;
+static struct pcb_header *s_mail_queue;
+
+static uintmax_t s_this_tick;
-static inline int
-s_scheduler_yield_pcb(processor_t *proc, pcb_t *current_pcb, pcb_t *target_pcb)
+static inline void
+s_scheduler_swap_pcb(processor_t *proc, pcb_t *current_pcb, pcb_t *target_pcb)
{
jove_owned_obj_t *target_rootdir = obj_take(pcb_getrootdir(target_pcb));
jove_owned_obj_t *target_pcb_obj = pcb_getself(target_rootdir);
+ obj_release(target_rootdir);
processor_setpcb(proc, target_pcb_obj);
- obj_release(target_rootdir);
_scheduler_switch_pcb_arch(target_pcb);
scheduler_switch(pcb_getksp_ref(current_pcb), pcb_getksp(target_pcb));
-
- return 0;
}
-
int scheduler_yield(pid_t target_pid)
{
processor_t *proc = processor_current();
@@ -35,19 +38,117 @@ int scheduler_yield(pid_t target_pid)
if(target_pcb == 0) {
return -EBADPROC;
}
+
+ scheduler_savestate();
+ s_scheduler_swap_pcb(proc, current_pcb, target_pcb);
+ scheduler_restorestate();
+ return 0;
+}
- return s_scheduler_yield_pcb(proc, current_pcb, target_pcb);
+void
+s_enqueue_pcb(struct pcb_header *pcb, struct pcb_header *list)
+{
+ if(pcb->sched_prev) pcb->sched_prev->sched_next = pcb->sched_next;
+ if(pcb->sched_prev) pcb->sched_next->sched_prev = pcb->sched_prev;
+
+ list->sched_prev = pcb;
+ pcb->sched_next = list;
+ pcb->sched_prev = NULL;
+}
+
+void
+scheduler_block_for_mail(struct pcb_header *pcb, uintmax_t timeout, pid_t yield_target)
+{
+ pcb->state = PCB_STATE_BLOCKED_MAIL;
+
+ s_enqueue_pcb(pcb, s_mail_queue);
+ s_mail_queue = pcb;
+
+ pcb->block_until = s_this_tick + timeout;
+ scheduler_yield(yield_target);
+}
+
+void
+s_schedule_ready_queue(void)
+{
+ for(struct pcb_header *pcb = s_ready_queue; pcb; pcb = s_ready_queue) {
+ s_ready_queue = pcb->sched_next;
+
+ //Add this process to the ready list after the last ready process.
+ pcb->state = PCB_STATE_READY;
+ s_enqueue_pcb(pcb, s_running_list);
+ s_running_list = pcb;
+ }
+}
+
+void
+s_schedule_mail_queue(void)
+{
+ struct pcb_header *pcb = s_mail_queue;
+ while(pcb) {
+ struct pcb_header *next_pcb = pcb->sched_next;
+
+ if(pcb->send.obj) {
+ struct pcb_header *send_target = (struct pcb_header*)pcb_frompid(pcb->send.pid);
+ if(send_target->recv.pid == 0 || send_target->recv.pid == pcb->pid) {
+ objdir_swapobj(send_target->recv.obj, pcb->send.obj);
+ send_target->recv.pid = pcb->pid;
+
+ s_enqueue_pcb(pcb, s_running_list);
+ s_running_list = pcb;
+ goto loop_end;
+ }
+ }
+ if(pcb->recv.obj) {
+ s_enqueue_pcb(pcb, s_running_list);
+ s_running_list = pcb;
+ goto loop_end;
+ }
+
+ if(s_this_tick >= pcb->block_until) {
+ s_enqueue_pcb(pcb, s_running_list);
+ s_running_list = pcb;
+ }
+loop_end:
+ pcb = next_pcb;
+ }
+}
+
+void
+scheduler_tick()
+{
+ size_t last_tick = s_this_tick++;
+
+ s_schedule_ready_queue();
+ s_schedule_mail_queue();
+
+ processor_t *proc = processor_current();
+ struct pcb_header *current_pcb = obj_deref(processor_getpcb(proc));
+ struct pcb_header *next_pcb = s_running_list;
+ struct pcb_header *tail = next_pcb;
+
+ while(tail->sched_next) {
+ tail = tail->sched_next;
+ if(tail == next_pcb) break;
+ }
+
+ tail->sched_next = next_pcb;
+ next_pcb->sched_prev = tail;
+ s_running_list = next_pcb->sched_next;
+
+ s_scheduler_swap_pcb(proc, (pcb_t*)current_pcb, (pcb_t*)next_pcb);
}
void scheduler_init(jove_owned_obj_t *pcb_obj, void *process_main)
{
struct pcb_header *pcb_header = obj_deref(pcb_obj);
pcb_header->state = PCB_STATE_RUNNING;
- pcb_header->sched_next = pcb_header;
processor_t *proc = processor_current();
processor_setpcb(proc, pcb_obj);
+ s_running_list = pcb_header;
+
__asm__("movq %0, %%rsp; \
xorq %%rbp, %%rbp; \
jmp %1"::
diff --git a/proc/scheduler.h b/proc/scheduler.h
index 0371e86..2fc0c44 100644
--- a/proc/scheduler.h
+++ b/proc/scheduler.h
@@ -5,10 +5,15 @@
void scheduler_init(jove_owned_obj_t *pcb_obj, void *process_main);
+void scheduler_savestate(void);
+void scheduler_restorestate(void);
+
void scheduler_switch(void *this_sp, void *other_sp);
void _scheduler_switch_pcb_arch(pcb_t *target);
int scheduler_yield(pid_t target);
+void scheduler_tick(void);
+void scheduler_block_for_mail(struct pcb_header *pcb, size_t timeout, pid_t yield_target);
#endif
diff --git a/sys/objdir.c b/sys/objdir.c
index c448e85..eab935f 100644
--- a/sys/objdir.c
+++ b/sys/objdir.c
@@ -112,3 +112,10 @@ objdir_trygetobj(
}
}
+void
+objdir_swapobj(jove_owned_obj_t *a, jove_owned_obj_t *b)
+{
+ jove_owned_obj_t buffer_obj = *a;
+ *a = *b;
+ *b = buffer_obj;
+}
diff --git a/sys/objdir.h b/sys/objdir.h
index 54e9618..e2ae7ef 100644
--- a/sys/objdir.h
+++ b/sys/objdir.h
@@ -23,5 +23,6 @@ jove_owned_obj_t *objdir_getobj(jove_owned_obj_t *objdir_obj, const uint16_t *pa
/*Same as objdir_getobj but does not block if child objects are locked.*/
jove_owned_obj_t *objdir_trygetobj(jove_owned_obj_t *objdir_obj, const uint16_t *path, size_t path_len);
+void objdir_swapobj(jove_owned_obj_t *a, jove_owned_obj_t *b);
#endif
diff --git a/sys/syscall_pcb.c b/sys/syscall_pcb.c
index a37e4bb..4fe557b 100644
--- a/sys/syscall_pcb.c
+++ b/sys/syscall_pcb.c
@@ -1,4 +1,6 @@
#include "api/error.h"
+#include "api/obj.h"
+#include "api/syscall.h"
#include "jove.h"
#include "lib/printf.h"
#include "proc/process.h"
@@ -19,6 +21,66 @@ s_syscall_pcb_yield(jove_owned_obj_t *pcb_obj,
}
int
+s_syscall_pcb_send(jove_owned_obj_t *pcb_obj,
+ lockstack_t *lockstack,
+ jove_owned_obj_t *objdir_obj,
+ size_t bytes_left,
+ void *payload_data)
+{
+ size_t bytes_needed = sizeof(struct syscall_pcb_send);
+ CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for payload");
+
+ struct syscall_pcb_send *payload = payload_data;
+ bytes_needed += payload->send_path_length;
+ CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for subject path");
+
+ jove_perm_obj_t subject_objdir = link_try_traverse_objdir(
+ objdir_obj, lockstack, payload->send_path_length - 1, payload->send_path);
+ if(errno) {
+ klogf("Unable to get subject %P objdir : %s\n",
+ payload->send_path, payload->send_path_length, errstr(errno));
+ return -errno;
+ }
+ CHECK_OBJPERM(subject_objdir, SYSCALL_PERM_PCB_SEND_OBJDIR, "Subject objdir does not permit send");
+
+ jove_owned_obj_t *subject = objdir_trygetlocal(subject_objdir.obj,
+ payload->send_path[payload->send_path_length-1]);
+ if(subject == 0) {
+ klogf("Unable to get subject %P object : %s\n",
+ payload->send_path, payload->send_path_length, errstr(errno));
+ return -errno;
+ }
+
+ return pcb_send(obj_deref(pcb_obj), payload->send_target, subject, payload->send_timeout);
+}
+
+int
+s_syscall_pcb_recv(jove_owned_obj_t *pcb_obj,
+ lockstack_t *lockstack,
+ jove_owned_obj_t *objdir_obj,
+ size_t bytes_left,
+ void *payload_data)
+{
+ size_t bytes_needed = sizeof(struct syscall_pcb_recv);
+ CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for payload");
+
+ struct syscall_pcb_recv *payload = payload_data;
+ bytes_needed += payload->recv_path_length * sizeof(objpath_part_t);
+ CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for destination path");
+
+ jove_perm_obj_t destination = link_try_traverse_objdir(
+ objdir_obj, lockstack, payload->recv_path_length, payload->recv_path);
+ if(errno) {
+ klogf("Unable to get destination %P : %s\n",
+ payload->recv_path, payload->recv_path_length, errstr(errno));
+ return -errno;
+ }
+ CHECK_OBJPERM(destination, SYSCALL_PERM_PCB_RECV_DST, "Destination object does not permit recv.");
+
+ return pcb_recv(obj_deref(pcb_obj), payload->recv_target, destination.obj, payload->recv_timeout);
+}
+
+int
_syscall_handle_pcb(syscall_context_t *ctx)
{
switch(ctx->header->callid) {
@@ -27,6 +89,18 @@ _syscall_handle_pcb(syscall_context_t *ctx)
return s_syscall_pcb_yield(ctx->target.obj, ctx->bytes_left, ctx->payload);
return -EPERM;
+ case SYSCALL_PCB_SEND:
+ if(ctx->target.permissions & SYSCALL_PERM_PCB_SEND)
+ return s_syscall_pcb_send(
+ ctx->target.obj, &ctx->lockstack, ctx->objdir, ctx->bytes_left, ctx->payload);
+ return -EPERM;
+
+ case SYSCALL_PCB_RECV:
+ if(ctx->target.permissions & SYSCALL_PERM_PCB_RECV)
+ return s_syscall_pcb_recv(
+ ctx->target.obj, &ctx->lockstack, ctx->objdir, ctx->bytes_left, ctx->payload);
+ return -EPERM;
+
default:
return -EBADCALL;
}