Syscalls take event_queues for completion (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 22 Feb 2011 00:09:31 +0000 (16:09 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:58 +0000 (17:35 -0700)
If the syscall has an ev_q, the kernel will try and signal/send an
event.  tests/syscall.c has examples of how userspace can do it (rawly),
for both a polling ev_q as well as for an IPI driven one (which requires
_M mode).

Use SYS_block whenever you want to test a syscall that will block, but
don't use more than one of them at a time or in conjunction with any
blocking disk reads (for now).

Rebuild your cross compiler.

kern/include/ros/bits/syscall.h
kern/include/ros/event.h
kern/include/ros/syscall.h
kern/include/syscall.h
kern/src/kthread.c
kern/src/process.c
kern/src/syscall.c
tests/mhello.c
tests/syscall.c [new file with mode: 0644]

index 95241db..fb88a26 100644 (file)
@@ -5,7 +5,7 @@
  * useless ones is okay, but if we change a number, we'll need to rebuild
  * userspace (which is why we have holes). */
 #define SYS_null                                        1
  * useless ones is okay, but if we change a number, we'll need to rebuild
  * userspace (which is why we have holes). */
 #define SYS_null                                        1
-/* buster renumbered */
+#define SYS_block                                       2
 #define SYS_cache_invalidate            3
 #define SYS_reboot                                      4
 #define SYS_cputs                                       5
 #define SYS_cache_invalidate            3
 #define SYS_reboot                                      4
 #define SYS_cputs                                       5
index 56ef5ce..06789c1 100644 (file)
@@ -29,7 +29,8 @@
 #define EV_ALARM                                7
 #define EV_EVENT                                8
 #define EV_FREE_APPLE_PIE               9
 #define EV_ALARM                                7
 #define EV_EVENT                                8
 #define EV_FREE_APPLE_PIE               9
-#define NR_EVENT_TYPES                 10 /* keep me last */
+#define EV_SYSCALL                             10
+#define NR_EVENT_TYPES                 11 /* keep me last */
 
 /* Will probably have dynamic notifications later */
 #define MAX_NR_DYN_EVENT               25
 
 /* Will probably have dynamic notifications later */
 #define MAX_NR_DYN_EVENT               25
index abb4d06..7858d74 100644 (file)
@@ -4,6 +4,7 @@
 #include <arch/arch.h>
 #include <ros/bits/syscall.h>
 #include <ros/arch/syscall.h>
 #include <arch/arch.h>
 #include <ros/bits/syscall.h>
 #include <ros/arch/syscall.h>
+#include <ros/event.h>
 
 /* Flags for an individual syscall */
 #define SC_DONE                                        0x0001
 
 /* Flags for an individual syscall */
 #define SC_DONE                                        0x0001
@@ -13,6 +14,8 @@ struct syscall {
        long                                            retval;
        int                                                     err;                    /* errno */
        int                                                     flags;
        long                                            retval;
        int                                                     err;                    /* errno */
        int                                                     flags;
+       struct event_queue                      *ev_q;
+       void                                            *u_data;
        long                                            arg0;
        long                                            arg1;
        long                                            arg2;
        long                                            arg0;
        long                                            arg1;
        long                                            arg2;
@@ -33,6 +36,7 @@ static inline long __ros_syscall(unsigned int _num, long _a0, long _a1, long _a2
        int num_started;
        struct syscall sysc = {0};
        sysc.num = _num;
        int num_started;
        struct syscall sysc = {0};
        sysc.num = _num;
+       sysc.ev_q = 0;
        sysc.arg0 = _a0;
        sysc.arg1 = _a1;
        sysc.arg2 = _a2;
        sysc.arg0 = _a0;
        sysc.arg1 = _a1;
        sysc.arg2 = _a2;
index b07f4e0..e4e8f81 100644 (file)
@@ -44,6 +44,7 @@ void prep_syscalls(struct proc *p, struct syscall *sysc, unsigned int nr_calls);
 intreg_t syscall(struct proc *p, uintreg_t sc_num, uintreg_t a0, uintreg_t a1,
                  uintreg_t a2, uintreg_t a3, uintreg_t a4, uintreg_t a5);
 void set_errno(int errno);
 intreg_t syscall(struct proc *p, uintreg_t sc_num, uintreg_t a0, uintreg_t a1,
                  uintreg_t a2, uintreg_t a3, uintreg_t a4, uintreg_t a5);
 void set_errno(int errno);
+void signal_syscall(struct syscall *sysc, struct proc *p);
 
 /* Tracing functions */
 void systrace_start(bool silent);
 
 /* Tracing functions */
 void systrace_start(bool silent);
index ae272e7..aa27a6d 100644 (file)
@@ -102,7 +102,7 @@ unwind_sleep_prep:
        /* We get here if we should not sleep on sem (the signal beat the sleep).
         * Note we are not optimizing for cases where the signal won. */
        spin_unlock(&sem->lock);
        /* We get here if we should not sleep on sem (the signal beat the sleep).
         * Note we are not optimizing for cases where the signal won. */
        spin_unlock(&sem->lock);
-       printd("Didn't sleep, unwinding...\n");
+       printd("[kernel] Didn't sleep, unwinding...\n");
        /* Restore the core's current and default stacktop */
        current = kthread->proc;                        /* arguably unnecessary */
        if (kthread->proc)
        /* Restore the core's current and default stacktop */
        current = kthread->proc;                        /* arguably unnecessary */
        if (kthread->proc)
@@ -114,7 +114,7 @@ unwind_sleep_prep:
        /* save the "freshly alloc'd" stack/page, not the one we came in on */
        kthread->stacktop = new_stacktop;
 block_return_path:
        /* save the "freshly alloc'd" stack/page, not the one we came in on */
        kthread->stacktop = new_stacktop;
 block_return_path:
-       printd("Returning from being 'blocked'! at %llu\n", read_tsc());
+       printd("[kernel] Returning from being 'blocked'! at %llu\n", read_tsc());
 block_return_path_np:
        enable_irqsave(&irq_state);
        return;
 block_return_path_np:
        enable_irqsave(&irq_state);
        return;
index 8319da0..fb10c2c 100644 (file)
@@ -1359,6 +1359,8 @@ void __startcore(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
                        vcpd->notif_tf = vcpd->preempt_tf; // could memset
                        proc_init_trapframe(&local_tf, vcoreid, p_to_run->env_entry,
                                            vcpd->transition_stack);
                        vcpd->notif_tf = vcpd->preempt_tf; // could memset
                        proc_init_trapframe(&local_tf, vcoreid, p_to_run->env_entry,
                                            vcpd->transition_stack);
+                       if (!vcpd->transition_stack)
+                               warn("No transition stack!");
                        vcpd->notif_enabled = FALSE;
                        vcpd->notif_pending = FALSE;
                } else {
                        vcpd->notif_enabled = FALSE;
                        vcpd->notif_pending = FALSE;
                } else {
index 17e3684..f01b461 100644 (file)
@@ -93,6 +93,47 @@ static int sys_null(void)
        return 0;
 }
 
        return 0;
 }
 
+/* Diagnostic function: blocks the kthread/syscall, to help userspace test its
+ * async I/O handling.  Don't mix this with things that mess with the interrupt
+ * handler, like other sys_blocks or the current blockdev crap. */
+static int sys_block(void)
+{
+       struct semaphore local_sem, *sem = &local_sem;
+       init_sem(sem, 0);
+#ifdef __i386__        /* Sparc can't register interrupt handlers yet */
+       /* Faking an interrupt.  The handler runs in interrupt context btw */
+       void x86_unblock_handler(struct trapframe *tf, void *data)
+       {
+               /* Turn off the interrupt, Re-register the old dumb handler */
+               set_core_timer(0);
+               register_interrupt_handler(interrupt_handlers,
+                                          LAPIC_TIMER_DEFAULT_VECTOR, timer_interrupt,
+                                          NULL);
+               struct semaphore *sem = (struct semaphore*)data;
+               struct kthread *sleeper = __up_sem(sem);
+               if (!sleeper) {
+                       warn("No one sleeping!");
+                       return;
+               }
+               kthread_runnable(sleeper);
+               assert(TAILQ_EMPTY(&sem->waiters));
+       }
+
+       register_interrupt_handler(interrupt_handlers, LAPIC_TIMER_DEFAULT_VECTOR,
+                                  x86_unblock_handler, sem);
+       /* This fakes a 100ms delay.  Though it might be less, esp in _M mode.  TODO
+        * KVM-timing. */
+       set_core_timer(100000); /* in microseconds */
+       printk("[kernel] sys_block(), sleeping at %llu\n", read_tsc());
+       sleep_on(sem);
+       printk("[kernel] sys_block(), waking up at %llu\n", read_tsc());
+       return 0;
+#else /* sparc */
+       set_errno(ENOSYS);
+       return -1;
+#endif
+}
+
 // Writes 'val' to 'num_writes' entries of the well-known array in the kernel
 // address space.  It's just #defined to be some random 4MB chunk (which ought
 // to be boot_alloced or something).  Meant to grab exclusive access to cache
 // Writes 'val' to 'num_writes' entries of the well-known array in the kernel
 // address space.  It's just #defined to be some random 4MB chunk (which ought
 // to be boot_alloced or something).  Meant to grab exclusive access to cache
@@ -1261,6 +1302,7 @@ intreg_t sys_setgid(struct proc *p, gid_t gid)
 
 const static struct sys_table_entry syscall_table[] = {
        [SYS_null] = {(syscall_t)sys_null, "null"},
 
 const static struct sys_table_entry syscall_table[] = {
        [SYS_null] = {(syscall_t)sys_null, "null"},
+       [SYS_block] = {(syscall_t)sys_block, "block"},
        [SYS_cache_buster] = {(syscall_t)sys_cache_buster, "buster"},
        [SYS_cache_invalidate] = {(syscall_t)sys_cache_invalidate, "wbinv"},
        [SYS_reboot] = {(syscall_t)reboot, "reboot!"},
        [SYS_cache_buster] = {(syscall_t)sys_cache_buster, "buster"},
        [SYS_cache_invalidate] = {(syscall_t)sys_cache_invalidate, "wbinv"},
        [SYS_reboot] = {(syscall_t)reboot, "reboot!"},
@@ -1387,6 +1429,7 @@ static void run_local_syscall(struct syscall *sysc)
        sysc->retval = syscall(pcpui->cur_proc, sysc->num, sysc->arg0, sysc->arg1,
                               sysc->arg2, sysc->arg3, sysc->arg4, sysc->arg5);
        sysc->flags |= SC_DONE;
        sysc->retval = syscall(pcpui->cur_proc, sysc->num, sysc->arg0, sysc->arg1,
                               sysc->arg2, sysc->arg3, sysc->arg4, sysc->arg5);
        sysc->flags |= SC_DONE;
+       signal_syscall(sysc, pcpui->cur_proc);
        /* Can unpin at this point */
 }
 
        /* Can unpin at this point */
 }
 
@@ -1407,6 +1450,22 @@ void prep_syscalls(struct proc *p, struct syscall *sysc, unsigned int nr_syscs)
        run_local_syscall(sysc);
 }
 
        run_local_syscall(sysc);
 }
 
+/* Call this when something happens on the syscall where userspace might want to
+ * get signaled.  Passing p, since the caller should know who the syscall
+ * belongs to (probably is current). */
+void signal_syscall(struct syscall *sysc, struct proc *p)
+{
+       struct event_queue *ev_q;
+       struct event_msg local_msg;
+       ev_q = sysc->ev_q;
+       if (ev_q) {
+               memset(&local_msg, 0, sizeof(struct event_msg));
+               local_msg.ev_type = EV_SYSCALL;
+               local_msg.ev_arg3 = sysc;
+               send_event(p, ev_q, &local_msg, 0);
+       }
+}
+
 /* Syscall tracing */
 static void __init_systrace(void)
 {
 /* Syscall tracing */
 static void __init_systrace(void)
 {
index 7e49cf4..7d59bba 100644 (file)
@@ -40,9 +40,7 @@ int main(int argc, char** argv)
        register_kevent_q(indirect_q, EV_FREE_APPLE_PIE);
 
 /* begin: stuff userspace needs to do before switching to multi-mode */
        register_kevent_q(indirect_q, EV_FREE_APPLE_PIE);
 
 /* begin: stuff userspace needs to do before switching to multi-mode */
-       if (vcore_init())
-               printf("vcore_init() failed, we're fucked!\n");
-
+       /* vcore_init() done in vcore_request() now. */
        /* Set up event reception.  For example, this will allow us to receive an
         * event and IPI for USER_IPIs on vcore 0.  Check event.c for more stuff. */
        enable_kevent(EV_USER_IPI, 0, TRUE);
        /* Set up event reception.  For example, this will allow us to receive an
         * event and IPI for USER_IPIs on vcore 0.  Check event.c for more stuff. */
        enable_kevent(EV_USER_IPI, 0, TRUE);
diff --git a/tests/syscall.c b/tests/syscall.c
new file mode 100644 (file)
index 0000000..4c88f7c
--- /dev/null
@@ -0,0 +1,151 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <parlib.h>
+#include <event.h>
+#include <vcore.h>
+#include <rassert.h>
+#include <ros/bcq.h>
+
+struct syscall sysc = {0};
+struct event_queue *ev_q;
+void *core0_tls = 0;
+
+int main(int argc, char** argv)
+{
+       int num_started, retval;
+       unsigned int ev_type;
+       printf("Trying to block\n");
+       /* Not doing anything else to it: no EVENT_IPI yet, etc. */
+       ev_q = get_big_event_q();
+       /* issue the diagnostic block syscall */
+       sysc.num = SYS_block;
+       sysc.ev_q = ev_q;
+       /* Trap */
+       num_started = __ros_arch_syscall((long)&sysc, 1);
+       if (!(sysc.flags & SC_DONE))
+               printf("Not done, looping!\n");
+       #if 0
+       /* You could poll on this */
+       while (!(sysc.flags & SC_DONE))
+               cpu_relax();
+       #endif
+       /* But let's check on events... */
+       while (!event_activity(ev_q->ev_mbox, ev_q->ev_flags))
+               cpu_relax();
+       ev_type = get_event_type(ev_q->ev_mbox);
+       if (ev_type = EV_SYSCALL) {
+               /* our syscall should be done (we ought to check the msg pointer) */
+               if (sysc.flags & SC_DONE) 
+                       printf("Syscall is done, retval: %d\n", sysc.retval);
+               else
+                       printf("BUG! Syscall wasn't done!\n");
+       } else {
+               printf("Whoa, got an unexpected event type %d!\n", ev_type);
+       }
+
+       /* Start MCP / IPI test */
+       printf("Switching to _M mode and testing an IPI-d ev_q\n");
+/* begin: stuff userspace needs to do before switching to multi-mode */
+       /* Need to save this somewhere that you can find it again when restarting
+        * core0 */
+       core0_tls = get_tls_desc(0);
+       /* Need to save our floating point state somewhere (like in the
+        * user_thread_tcb so it can be restarted too */
+       /* don't forget to enable notifs on vcore0 at some point */
+       struct preempt_data *vcpd;
+       vcpd = &__procdata.vcore_preempt_data[0];
+       vcpd->notif_enabled = TRUE;
+/* end: stuff userspace needs to do before switching to multi-mode */
+
+       retval = vcore_request(1);
+       if (retval < 0)
+               printf("No cores granted, Rut Ro Raggy!\n");
+       /* now we're back in thread 0 on vcore 0 */
+       ev_q->ev_flags = EVENT_IPI;
+       ev_q->ev_vcore = 0;
+       sysc.u_data = (void*)1; /* using this to loop on */
+       /* issue the diagnostic block syscall */
+       sysc.num = SYS_block;
+       sysc.ev_q = ev_q;
+       num_started = __ros_arch_syscall((long)&sysc, 1);
+       /* have this thread "wait" */
+       if (!(sysc.flags & SC_DONE))
+               printf("Not done, looping on a local variable!\n");
+       while (sysc.u_data)
+               cpu_relax();
+       assert((sysc.flags & SC_DONE));
+       printf("Syscall unblocked, IPI broke me out of the loop.\n");
+
+       /* done */
+       put_big_event_q(ev_q);
+       printf("Syscall test exiting\n");
+       return 0;
+}
+
+void vcore_entry(void)
+{
+       uint32_t vcoreid = vcore_id();
+       static bool first_time = TRUE;
+
+/* begin: stuff userspace needs to do to handle notifications */
+
+       struct vcore *vc = &__procinfo.vcoremap[vcoreid];
+       struct preempt_data *vcpd;
+       vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       
+       /* here is how you receive an event */
+       struct event_msg ev_msg = {0};
+       struct event_queue_big *indir_q;
+       struct syscall *my_sysc;
+       if (event_activity(&vcpd->ev_mbox, 0)) {
+               /* Ought to while loop/dequeue, processing as they come in. */
+               bcq_dequeue(&vcpd->ev_mbox.ev_msgs, &ev_msg, NR_BCQ_EVENTS);
+               if (vcpd->ev_mbox.ev_overflows)
+                       printf("Had an overflow...\n");
+               /* should do generic handling.  this is customized for the syscalls */
+               if (ev_msg.ev_type == EV_EVENT) {
+                       indir_q = ev_msg.ev_arg3;       /* convention */
+                       printf("Detected EV_EVENT, ev_q is %08p (%08p)\n", indir_q, ev_q);
+                       assert(indir_q);
+                       /* Ought to loop/dequeue, processing as they come in. */
+                       bcq_dequeue(&indir_q->ev_mbox->ev_msgs, &ev_msg, NR_BCQ_EVENTS);
+                       /* should have received a syscall off the indirect ev_q */
+                       if (ev_msg.ev_type == EV_SYSCALL) {
+                               my_sysc = ev_msg.ev_arg3;
+                               printf("Handling syscall event for sysc %08p (%08p)\n",
+                                      my_sysc, &sysc);
+                               /* signal to thread 0 that the sysc is done, just to show this
+                                * is getting done in vcore context. */
+                               my_sysc->u_data = 0;
+                       } else {
+                               printf("Got a different event, type %d\n", ev_msg.ev_type);
+                       }
+               }
+       }
+
+       /* how we tell a preemption is pending (regardless of notif/events) */
+       if (vc->preempt_pending) {
+               printf("Oh crap, vcore %d is being preempted!  Yielding\n", vcoreid);
+               sys_yield(TRUE);
+               printf("After yield on vcore %d. I wasn't being preempted.\n", vcoreid);
+       }
+               
+       /* Restart vcore0's context. */
+       if (vcoreid == 0) {
+               vcpd->notif_pending = 0;
+               /* TODO: Do one last check for notifs after clearing pending */
+               set_tls_desc(core0_tls, 0);
+               /* Load silly state (Floating point) too */
+               pop_ros_tf(&vcpd->notif_tf, vcoreid);
+               panic("should never see me!");
+       }       
+       /* unmask notifications once you can let go of the notif_tf and it is okay
+        * to clobber the transition stack.
+        * Check Documentation/processes.txt: 4.2.4.  In real code, you should be
+        * popping the tf of whatever user process you want (get off the x-stack) */
+       vcpd->notif_enabled = TRUE;
+       
+/* end: stuff userspace needs to do to handle notifications */
+       /* if you have other vcores, they'll just chill here */
+       while(1);
+}