Add User FDs to glibc (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 26 Aug 2015 21:11:31 +0000 (17:11 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 28 Sep 2015 19:14:00 +0000 (15:14 -0400)
There are a bunch of Linux APIs that we can implement in userspace that
use FDs as a handle, e.g. epoll.  Eventually that handle gets passed to
close().  We need a way to intercept close() for these FDs.  It'd also
be nice to provide basic sanity checking too, so that if you pass the
wrong type of FD to something like epoll_ctl(), we can catch it.

User FDs are a chunk of reserved numbers in the space of FDs, used by
various userspace libraries that use FDs as their API.  The user FD
space starts where the kernel's leaves off.  Currently, the kernel
claims 19 bits of the 32 bit int for an FD.  The MSB flags whether it
is negative or not.  That leaves 12 bits for us.

Libraries just get an FD, which registers their struct user_fd.  Then
later they can lookup their user_fd for a given FD.  When the program
closes the fd, the user_fd's callback is called.

Rebuild glibc.

tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/Makefile
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/Versions
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/close.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/sys/user_fd.h [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/user_fd.c [new file with mode: 0644]

index 42dcd25..be2ba8c 100644 (file)
@@ -46,3 +46,9 @@ endif
 ifeq ($(subdir),stdlib)
 sysdep_routines += tls
 endif
+
+# User FDs
+ifeq ($(subdir),stdlib)
+sysdep_routines += user_fd
+endif
+sysdep_headers += sys/user_fd.h
index f60ff62..49b2684 100644 (file)
@@ -42,5 +42,8 @@ libc {
        # Normally not exported by glibc, but we do all of our threading in
        # userspace and need to set this up for each of our uthreads that have TLS.
        __ctype_init;
+
+    ufd_get_fd;
+    ufd_lookup;
   }
 }
index fb403ee..fc9f0df 100644 (file)
 #include <unistd.h>
 #include <stddef.h>
 #include <ros/syscall.h>
+#include <sys/user_fd.h>
 
 /* Write NBYTES of BUF to FD.  Return the number written, or -1.  */
 int
 __close (int fd)
 {
-  return ros_syscall(SYS_close, fd, 0, 0, 0, 0, 0);
+       if (fd >= USER_FD_BASE)
+               return glibc_close_helper(fd);
+       else
+               return ros_syscall(SYS_close, fd, 0, 0, 0, 0, 0);
 }
 libc_hidden_def (__close)
 weak_alias (__close, close)
diff --git a/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/sys/user_fd.h b/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/sys/user_fd.h
new file mode 100644 (file)
index 0000000..dc0a802
--- /dev/null
@@ -0,0 +1,57 @@
+/* Copyright (c) 2015 Google Inc.
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * User FDs
+ *
+ * There are a bunch of Linux APIs that we can implement in userspace that use
+ * FDs as a handle.  Eventually that handle gets passed to close().  User FDs
+ * are a chunk of reserved numbers in the space of FDs, used by various
+ * userspace libraries that use FDs as their API.
+ *
+ * The user FD space starts where the kernel's leaves off.  Currently, the
+ * kernel claims 19 bits of the 32 bit int for an FD.  The MSB flags whether it
+ * is negative or not.  That leaves 12 bits for us. */
+
+#ifndef _GLIBC_AKAROS_SYS_USER_FD_H
+#define _GLIBC_AKAROS_SYS_USER_FD_H
+
+#include <ros/limits.h>
+
+/* NR_FILE_DESC_MAX marks the limit of the kernel's FDs */
+#define USER_FD_BASE NR_FILE_DESC_MAX
+
+/* Tracker for user FDs.  Clients embed this in one of their structs.  When
+ * someone calls glibc's close() on the FD, it'll call the close func ptr.  The
+ * callback uses container_of() to get its object.
+ *
+ * 'magic' can be whatever the client wants - it could be useful to make sure
+ * you have the right type of FD. */
+struct user_fd {
+       int                                                     magic;
+       void (*close)(struct user_fd *);
+};
+
+/* Finds a free user FD and stores ufd there.  Returns the FD, or -1 on error
+ * (out of FDs).  You can remove it with a close(). */
+int ufd_get_fd(struct user_fd *ufd);
+
+/* Given an FD, returns the user_fd struct.  Returns 0 and sets errno if there's
+ * an error.  There's no protection for concurrent closes, just like how you
+ * shouldn't attempt to use an FD after closing.  So don't do stuff like:
+ *             foo = ufd_lookup(7);
+ *                                                        close(7);
+ *             foo->whatever = 6; // foo could be free!
+ *
+ * or
+ *                                                        close(7);
+ *             foo = ufd_lookup(7);
+ *      // this might succeed if it races with close()
+ *             foo->whatever = 6; // foo could be free!
+ */
+struct user_fd *ufd_lookup(int fd);
+
+/* Called by glibc's close.  Do not call this directly. */
+int glibc_close_helper(int fd);
+
+#endif /* _GLIBC_AKAROS_SYS_USER_FD_H */
diff --git a/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/user_fd.c b/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/user_fd.c
new file mode 100644 (file)
index 0000000..377496e
--- /dev/null
@@ -0,0 +1,87 @@
+/* Copyright (c) 2015 Google Inc.
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * User FDs
+ *
+ * There are a bunch of Linux APIs that we can implement in userspace that use
+ * FDs as a handle.  Eventually that handle gets passed to close().  User FDs
+ * are a chunk of reserved numbers in the space of FDs, used by various
+ * userspace libraries that use FDs as their API.
+ *
+ * The user FD space starts where the kernel's leaves off.  Currently, the
+ * kernel claims 19 bits of the 32 bit int for an FD.  The MSB flags whether it
+ * is negative or not.  That leaves 12 bits for us. */
+
+#include <sys/user_fd.h>
+#include <parlib/arch/atomic.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+static struct user_fd **ufds = 0;
+static size_t nr_ufds = 0;
+
+/* Finds a free user FD and stores ufd there.  Returns the FD, or -1 on error
+ * (out of FDs).  You can remove it with a close(). */
+int ufd_get_fd(struct user_fd *ufd)
+{
+       struct user_fd **new_ufds;
+       if (!ufds) {
+               nr_ufds = 1 << (sizeof(int) * 8 - LOG2_UP(NR_FILE_DESC_MAX) - 1);
+               /* Two things: instead of worrying about growing and reallocing (which
+                * would need a lock), let's just alloc the entire 2^15 bytes (32KB).
+                * Also, it's unlikely, but we might have two threads trying to init at
+                * once.  First one wins, second one aborts (and frees). */
+               new_ufds = malloc(sizeof(struct user_fd*) * nr_ufds);
+               memset(new_ufds, 0, sizeof(struct user_fd*) * nr_ufds);
+               if (!atomic_cas_ptr((void**)&ufds, 0, new_ufds))
+                       free(new_ufds);
+               cmb();
+       }
+       /* At this point, ufds is set.  Just do a linear search for an empty slot.
+        * We're not actually bound to return the lowest number available, so in the
+        * future we could do things like partition the space based on vcoreid so we
+        * start in different areas, or maintain a 'last used' hint FD. */
+       for (int i = 0; i < nr_ufds; i++) {
+               if (!ufds[i]) {
+                       if (atomic_cas_ptr((void**)&ufds[i], 0, ufd))
+                               return i + USER_FD_BASE;
+               }
+       }
+       __set_errno(ENFILE);
+       return -1;
+}
+
+/* Given an FD, returns the user_fd struct.  Returns 0 and sets errno if there's
+ * an error.  There's no protection for concurrent closes, just like how you
+ * shouldn't attempt to use an FD after closing.  So don't do stuff like:
+ *             foo = ufd_lookup(7);
+ *                                                        close(7);
+ *             foo->whatever = 6; // foo could be free!
+ *
+ * or
+ *                                                        close(7);
+ *             foo = ufd_lookup(7);
+ *      // this might succeed if it races with close()
+ *             foo->whatever = 6; // foo could be free!
+ */
+struct user_fd *ufd_lookup(int fd)
+{
+       if (!ufds || (fd - USER_FD_BASE >= nr_ufds)) {
+               __set_errno(EBADF);
+               return 0;
+       }
+       return ufds[fd - USER_FD_BASE];
+}
+
+/* Removes the user_fd from the FD space, calls its callback.  Returns 0 on
+ * success.  -1 and sets errno o/w (e.g. EBADF). */
+int glibc_close_helper(int fd)
+{
+       struct user_fd *ufd = ufd_lookup(fd);
+       if (!ufd)
+               return -1;
+       ufds[fd - USER_FD_BASE] = 0;
+       ufd->close(ufd);
+}