parlib: Fix the use-after-func issue
[akaros.git] / user / iplib / select.c
1 /* Copyright (c) 2016 Google Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * select()
6  *
7  * Our select() is a bit rough and only works with FDs where fstat can return
8  * S_READABLE or S_WRITABLE.  For the most part, this applies to qio queues,
9  * which are the basis for a lot of the network stack and pipes.  FDs where
10  * fstat doesn't tell us the readiness will have races.
11  *
12  * Under the hood, our select() is implemented with epoll (and under that, FD
13  * taps).  Those can only detect edges (e.g. a socket becomes readable).
14  *
15  * The problem is that we want to detect a level status (e.g. socket is
16  * readable) with an edge event (e.g. socket *becomes* readable).  To do this,
17  * when someone initially selects, the FD gets tracked with epoll and we
18  * manually poll the FDs with fstat.  Subsequent selects() will still be tracked
19  * in the epoll set, but since apps can select() even on FDs they didn't drain
20  * to the point of blocking, we still need to poll every FD on every select()
21  * call.
22  *
23  * We maintain one FD set per program.  It tracks *any* FD being tracked by
24  * *any* select call.  This is because you can only have one tap per FD.
25  * Regardless of whether the user asked for read/write/except, the FD gets
26  * watched for anything until it closes.
27  *
28  * One issue with the global FD set is that one thread may consume the epoll
29  * events intended for another thread (or even for itself at another call
30  * site!).  To get around this, only one thread is the actual epoller, and the
31  * others block on a mutex.  TLS isn't an option for two reasons: not all 2LSs
32  * use TLS (minor concern, maybe they should) and there are some threads who
33  * make multiple select calls - we actually want per-call-site-and-thread fd
34  * sets.
35  *
36  * Notes:
37  * - pselect might be racy
38  * - if the user has no read/write/except sets, we won't wait.  some users of
39  *   select use it as a timer only.  if that comes up, we can expand this.
40  * - if you epoll or FD tap an FD, then try to use select on it, you'll get an
41  *   error (only one tap per FD).  select() only knows about the FDs in its set.
42  * - if you select() on a readfd that is a disk file, it'll always say it is
43  *   available for I/O.
44  */
45
46 #include <sys/select.h>
47 #include <sys/stat.h>
48 #include <sys/time.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51
52 #include <errno.h>
53 #include <malloc.h>
54 #include <parlib/arch/arch.h>
55 #include <parlib/uthread.h>
56 #include <parlib/parlib.h>
57 #include <ros/common.h>
58 #include <ros/fs.h>
59 #include <signal.h>
60 #include <stdlib.h>
61 #include <sys/close_cb.h>
62 #include <sys/epoll.h>
63 #include <sys/fork_cb.h>
64
65 static int epoll_fd;
66 static fd_set all_fds;
67 static fd_set working_read_fds;
68 static fd_set working_write_fds;
69 static fd_set working_except_fds;
70 static uth_mutex_t *epoll_mtx;
71
72 static bool fd_is_set(unsigned int fd, fd_set *set)
73 {
74         if (fd > FD_SETSIZE)
75                 return FALSE;
76         if (!set)
77                 return FALSE;
78         return FD_ISSET(fd, set);
79 }
80
81 static void select_fd_closed(int fd)
82 {
83         /* Slightly racy, but anything concurrently added will be closed later, and
84          * after it is_set. */
85         if (!fd_is_set(fd, &all_fds))
86                 return;
87         /* We just need to stop tracking FD.  We do not need to remove it from the
88          * epoll set, since that will happen automatically on close(). */
89         uth_mutex_lock(epoll_mtx);
90         FD_CLR(fd, &all_fds);
91         uth_mutex_unlock(epoll_mtx);
92 }
93
94 static void select_forked(void)
95 {
96         struct epoll_event ep_ev;
97
98         uth_mutex_lock(epoll_mtx);
99         for (int i = 0; i < FD_SETSIZE; i++) {
100                 if (fd_is_set(i, &all_fds)) {
101                         ep_ev.events = EPOLLET | EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLERR;
102                         ep_ev.data.fd = i;
103                         /* Discard error.  The underlying tap is gone, and the epoll ctlr
104                          * might also have been emptied.  We just want to make sure there is
105                          * no epoll/tap so that a future CTL_ADD doesn't fail. */
106                         epoll_ctl(epoll_fd, EPOLL_CTL_DEL, i, &ep_ev);
107                         FD_CLR(i, &all_fds);
108                 }
109         }
110         uth_mutex_unlock(epoll_mtx);
111 }
112
113 static void select_init(void *arg)
114 {
115         static struct close_cb select_close_cb = {.func = select_fd_closed};
116         static struct fork_cb select_fork_cb = {.func = select_forked};
117
118         register_close_cb(&select_close_cb);
119         epoll_fd = epoll_create(FD_SETSIZE);
120         if (epoll_fd < 0) {
121                 perror("select failed epoll_create");
122                 exit(-1);
123         }
124         epoll_mtx = uth_mutex_alloc();
125         register_fork_cb(&select_fork_cb);
126 }
127
128 static int select_tv_to_ep_timeout(struct timeval *tv)
129 {
130         if (!tv)
131                 return -1;
132         return tv->tv_sec * 1000 + DIV_ROUND_UP(tv->tv_usec, 1000);
133 }
134
135 /* Helper: check with the kernel if FD is readable/writable or not.  Some apps
136  * will call select() on something even if it is already actionable, and not
137  * wait until they get the EAGAIN.
138  *
139  * This modifies the global working_ fd sets by setting bits of actionable FDs
140  * and will return the number of bits turned on.  So basically, 1 for readable
141  * xor writable, 2 for both.
142  *
143  * TODO: this *won't* work for disk based files.  It only works on qids that are
144  * backed with qio queues or something similar, where the device has support for
145  * setting DMREADABLE/DMWRITABLE. */
146 static unsigned int fd_set_actionable(int fd, fd_set *readfds, fd_set *writefds)
147 {
148         struct stat stat_buf;
149         int ret;
150
151         /* Avoid the stat call on FDs we're not tracking (which should trigger an
152          * error, or give us the stat for FD 0). */
153         if (!(fd_is_set(fd, readfds) || fd_is_set(fd, writefds)))
154                 return 0;
155         ret = fstat(fd, &stat_buf);
156         assert(!ret);
157         ret = 0;
158         if (fd_is_set(fd, readfds)) {
159                 if (S_READABLE(stat_buf.st_mode)) {
160                         ret++;
161                         FD_SET(fd, &working_read_fds);
162                 }
163         }
164         if (fd_is_set(fd, writefds)) {
165                 if (S_WRITABLE(stat_buf.st_mode)) {
166                         ret++;
167                         FD_SET(fd, &working_write_fds);
168                 }
169         }
170         return ret;
171 }
172
173 /* Helper: extracts events from ep_result for types ep_event_types, and sets
174  * their bits in ret_fds if the FD was watched.  Returns the number of bits set.
175  */
176 static int extract_bits_for_events(struct epoll_event *ep_result,
177                                    uint32_t ep_event_types,
178                                    fd_set *watched_fds, fd_set *ret_fds)
179 {
180         int ret = 0;
181         int fd = ep_result->data.fd;
182
183         if (ep_result->events & ep_event_types) {
184                 if (fd_is_set(fd, watched_fds) && !FD_ISSET(fd, ret_fds)) {
185                         FD_SET(fd, ret_fds);
186                         ret++;
187                 }
188         }
189         return ret;
190 }
191
192 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
193            struct timeval *timeout)
194 {
195         struct epoll_event ep_ev;
196         struct epoll_event *ep_results;
197         int ret, ep_ret, ep_timeout;
198         static parlib_once_t once = PARLIB_ONCE_INIT;
199         struct timeval start_tv[1], end_tv[1];
200
201         parlib_run_once(&once, select_init, NULL);
202         /* good thing nfds is a signed int... */
203         if (nfds < 0) {
204                 errno = EINVAL;
205                 return -1;
206         }
207 loop:
208         if (timeout)
209                 gettimeofday(start_tv, NULL);
210         ep_timeout = select_tv_to_ep_timeout(timeout);
211         uth_mutex_lock(epoll_mtx);
212         for (int i = 0; i < nfds; i++) {
213                 if ((fd_is_set(i, readfds) || fd_is_set(i, writefds) ||
214                      fd_is_set(i, exceptfds)) &&
215                     !fd_is_set(i, &all_fds)) {
216
217                         FD_SET(i, &all_fds);
218                         /* FDs that we track for *any* reason with select will be
219                          * tracked for *all* reasons with epoll. */
220                         ep_ev.events = EPOLLET | EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLERR;
221                         ep_ev.data.fd = i;
222                         if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, i, &ep_ev)) {
223                                 /* We might have failed because we tried to set up too many
224                                  * FD tap types.  Listen FDs, for instance, can only be
225                                  * tapped for READABLE and HANGUP.  Let's try for one of
226                                  * those. */
227                                 if (errno == ENOSYS) {
228                                         ep_ev.events = EPOLLET | EPOLLIN | EPOLLHUP;
229                                         if (!epoll_ctl(epoll_fd, EPOLL_CTL_ADD, i, &ep_ev))
230                                                 continue;
231                                 }
232                                 /* Careful to unlock before calling perror.  perror calls
233                                  * close, which calls our CB, which grabs the lock. */
234                                 uth_mutex_unlock(epoll_mtx);
235                                 perror("select epoll_ctl failed");
236                                 return -1;
237                         }
238                 }
239         }
240         /* Since we just added some FDs to our tracking set, we don't know if they
241          * are readable or not.  We'll only catch edge-triggered changes in the
242          * future.
243          *
244          * Similarly, it is legal to select on a readable FD even if you didn't
245          * consume all of the data yet; similarly for writers on non-full FDs.
246          *
247          * Additionally, since there is a global epoll set, we could have multiple
248          * threads epolling concurrently and one thread could consume the events
249          * that should wake another thread.  Also, keep in mind we could also have a
250          * single thread that selects multiple times on separate FD sets.
251          *
252          * Due to any of these cases, we need to check every FD this select call
253          * cares about (i.e. in an fd_set) to see if it is actionable.  We do it
254          * while holding the mutex to prevent other threads from consuming our epoll
255          * events. */
256         ret = 0;
257         FD_ZERO(&working_read_fds);
258         FD_ZERO(&working_write_fds);
259         FD_ZERO(&working_except_fds);
260         /* Note the helper sets bits in the working_ fd sets */
261         for (int i = 0; i < nfds; i++)
262                 ret += fd_set_actionable(i, readfds, writefds);
263         if (ret) {
264                 if (readfds)
265                         *readfds = working_read_fds;
266                 if (writefds)
267                         *writefds = working_write_fds;
268                 uth_mutex_unlock(epoll_mtx);
269                 return ret;
270         }
271         /* Need to check for up to FD_SETSIZE - nfds isn't the size of all FDs
272          * tracked; it's the size of only our current select call */
273         ep_results = malloc(sizeof(struct epoll_event) * FD_SETSIZE);
274         if (!ep_results) {
275                 uth_mutex_unlock(epoll_mtx);
276                 errno = ENOMEM;
277                 return -1;
278         }
279         ep_ret = epoll_wait(epoll_fd, ep_results, FD_SETSIZE, ep_timeout);
280         /* We need to hold the mtx during all of this processing since we're using
281          * the global working_ fds sets.  We can't modify the
282          * readfds/writefds/exceptfds until we're sure we are done. */
283         ret = 0;
284         /* Note that ret can be > ep_ret.  An FD that is both readable and writable
285          * counts as one event for epoll, but as two bits for select. */
286         for (int i = 0; i < ep_ret; i++) {
287                 ret += extract_bits_for_events(&ep_results[i], EPOLLIN | EPOLLHUP,
288                                                readfds, &working_read_fds);
289                 ret += extract_bits_for_events(&ep_results[i], EPOLLOUT | EPOLLHUP,
290                                                writefds, &working_write_fds);
291                 ret += extract_bits_for_events(&ep_results[i], EPOLLERR,
292                                                exceptfds, &working_except_fds);
293         }
294         free(ep_results);
295         if (ret) {
296                 if (readfds)
297                         *readfds = working_read_fds;
298                 if (writefds)
299                         *writefds = working_write_fds;
300                 if (exceptfds)
301                         *exceptfds = working_except_fds;
302         }
303         uth_mutex_unlock(epoll_mtx);
304         /* TODO: Consider updating timeval for non-timeouts.  It's not mandatory
305          * (POSIX). */
306         if (ret)
307                 return ret;
308         /* If we have no rets at this point, there are a few options: we could have
309          * timed out (if requested), or we could have consumed someone else's event.
310          * No one could have consumed our event, since we were the only epoller
311          * (while holding the mtx).  In the latter case, we'll need to try again,
312          * but with an updated timeout. */
313         if (timeout) {
314                 gettimeofday(end_tv, NULL);
315                 timersub(end_tv, start_tv, end_tv);     /* diff in end_tv */
316                 if (timercmp(timeout, end_tv, >))
317                         timersub(timeout, end_tv, timeout);
318                 else
319                         return 0;       /* select timed out */
320         }
321         goto loop;
322 }
323
324 int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
325             const struct timespec *timeout, const sigset_t *sigmask)
326 {
327         int ready;
328         sigset_t origmask;
329         struct timeval local_tv, *tv = &local_tv;
330
331         if (!timeout) {
332                 tv = 0;
333         } else {
334                 tv->tv_sec = timeout->tv_sec;
335                 tv->tv_usec = DIV_ROUND_UP(timeout->tv_nsec, 1000);
336         }
337         /* TODO: this is probably racy */
338         sigprocmask(SIG_SETMASK, sigmask, &origmask);
339         ready = select(nfds, readfds, writefds, exceptfds, tv);
340         sigprocmask(SIG_SETMASK, &origmask, NULL);
341         return ready;
342 }