GCC/uthread callbacks on blocking syscalls (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 8 Mar 2011 00:04:39 +0000 (16:04 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:00 +0000 (17:36 -0700)
Doesn't do much yet, just runs a 2LS handler.  The uthread stuff will
change a bit soon, and that handler will run in the bottom half of a
yield-ish path in vcore context.

Rebuild your cross compiler.

kern/include/ros/syscall.h
user/parlib/include/uthread.h
user/parlib/uthread.c
user/pthread/pthread.c

index 7858d74..6423536 100644 (file)
@@ -8,6 +8,7 @@
 
 /* Flags for an individual syscall */
 #define SC_DONE                                        0x0001
+#define SC_PROGRESS                            0x0002
 
 struct syscall {
        unsigned int                            num;
@@ -26,6 +27,19 @@ struct syscall {
 
 #ifndef ROS_KERNEL
 
+/* Attempts to block on sysc, returning when it is done or progress has been
+ * made. */
+void ros_syscall_blockon(struct syscall *sysc);
+
+/* This weak version is meant to work if there is no 2LS.  For now we just
+ * spin, but in the future we could block the whole process. */
+static inline void __ros_syscall_blockon(struct syscall *sysc)
+{
+       while (!(sysc->flags & (SC_DONE | SC_PROGRESS)))
+               cpu_relax();
+}
+weak_alias(__ros_syscall_blockon, ros_syscall_blockon);
+
 /* TODO: make variants of __ros_syscall() based on the number of args (0 - 6) */
 /* These are simple synchronous system calls, built on top of the kernel's async
  * interface.  This version makes no assumptions about errno.  You usually don't
@@ -33,7 +47,7 @@ struct syscall {
 static inline long __ros_syscall(unsigned int _num, long _a0, long _a1, long _a2,
                                  long _a3, long _a4, long _a5, int *errno_loc)
 {
-       int num_started;
+       int num_started;        /* not used yet */
        struct syscall sysc = {0};
        sysc.num = _num;
        sysc.ev_q = 0;
@@ -44,8 +58,9 @@ static inline long __ros_syscall(unsigned int _num, long _a0, long _a1, long _a2
        sysc.arg4 = _a4;
        sysc.arg5 = _a5;
        num_started = __ros_arch_syscall(&sysc, 1);
+       /* Don't proceed til we are done */
        while (!(sysc.flags & SC_DONE))
-               cpu_relax();
+               ros_syscall_blockon(&sysc);
        if (errno_loc)
                *errno_loc = sysc.err;
        return sysc.retval;
index e20e315..84eb8f2 100644 (file)
@@ -24,6 +24,7 @@ struct schedule_ops {
        void (*thread_runnable)(struct uthread *);
        void (*thread_yield)(struct uthread *);
        void (*thread_exit)(struct uthread *);
+       void (*thread_blockon_sysc)(struct syscall *);
        unsigned int (*vcores_wanted)(void);
        /* Functions event handling wants */
        void (*preempt_pending)(void);
@@ -41,6 +42,8 @@ struct uthread *uthread_create(void (*func)(void), void *udata);
 void uthread_runnable(struct uthread *uthread);
 void uthread_yield(void);
 void uthread_exit(void);
+/* Block the calling uthread on sysc until it makes progress or is done */
+void ros_syscall_blockon(struct syscall *sysc);
 
 /* Utility function.  Event code also calls this. */
 bool check_preempt_pending(uint32_t vcoreid);
index 1c9a5ec..09c3d54 100644 (file)
@@ -237,6 +237,27 @@ void uthread_exit(void)
        __uthread_exit(current_uthread);
 }
 
+/* Attempts to block on sysc, returning when it is done or progress has been
+ * made. */
+void ros_syscall_blockon(struct syscall *sysc)
+{
+       if (in_vcore_context()) {
+               /* vcore's don't know what to do yet, so do the default (spin) */
+               __ros_syscall_blockon(sysc);
+               return;
+       }
+       if (!sched_ops->thread_blockon_sysc || !current_uthread) {
+               /* There isn't a 2LS op for blocking.  Spin for now. */
+               __ros_syscall_blockon(sysc);
+               return;
+       }
+       /* double check before doing all this crap */
+       if (sysc->flags & (SC_DONE | SC_PROGRESS))
+               return;
+       /* TODO: switch to vcore context, then do shit */
+       sched_ops->thread_blockon_sysc(sysc);
+}
+
 /* Runs whatever thread is vcore's current_uthread */
 void run_current_uthread(void)
 {
index 1d31349..94c754b 100644 (file)
@@ -37,6 +37,7 @@ void pth_thread_exit(struct uthread *uthread);
 unsigned int pth_vcores_wanted(void);
 void pth_preempt_pending(void);
 void pth_spawn_thread(uintptr_t pc_start, void *data);
+void pth_blockon_sysc(struct syscall *sysc);
 
 struct schedule_ops pthread_sched_ops = {
        pth_init,
@@ -45,6 +46,7 @@ struct schedule_ops pthread_sched_ops = {
        pth_thread_runnable,
        pth_thread_yield,
        pth_thread_exit,
+       pth_blockon_sysc,
        pth_vcores_wanted,
        0, /* pth_preempt_pending, */
        0, /* pth_spawn_thread, */
@@ -218,6 +220,35 @@ void pth_spawn_thread(uintptr_t pc_start, void *data)
 {
 }
 
+/* Eventually, this will be called from vcore context, after the current thread
+ * has yielded and is trying to block on sysc. */
+void pth_blockon_sysc(struct syscall *sysc)
+{
+       printf("We tried to block on a syscall!\n");    
+       /* for now, just spin.  2LS stuff in later commits */
+       __ros_syscall_blockon(sysc);
+
+       #if 0
+       // testing shit.  should dont_migrate for this
+       uint32_t vcoreid = vcore_id();
+       struct event_queue local_ev_q = {0}, *ev_q = &local_ev_q;
+       ev_q->ev_mbox = &__procdata.vcore_preempt_data[vcoreid].ev_mbox;
+       ev_q->ev_flags = EVENT_IPI;
+       ev_q->ev_vcore = vcoreid;
+       sysc->u_data = current_uthread;
+       wmb();
+       sysc->ev_q = ev_q;
+       if (sysc->flags & (SC_DONE | SC_PROGRESS)) {
+               // try and atomically swap out our u_data.  if we got it, we restart/
+               // handle it.  o/w, the receiver of the message restarts.  we can't just
+               // let them do it, since the kernel might have checked the ev_q before
+               // we wrote it (but after it wrote the flags).
+       } else {
+               //we're done
+       }
+       #endif
+}
+
 /* Pthread interface stuff and helpers */
 
 int pthread_attr_init(pthread_attr_t *a)