Add User FDs to glibc (XCC)
[akaros.git] / tools / compilers / gcc-glibc / glibc-2.19-akaros / sysdeps / akaros / user_fd.c
1 /* Copyright (c) 2015 Google Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * User FDs
6  *
7  * There are a bunch of Linux APIs that we can implement in userspace that use
8  * FDs as a handle.  Eventually that handle gets passed to close().  User FDs
9  * are a chunk of reserved numbers in the space of FDs, used by various
10  * userspace libraries that use FDs as their API.
11  *
12  * The user FD space starts where the kernel's leaves off.  Currently, the
13  * kernel claims 19 bits of the 32 bit int for an FD.  The MSB flags whether it
14  * is negative or not.  That leaves 12 bits for us. */
15
16 #include <sys/user_fd.h>
17 #include <parlib/arch/atomic.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <errno.h>
21
22 static struct user_fd **ufds = 0;
23 static size_t nr_ufds = 0;
24
25 /* Finds a free user FD and stores ufd there.  Returns the FD, or -1 on error
26  * (out of FDs).  You can remove it with a close(). */
27 int ufd_get_fd(struct user_fd *ufd)
28 {
29         struct user_fd **new_ufds;
30         if (!ufds) {
31                 nr_ufds = 1 << (sizeof(int) * 8 - LOG2_UP(NR_FILE_DESC_MAX) - 1);
32                 /* Two things: instead of worrying about growing and reallocing (which
33                  * would need a lock), let's just alloc the entire 2^15 bytes (32KB).
34                  * Also, it's unlikely, but we might have two threads trying to init at
35                  * once.  First one wins, second one aborts (and frees). */
36                 new_ufds = malloc(sizeof(struct user_fd*) * nr_ufds);
37                 memset(new_ufds, 0, sizeof(struct user_fd*) * nr_ufds);
38                 if (!atomic_cas_ptr((void**)&ufds, 0, new_ufds))
39                         free(new_ufds);
40                 cmb();
41         }
42         /* At this point, ufds is set.  Just do a linear search for an empty slot.
43          * We're not actually bound to return the lowest number available, so in the
44          * future we could do things like partition the space based on vcoreid so we
45          * start in different areas, or maintain a 'last used' hint FD. */
46         for (int i = 0; i < nr_ufds; i++) {
47                 if (!ufds[i]) {
48                         if (atomic_cas_ptr((void**)&ufds[i], 0, ufd))
49                                 return i + USER_FD_BASE;
50                 }
51         }
52         __set_errno(ENFILE);
53         return -1;
54 }
55
56 /* Given an FD, returns the user_fd struct.  Returns 0 and sets errno if there's
57  * an error.  There's no protection for concurrent closes, just like how you
58  * shouldn't attempt to use an FD after closing.  So don't do stuff like:
59  *              foo = ufd_lookup(7);
60  *                                                         close(7);
61  *              foo->whatever = 6; // foo could be free!
62  *
63  * or
64  *                                                         close(7);
65  *              foo = ufd_lookup(7);
66  *      // this might succeed if it races with close()
67  *              foo->whatever = 6; // foo could be free!
68  */
69 struct user_fd *ufd_lookup(int fd)
70 {
71         if (!ufds || (fd - USER_FD_BASE >= nr_ufds)) {
72                 __set_errno(EBADF);
73                 return 0;
74         }
75         return ufds[fd - USER_FD_BASE];
76 }
77
78 /* Removes the user_fd from the FD space, calls its callback.  Returns 0 on
79  * success.  -1 and sets errno o/w (e.g. EBADF). */
80 int glibc_close_helper(int fd)
81 {
82         struct user_fd *ufd = ufd_lookup(fd);
83         if (!ufd)
84                 return -1;
85         ufds[fd - USER_FD_BASE] = 0;
86         ufd->close(ufd);
87 }