|  | /* 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; | 
|  | int fd; | 
|  |  | 
|  | 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)) { | 
|  | fd = i + USER_FD_BASE; | 
|  | ufds[i]->fd = fd; | 
|  | return fd; | 
|  | } | 
|  | } | 
|  | } | 
|  | __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); | 
|  | return 0; | 
|  | } |