VMM: Sync halting GPCs and interrupt injection
[akaros.git] / user / iplib / epoll.c
index ef7486c..87fdae5 100644 (file)
  *     exec and flags in struct user_fd.
  *     - EPOLL_CTL_MOD is just a DEL then an ADD.  There might be races associated
  *     with that.
- *     - If you close a tracked FD without removing it from the epoll set, the
- *     kernel will turn off the FD tap.  You may still have an epoll event that was
- *     concurrently sent.  Likewise, that FD may be used again by your program, and
- *     if you add *that* one to another epoll set before removing it from the
- *     current one, weird things may happen (like having two epoll ctlrs turning on
- *     and off taps).
  *     - epoll_pwait is probably racy.
  *     - You can't dup an epoll fd (same as other user FDs).
  *     - If you add a BSD socket FD to an epoll set before calling listen(), you'll
 #include <parlib/uthread.h>
 #include <parlib/timing.h>
 #include <sys/user_fd.h>
+#include <sys/close_cb.h>
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
 #include <malloc.h>
 #include <sys/queue.h>
+#include <sys/plan9_helpers.h>
 
 /* Sanity check, so we can ID our own FDs */
 #define EPOLL_UFD_MAGIC                0xe9011
@@ -73,7 +69,6 @@ struct ep_fd_data {
        struct epoll_event                      ep_event;
        int                                                     fd;
        int                                                     filter;
-       int                                                     sock_listen_fd;
 };
 
 /* Converts epoll events to FD taps. */
@@ -194,14 +189,6 @@ static void epoll_close(struct user_fd *ufd)
                ep_fd_i = (struct ep_fd_data*)ceq_ev_i->user_data;
                if (!ep_fd_i)
                        continue;
-               if (ep_fd_i->sock_listen_fd >= 0) {
-                       /* This tap is using a listen_fd, opened by __epoll_ctl_add, so the
-                        * user doesn't know about this FD.  We need to remove the tap and
-                        * close the FD; the kernel will remove the tap when we close it. */
-                       close(ep_fd_i->sock_listen_fd);
-                       free(ep_fd_i);
-                       continue;
-               }
                tap_req_i = &tap_reqs[nr_tap_req++];
                tap_req_i->fd = i;
                tap_req_i->cmd = FDTAP_CMD_REM;
@@ -236,8 +223,24 @@ static int init_ep_ctlr(struct epoll_ctlr *ep, int size)
        return 0;
 }
 
+static void epoll_fd_closed(int fd)
+{
+       struct epoll_ctlr *ep;
+
+       /* Lockless peek, avoid locking for every close() */
+       if (TAILQ_EMPTY(&all_ctlrs))
+               return;
+       uth_mutex_lock(ctlrs_mtx);
+       TAILQ_FOREACH(ep, &all_ctlrs, link)
+               epoll_ctl(ep->ufd.fd, EPOLL_CTL_DEL, fd, 0);
+       uth_mutex_unlock(ctlrs_mtx);
+}
+
 static void epoll_init(void)
 {
+       static struct close_cb epoll_close_cb = {.func = epoll_fd_closed};
+
+       register_close_cb(&epoll_close_cb);
        ctlrs_mtx = uth_mutex_alloc();
 }
 
@@ -291,26 +294,12 @@ static int __epoll_ctl_add(struct epoll_ctlr *ep, int fd,
        }
        /* The sockets-to-plan9 networking shims are a bit inconvenient.  The user
         * asked us to epoll on an FD, but that FD is actually a Qdata FD.  We need
-        * to actually epoll on the listen_fd.  We'll store this in the ep_fd, so
-        * that later on we can close it.
+        * to actually epoll on the listen_fd.
         *
         * As far as tracking the FD goes for epoll_wait() reporting, if the app
         * wants to track the FD they think we are using, then they already passed
-        * that in event->data.
-        *
-        * But before we get too far, we need to make sure we aren't already tapping
-        * this FD's listener (hence the lookup).
-        *
-        * This all assumes that this socket is only added to one epoll set at a
-        * time.  The _sock calls are racy, and once one epoller set up a listen_fd
-        * in the Rock, we'll think that it was us. */
-       extern int _sock_lookup_listen_fd(int sock_fd); /* in glibc */
-       extern int _sock_get_listen_fd(int sock_fd);
-       if (_sock_lookup_listen_fd(fd) >= 0) {
-               errno = EEXIST;
-               return -1;
-       }
-       sock_listen_fd = _sock_get_listen_fd(fd);
+        * that in event->data. */
+       sock_listen_fd = _sock_lookup_listen_fd(fd);
        if (sock_listen_fd >= 0)
                fd = sock_listen_fd;
        ceq_ev = ep_get_ceq_ev(ep, fd);
@@ -339,7 +328,6 @@ static int __epoll_ctl_add(struct epoll_ctlr *ep, int fd,
        ep_fd->filter = filter;
        ep_fd->ep_event = *event;
        ep_fd->ep_event.events |= EPOLLHUP;
-       ep_fd->sock_listen_fd = sock_listen_fd;
        ceq_ev->user_data = (uint64_t)ep_fd;
        return 0;
 }
@@ -354,7 +342,6 @@ static int __epoll_ctl_del(struct epoll_ctlr *ep, int fd,
 
        /* They could be asking to clear an epoll for a listener.  We need to remove
         * the tap for the real FD we tapped */
-       extern int _sock_lookup_listen_fd(int sock_fd); /* in glibc */
        sock_listen_fd = _sock_lookup_listen_fd(fd);
        if (sock_listen_fd >= 0)
                fd = sock_listen_fd;
@@ -375,11 +362,6 @@ static int __epoll_ctl_del(struct epoll_ctlr *ep, int fd,
         * has already closed and the kernel removed the tap. */
        sys_tap_fds(&tap_req, 1);
        ceq_ev->user_data = 0;
-       assert(ep_fd->sock_listen_fd == sock_listen_fd);
-       if (ep_fd->sock_listen_fd >= 0) {
-               assert(ep_fd->sock_listen_fd == sock_listen_fd);
-               close(ep_fd->sock_listen_fd);
-       }
        free(ep_fd);
        return 0;
 }