Glibc syscalls now block properly (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 21 Mar 2012 19:50:38 +0000 (12:50 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 21 Mar 2012 19:50:38 +0000 (12:50 -0700)
Glibc wasn't calling out to the proper blockon call.  We deal with this
by having a default blockon, defined in glibc's syscall.c, that
degenerate SCPs can use.  Once they init enough and have access to
parlib symbols, they switch the function ptr to use the 'real' blockon,
which uses glibc's default one for SCPs.

Rebuild glibc and anything in userspace.

12 files changed:
Documentation/glibc.txt
kern/include/ros/syscall.h
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/Makefile
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/Versions
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/i386/tls.h
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/sys/syscall.h [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/syscall.c
user/parlib/include/parlib.h
user/parlib/include/uthread.h
user/parlib/include/vcore.h
user/parlib/uthread.c
user/parlib/vcore.c

index 789e54c..ea5d901 100644 (file)
@@ -115,9 +115,6 @@ Because errno is valid when the linker runs, regular syscalls can be made.
 However for statically linked apps, several syscalls cannot use the ros_syscall
 macro, because there is no valid errno set up. 
 
-
-
-
 Unimplemented Stubs
 --------------------------
 There are a lot of things we haven't ported, but we have the stub functions so
@@ -131,6 +128,47 @@ something to keep glibc happy, even though ROS doesn't have any networking
 syscalls.  Don't rely on this stuff too much.  These files, and other future
 glibc compatibility kernel headers, are in kern/include/ros/glibc-asm.
 
+Weak Symbols, start.c, and ros_syscall_blockon
+--------------------------
+For a long time, __ros_syscall_blockon was not getting overridden by the
+strong symbol in parlib.  This means that glibc's syscalls were not blocking
+uthreads properly.  Why did the weak symbols work for vcore_entry() and
+vcore_event_init(), but not blockon?
+
+Side note: we needed to force the linker to find out about vcore_entry and
+vcore_event_init, via the -u tricks of commit f188983.  The linker is not
+obligated to look in libraries to override a weak symbol.
+
+The crux of the matter is that start.c is not linked with applications in the
+same manner as the rest of glibc (notably all the I/O syscalls).  start.c will
+get linked with the program at compile time, while libc can be linked
+dynamically.  Because of this, start.c's weak symbols would get (correctly)
+overridden by the strong symbols of libparlib.a.  But glibc would build
+libc.so, and that would not get a chance to link against the binary (and
+libparlib.a) until load time.  The weak symbols in libc get promoted to strong
+symbols when it is done linking libc.so, and when it later is linked against
+the program, there is no longer an opportunity to override the weak symbol.
+
+Also note that rtld will never link with parlib, so any weak symbol in there
+will never get overriden.  I briefly considered linking rtld with -lparlib,
+but it blows up in a nasty way: parlib needs lots of libc, which rtld is not
+built with (memset for example).
+
+Anyway, the moral of the story is to be careful with your weak
+symbols/aliases.  Only put them in start.c, or similar files.
+
+Adding a Global Variable to the ABI
+--------------------------
+It's not enough to simply 'extern' a variable (or declare a function, which is
+extern by default).  At some point, glibc will change those symbols from
+GLOBAL to LOCAL.
+
+You need to add an entry to the Versions file (sysdeps/ros/Versions).  Be sure
+to indent with spaces, not tabs, or else glibc's scripts will not handle your
+symbol.
+
+Putting them in the libc, glibc 2.0 section seems to work fine.
+
 Tips, Questions, and Misc Notes
 --------------------------
 - Grep and find are your friend.
index 5ff45a0..9cd2c5b 100644 (file)
@@ -31,65 +31,8 @@ struct syscall {
 
 #ifndef ROS_KERNEL
 
-#include <arch/atomic.h>
-
-/* Attempts to block on sysc, returning when it is done or progress has been
- * made.  (function is in uthread.c) */
-void ros_syscall_blockon(struct syscall *sysc);
-
-/* No one should be using this - it's meant to allow glibc to compile, and all
- * apps will link against parlib to get the real function. */
-static inline void __ros_syscall_blockon(struct syscall *sysc)
-{
-       /* My ghetto error message: force a PF */
-       int *x = (int*)0xdeadbeef;
-       *x = 1337;
-}
-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
- * want this. */
-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;        /* not used yet */
-       struct syscall sysc = {0};
-       sysc.num = _num;
-       sysc.ev_q = 0;
-       sysc.arg0 = _a0;
-       sysc.arg1 = _a1;
-       sysc.arg2 = _a2;
-       sysc.arg3 = _a3;
-       sysc.arg4 = _a4;
-       sysc.arg5 = _a5;
-       num_started = __ros_arch_syscall(&sysc, 1);
-       /* Don't proceed til we are done */
-       while (!(atomic_read(&sysc.flags) & SC_DONE))
-               ros_syscall_blockon(&sysc);
-       /* Need to wait til it is unlocked.  It's not really done until SC_DONE &
-        * !SC_K_LOCK. */
-       while (atomic_read(&sysc.flags) & SC_K_LOCK)
-               cpu_relax();
-       if (errno_loc)
-               *errno_loc = sysc.err;
-       return sysc.retval;
-}
-
-#include <errno.h>
-
-/* This version knows about errno and will handle it. */
-static inline long __ros_syscall_errno(unsigned int _num, long _a0, long _a1,
-                                       long _a2, long _a3, long _a4, long _a5)
-{
-       return __ros_syscall(_num, _a0, _a1, _a2, _a3, _a4, _a5, &errno);
-}
-
-/* Convenience wrapper for __ros_syscall */
-#define ros_syscall(which, a0, a1, a2, a3, a4, a5) \
-   __ros_syscall_errno(which, (long)(a0), (long)(a1), (long)(a2), (long)(a3), \
-                       (long)(a4), (long)(a5))
+/* Temp hack, til the rest of glibc/userspace uses sys/syscall.h */
+#include <sys/syscall.h>
 
 #endif /* ifndef ROS_KERNEL */
 
index 3816eda..b78b4f7 100644 (file)
@@ -9,3 +9,4 @@ CPPFLAGS += -D_IO_MTSAFE_IO=1
 ifeq ($(subdir),socket)
 sysdep_routines += sa_len
 endif
+sysdep_headers += sys/syscall.h
index 7f72d5c..e0c44ac 100644 (file)
@@ -1,6 +1,10 @@
 libc {
   GLIBC_2.0 {
     __errno_location;
+    ros_syscall_blockon;
+    __ros_scp_syscall_blockon;
+    __ros_scp_simple_evq;
+    __ros_syscall;
+    __ros_syscall_errno;
   }
 }
-
index dc70913..325eaec 100644 (file)
@@ -30,7 +30,7 @@
 # include <sysdep.h>
 # include <kernel-features.h>
 #include <sys/mman.h>
-#include <ros/syscall.h>
+#include <sys/syscall.h>
 #include <ros/procinfo.h>
 #include <ros/procdata.h>
 #include <ros/arch/mmu.h>
diff --git a/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/sys/syscall.h b/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/sys/syscall.h
new file mode 100644 (file)
index 0000000..50459d4
--- /dev/null
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * glibc syscall functions / tools for working with Akaros */
+
+#ifndef _GLIBC_ROS_SYSCALL_H
+#define _GLIBC_ROS_SYSCALL_H
+
+#include <ros/syscall.h>
+#include <ros/event.h>
+
+/**************** Functions to invoke syscalls ****************/
+/* Convenience wrapper for __ros_syscall_errno.  Most code uses this (for now)*/
+#define ros_syscall(which, a0, a1, a2, a3, a4, a5) \
+   __ros_syscall_errno(which, (long)(a0), (long)(a1), (long)(a2), (long)(a3), \
+                       (long)(a4), (long)(a5))
+
+/* Raw syscall, user-provided errno (send in 0 if you don't want it).  This is
+ * usually used by code that can't handle errno (TLS). */
+long __ros_syscall(unsigned int _num, long _a0, long _a1, long _a2, long _a3,
+                   long _a4, long _a5, int *errno_loc);
+
+/* This version knows about errno and will handle it. */
+long __ros_syscall_errno(unsigned int _num, long _a0, long _a1, long _a2,
+                         long _a3, long _a4, long _a5);
+
+/**************** Additional syscall support ****************/
+/* Simple ev_q that routes notifs to vcore0's public mbox.  This is used by the
+ * default scp_syscall, but can also be used for signals or other basic
+ * event/signal needs. */
+struct event_queue __ros_scp_simple_evq;
+
+/* Attempts to block on sysc, returning when it is done or progress has been
+ * made.  (function is in uthread.c) */
+extern void (*ros_syscall_blockon)(struct syscall *sysc);
+
+/* Glibc initial blockon, usable before parlib code can init things (or if it
+ * never can, like for RTLD).  MCPs will need the 'uthread-aware' blockon. */
+void __ros_scp_syscall_blockon(struct syscall *sysc);
+
+#endif /* _GLIBC_ROS_SYSCALL_H */
index ebdd17f..2f1efcf 100644 (file)
 #include <errno.h>
 #include <unistd.h>
 #include <stdarg.h>
-#include <ros/syscall.h>
+#include <sys/syscall.h>
+#include <arch/atomic.h>
+#include <ros/procdata.h>
 
-long int
-syscall(long int num, ...)
+/* This is a simple ev_q that routes notifs to vcore0's public mbox.  This
+ * should work for any bit messages, even if the process hasn't done any set up
+ * yet, since the memory for the mbox is allocted by the kernel (procdata).
+ * Don't send full messages to it, since the UCQ won't be initialized.  Note
+ * that the kernel will actually ignore your ev_mbox and just about everything
+ * other than flags if you're an SCP, but that might change later. */
+struct event_queue __ros_scp_simple_evq =
+                  { .ev_mbox = &__procdata.vcore_preempt_data[0].ev_mbox_public,
+                    .ev_flags = EVENT_IPI | EVENT_NOMSG, 
+                    .ev_alert_pending = FALSE,
+                    .ev_vcore = 0,
+                    .ev_handler = 0 };
+
+/* Helper, from u/p/uthread.c.  Keep it in sync.  (don't want to move this into
+ * glibc yet). */
+static bool register_evq(struct syscall *sysc, struct event_queue *ev_q)
+{
+       int old_flags;
+       sysc->ev_q = ev_q;
+       wrmb(); /* don't let that write pass any future reads (flags) */
+       /* Try and set the SC_UEVENT flag (so the kernel knows to look at ev_q) */
+       do {
+               /* no cmb() needed, the atomic_read will reread flags */
+               old_flags = atomic_read(&sysc->flags);
+               /* Spin if the kernel is mucking with syscall flags */
+               while (old_flags & SC_K_LOCK)
+                       old_flags = atomic_read(&sysc->flags);
+               /* If the kernel finishes while we are trying to sign up for an event,
+                * we need to bail out */
+               if (old_flags & (SC_DONE | SC_PROGRESS)) {
+                       sysc->ev_q = 0;         /* not necessary, but might help with bugs */
+                       return FALSE;
+               }
+       } while (!atomic_cas(&sysc->flags, old_flags, old_flags | SC_UEVENT));
+       return TRUE;
+}
+
+/* Glibc initial blockon, usable before parlib code can init things (or if it
+ * never can, like for RTLD).  MCPs will need the 'uthread-aware' blockon. */
+void __ros_scp_syscall_blockon(struct syscall *sysc)
+{
+       /* Ask for a SYSCALL event when the sysc is done.  We don't need a handler,
+        * we just need the kernel to restart us from proc_yield.  If register
+        * fails, we're already done. */
+       if (register_evq(sysc, &__ros_scp_simple_evq)) {
+               /* Sending false for now - we want to signal proc code that we want to
+                * wait (piggybacking on the MCP meaning of this variable) */
+               __ros_syscall(SYS_yield, FALSE, 0, 0, 0, 0, 0, 0);
+       }
+}
+
+/* Function pointer for the blockon function.  MCPs need to switch to the parlib
+ * blockon before becoming an MCP.  Default is the glibc SCP handler */
+void (*ros_syscall_blockon)(struct syscall *sysc) = __ros_scp_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
+ * want this. */
+long __ros_syscall(unsigned int _num, long _a0, long _a1, long _a2, long _a3,
+                   long _a4, long _a5, int *errno_loc)
+{
+       int num_started;        /* not used yet */
+       struct syscall sysc = {0};
+       sysc.num = _num;
+       sysc.ev_q = 0;
+       sysc.arg0 = _a0;
+       sysc.arg1 = _a1;
+       sysc.arg2 = _a2;
+       sysc.arg3 = _a3;
+       sysc.arg4 = _a4;
+       sysc.arg5 = _a5;
+       num_started = __ros_arch_syscall(&sysc, 1);
+       /* Don't proceed til we are done */
+       while (!(atomic_read(&sysc.flags) & SC_DONE))
+               ros_syscall_blockon(&sysc);
+       /* Need to wait til it is unlocked.  It's not really done until SC_DONE &
+        * !SC_K_LOCK. */
+       while (atomic_read(&sysc.flags) & SC_K_LOCK)
+               cpu_relax();
+       if (errno_loc)
+               *errno_loc = sysc.err;
+       return sysc.retval;
+}
+
+/* This version knows about errno and will handle it. */
+long __ros_syscall_errno(unsigned int _num, long _a0, long _a1, long _a2,
+                         long _a3, long _a4, long _a5)
+{
+       return __ros_syscall(_num, _a0, _a1, _a2, _a3, _a4, _a5, &errno);
+}
+
+long int syscall(long int num, ...)
 {
-  va_list vl;
-  va_start(vl,num);
-  long int a0 = va_arg(vl,long int);
-  long int a1 = va_arg(vl,long int);
-  long int a2 = va_arg(vl,long int);
-  long int a3 = va_arg(vl,long int);
-  long int a4 = va_arg(vl,long int);
-  long int a5 = va_arg(vl,long int);
-  va_end(vl);
-
-  return ros_syscall(num, a0, a1, a2, a3, a4, a5);
+       va_list vl;
+       va_start(vl, num);
+       long int a0 = va_arg(vl, long int);
+       long int a1 = va_arg(vl, long int);
+       long int a2 = va_arg(vl, long int);
+       long int a3 = va_arg(vl, long int);
+       long int a4 = va_arg(vl, long int);
+       long int a5 = va_arg(vl, long int);
+       va_end(vl);
+       
+       return ros_syscall(num, a0, a1, a2, a3, a4, a5);
 }
 
index 1e6f350..28a836d 100644 (file)
@@ -15,6 +15,7 @@
 #include <ros/procinfo.h>
 #include <ros/procdata.h>
 #include <stdint.h>
+#include <errno.h>
 #include <ros_debug.h>
 
 enum {
index 30ef9be..4c35135 100644 (file)
@@ -59,8 +59,6 @@ void uthread_slim_init(void);
 void uthread_cleanup(struct uthread *uthread);
 void uthread_runnable(struct uthread *uthread);
 void uthread_yield(bool save_state);
-/* Block the calling uthread on sysc until it makes progress or is done */
-void ros_syscall_blockon(struct syscall *sysc);
 
 /* Utility functions */
 bool __check_preempt_pending(uint32_t vcoreid);        /* careful: check the code */
index 645c051..126490c 100644 (file)
@@ -6,6 +6,7 @@ extern "C" {
 #endif
 
 #include <arch/vcore.h>
+#include <arch/atomic.h>
 #include <sys/param.h>
 #include <string.h>
 
@@ -31,8 +32,6 @@ extern "C" {
 extern void vcore_entry();
 /* Declared in glibc's start.c */
 extern __thread bool __vcore_context;
-/* Simple ev_q (bits, IPIs, vc0) for scp syscalls, signals, etc */
-struct event_queue *__scp_simple_evq;
 
 /* Utility Functions */
 void *allocate_tls(void);
index 70187af..9287b92 100644 (file)
@@ -22,6 +22,9 @@ static void __uthread_free_tls(struct uthread *uthread);
 static void __run_current_uthread_raw(void);
 static void handle_vc_preempt(struct event_msg *ev_msg, unsigned int ev_type);
 
+/* Block the calling uthread on sysc until it makes progress or is done */
+static void __ros_mcp_syscall_blockon(struct syscall *sysc);
+
 /* Helper, make the uthread code manage thread0.  This sets up uthread such
  * that the calling code and its TLS are tracked by the uthread struct, and
  * vcore0 thinks the uthread is running there.  Called only by slim_init (early
@@ -111,6 +114,11 @@ void uthread_slim_init(void)
        assert(!vcore_init());
        uthread_manage_thread0(uthread);
        scp_vcctx_ready();
+       /* change our blockon from glibc's internal one to the mcp one (which can
+        * handle SCPs too).  we must do this before switching to _M, or at least
+        * before blocking while an _M.  it's harmless (and probably saner) to do it
+        * earlier, so we do it as early as possible. */
+       ros_syscall_blockon = __ros_mcp_syscall_blockon;
 }
 
 /* 2LSs shouldn't call uthread_vcore_entry directly */
@@ -319,30 +327,13 @@ static void __ros_syscall_spinon(struct syscall *sysc)
                cpu_relax();
 }
 
-static void __ros_syscall_scp_blockon(struct syscall *sysc)
-{
-       /* Until we're ready (advertised via the *evq), we must spin */
-       if (!__scp_simple_evq) {
-               __ros_syscall_spinon(sysc);
-               return;
-       }
-       /* Ask for a SYSCALL event when the sysc is done.  We don't need a handler,
-        * we just need the kernel to restart us from proc_yield.  If register
-        * fails, we're already done. */
-       if (register_evq(sysc, __scp_simple_evq)) {
-               /* Sending false for now - we want to signal proc code that we want to
-                * wait (piggybacking on the MCP meaning of this variable */
-               sys_yield(FALSE);
-       }
-}
-
 /* Attempts to block on sysc, returning when it is done or progress has been
  * made. */
-void ros_syscall_blockon(struct syscall *sysc)
+void __ros_mcp_syscall_blockon(struct syscall *sysc)
 {
        /* even if we are in 'vcore context', an _S can block */
        if (!in_multi_mode()) {
-               __ros_syscall_scp_blockon(sysc);
+               __ros_scp_syscall_blockon(sysc);
                return;
        }
        /* MCP vcore's don't know what to do yet, so we have to spin */
@@ -755,7 +746,9 @@ out_indirs:
 /* Attempts to register ev_q with sysc, so long as sysc is not done/progress.
  * Returns true if it succeeded, and false otherwise.  False means that the
  * syscall is done, and does not need an event set (and should be handled
- * accordingly)*/
+ * accordingly).
+ * 
+ * A copy of this is in glibc/sysdeps/ros/syscall.c.  Keep them in sync. */
 bool register_evq(struct syscall *sysc, struct event_queue *ev_q)
 {
        int old_flags;
index 73d8d6e..6085e80 100644 (file)
@@ -19,8 +19,6 @@
 static size_t _max_vcores_ever_wanted = 1;
 atomic_t nr_new_vcores_wanted;
 atomic_t vc_req_being_handled;
-/* Simple ev_q (bits, IPIs, vc0) for scp syscalls, signals, etc */
-struct event_queue *__scp_simple_evq = 0;
 
 extern void** vcore_thread_control_blocks;
 
@@ -171,7 +169,6 @@ vcore_init_fail:
 void force_parlib_symbols(void)
 {
        vcore_event_init();
-       ros_syscall_blockon(0); /* don't seem to need to force this for now */
        assert(0);
 }
 
@@ -182,12 +179,6 @@ void vcore_event_init(void)
 {
        /* set up our thread0 as a uthread */
        uthread_slim_init();
-       /* Set up an ev_q for blocking _Ss on syscalls.  Events will get sent to
-        * vcore0's VCPD public mbox.  We'll get a bit, instead of a full message,
-        * since we don't need to know *sysc.  Also note that the event handler is
-        * 0, until set by a 2LS.  We don't need a handler - just need to get woken
-        * up. */
-       __scp_simple_evq = get_event_q_vcpd(0, EVENT_NOMSG | EVENT_IPI);
        /* TODO: register for other kevents/signals and whatnot (can probably reuse
         * the simple ev_q).  Could also do this via explicit functions from the
         * program. */