vmm: Use VMM_CTL to set VMM flags (XCC)
[akaros.git] / user / iplib / epoll.c
index cfb78cf..9339a58 100644 (file)
@@ -33,6 +33,8 @@
 #include <parlib/ceq.h>
 #include <parlib/uthread.h>
 #include <parlib/timing.h>
+#include <parlib/slab.h>
+#include <parlib/assert.h>
 #include <sys/user_fd.h>
 #include <sys/close_cb.h>
 #include <stdio.h>
 /* Sanity check, so we can ID our own FDs */
 #define EPOLL_UFD_MAGIC                0xe9011
 
+/* Each waiter that uses a timeout will have its own structure for dealing with
+ * its timeout.
+ *
+ * TODO: (RCU/SLAB) it's not safe to reap the objects, until we sort out
+ * INDIRs and RCU-style grace periods.  Not a big deal, since the number of
+ * these is the number of threads that concurrently do epoll timeouts. */
+struct ep_alarm {
+       struct event_queue                      *alarm_evq;
+       struct syscall                          sysc;
+};
+
+static struct kmem_cache *ep_alarms_cache;
+
 struct epoll_ctlr {
        TAILQ_ENTRY(epoll_ctlr)         link;
        struct event_queue                      *ceq_evq;
-       struct event_queue                      *alarm_evq;
-       struct ceq                                      *ceq;   /* convenience pointer */
-       uth_mutex_t                                     mtx;
+       uth_mutex_t                                     *mtx;
        struct user_fd                          ufd;
 };
 
 TAILQ_HEAD(epoll_ctlrs, epoll_ctlr);
 static struct epoll_ctlrs all_ctlrs = TAILQ_HEAD_INITIALIZER(all_ctlrs);
-static uth_mutex_t ctlrs_mtx;
+static uth_mutex_t *ctlrs_mtx;
 
 /* There's some bookkeeping we need to maintain on every FD.  Right now, the FD
  * is the index into the CEQ event array, so we can just hook this into the user
@@ -208,7 +221,6 @@ static void epoll_close(struct user_fd *ufd)
        } while (nr_done < nr_tap_req);
        free(tap_reqs);
        ep_put_ceq_evq(ep->ceq_evq);
-       ep_put_alarm_evq(ep->alarm_evq);
        uth_mutex_lock(ctlrs_mtx);
        TAILQ_REMOVE(&all_ctlrs, ep, link);
        uth_mutex_unlock(ctlrs_mtx);
@@ -226,7 +238,6 @@ static int init_ep_ctlr(struct epoll_ctlr *ep, int size)
        /* Size is a hint for the CEQ concurrency.  We can actually handle as many
         * kernel FDs as is possible. */
        ep->ceq_evq = ep_get_ceq_evq(ROUNDUPPWR2(size));
-       ep->alarm_evq = ep_get_alarm_evq();
        return 0;
 }
 
@@ -243,20 +254,46 @@ static void epoll_fd_closed(int fd)
        uth_mutex_unlock(ctlrs_mtx);
 }
 
-static void epoll_init(void)
+static int ep_alarm_ctor(void *obj, void *priv, int flags)
+{
+       struct ep_alarm *ep_a = (struct ep_alarm*)obj;
+
+       ep_a->alarm_evq = ep_get_alarm_evq();
+       return 0;
+}
+
+static void ep_alarm_dtor(void *obj, void *priv)
+{
+       struct ep_alarm *ep_a = (struct ep_alarm*)obj;
+
+       /* TODO: (RCU/SLAB).  Somehow the slab allocator is trying to reap our
+        * objects.  Note that when we update userspace to use magazines, the dtor
+        * will fire earlier (when the object is given to the slab layer).  We'll
+        * need to be careful about the final freeing of the ev_q. */
+       panic("Epoll alarms should never be destroyed!");
+       ep_put_alarm_evq(ep_a->alarm_evq);
+}
+
+static void epoll_init(void *arg)
 {
        static struct close_cb epoll_close_cb = {.func = epoll_fd_closed};
 
        register_close_cb(&epoll_close_cb);
        ctlrs_mtx = uth_mutex_alloc();
+       ep_alarms_cache = kmem_cache_create("epoll alarms",
+                                           sizeof(struct ep_alarm),
+                                           __alignof__(sizeof(struct ep_alarm)), 0,
+                                           ep_alarm_ctor, ep_alarm_dtor, NULL);
+       assert(ep_alarms_cache);
 }
 
 int epoll_create(int size)
 {
        int fd;
        struct epoll_ctlr *ep;
+       static parlib_once_t once = PARLIB_ONCE_INIT;
 
-       run_once(epoll_init());
+       parlib_run_once(&once, epoll_init, NULL);
        /* good thing the arg is a signed int... */
        if (size < 0) {
                errno = EINVAL;
@@ -330,6 +367,11 @@ static int __epoll_ctl_add(struct epoll_ctlr *ep, int fd,
                werrstr("Epoll level-triggered not supported");
                return -1;
        }
+       if (event->events & EPOLLONESHOT) {
+               errno = EPERM;
+               werrstr("Epoll one-shot not supported");
+               return -1;
+       }
        /* 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 might
         * need to actually epoll on the listen_fd.  Further, we don't know yet
@@ -517,8 +559,8 @@ static int __epoll_wait(struct epoll_ctlr *ep, struct epoll_event *events,
        struct event_msg msg = {0};
        struct event_msg dummy_msg;
        struct event_queue *which_evq;
+       struct ep_alarm *ep_a;
        int nr_ret;
-       struct syscall sysc;
 
        nr_ret = __epoll_wait_poll(ep, events, maxevents);
        if (nr_ret)
@@ -527,16 +569,22 @@ static int __epoll_wait(struct epoll_ctlr *ep, struct epoll_event *events,
                return 0;
        /* From here on down, we're going to block until there is some activity */
        if (timeout != -1) {
-               syscall_async_evq(&sysc, ep->alarm_evq, SYS_block, timeout * 1000);
-               uth_blockon_evqs(&msg, &which_evq, 2, ep->ceq_evq, ep->alarm_evq);
-               if (which_evq == ep->alarm_evq)
+               ep_a = kmem_cache_alloc(ep_alarms_cache, 0);
+               assert(ep_a);
+               syscall_async_evq(&ep_a->sysc, ep_a->alarm_evq, SYS_block,
+                                 timeout * 1000);
+               uth_blockon_evqs(&msg, &which_evq, 2, ep->ceq_evq, ep_a->alarm_evq);
+               if (which_evq == ep_a->alarm_evq) {
+                       kmem_cache_free(ep_alarms_cache, ep_a);
                        return 0;
+               }
                /* The alarm sysc may or may not have finished yet.  This will force it
                 * to *start* to finish iff it is still a submitted syscall. */
-               sys_abort_sysc(&sysc);
+               sys_abort_sysc(&ep_a->sysc);
                /* But we still need to wait until the syscall completed.  Need a
                 * dummy msg, since we don't want to clobber the real msg. */
-               uth_blockon_evqs(&dummy_msg, 0, 1, ep->alarm_evq);
+               uth_blockon_evqs(&dummy_msg, 0, 1, ep_a->alarm_evq);
+               kmem_cache_free(ep_alarms_cache, ep_a);
        } else {
                uth_blockon_evqs(&msg, &which_evq, 1, ep->ceq_evq);
        }