epoll: Split out the guts of ctl_add and ctl_del
This way we don't do sock lookups when we know they will fail.
Previously, whenever we recursively called __epoll_ctl_add() on the listen
FD, that sock lookup would always fail.
It's a little hard to see from the diff, but this only moves the actual
ctl_add and ctl_del functionality into separate helpers.
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/user/iplib/epoll.c b/user/iplib/epoll.c
index 8890eb1..f415cb2 100644
--- a/user/iplib/epoll.c
+++ b/user/iplib/epoll.c
@@ -351,13 +351,50 @@
}
}
-static int __epoll_ctl_add(struct epoll_ctlr *ep, int fd,
- struct epoll_event *event)
+static int __epoll_ctl_add_raw(struct epoll_ctlr *ep, int fd,
+ struct epoll_event *event)
{
struct ceq_event *ceq_ev;
struct ep_fd_data *ep_fd;
struct fd_tap_req tap_req = {0};
- int ret, filter, sock_listen_fd, sock_ctl_fd;
+ int ret, filter;
+
+ ceq_ev = ep_get_ceq_ev(ep, fd);
+ if (!ceq_ev) {
+ errno = ENOMEM;
+ werrstr("Epoll set cannot grow yet!");
+ return -1;
+ }
+ ep_fd = (struct ep_fd_data*)ceq_ev->user_data;
+ if (ep_fd) {
+ errno = EEXIST;
+ return -1;
+ }
+ tap_req.fd = fd;
+ tap_req.cmd = FDTAP_CMD_ADD;
+ /* EPOLLHUP is implicitly set for all epolls. */
+ filter = ep_events_to_taps(event->events | EPOLLHUP);
+ tap_req.filter = filter;
+ tap_req.ev_q = ep->ceq_evq;
+ tap_req.ev_id = fd; /* using FD as the CEQ ID */
+ ret = sys_tap_fds(&tap_req, 1);
+ if (ret != 1)
+ return -1;
+ ep_fd = malloc(sizeof(struct ep_fd_data));
+ ep_fd->fd = fd;
+ ep_fd->filter = filter;
+ ep_fd->ep_event = *event;
+ ep_fd->ep_event.events |= EPOLLHUP;
+ ceq_ev->user_data = (uint64_t)ep_fd;
+ fire_existing_events(fd, ep_fd->ep_event.events, ep->ceq_evq);
+ return 0;
+}
+
+static int __epoll_ctl_add(struct epoll_ctlr *ep, int fd,
+ struct epoll_event *event)
+{
+ struct fd_tap_req tap_req = {0};
+ int ret, sock_listen_fd, sock_ctl_fd;
struct epoll_event listen_event;
/* Only support ET. Also, we just ignore EPOLLONESHOT. That might work,
@@ -390,65 +427,20 @@
if (sock_listen_fd >= 0) {
listen_event.events = EPOLLET | EPOLLIN | EPOLLHUP;
listen_event.data = event->data;
- ret = __epoll_ctl_add(ep, sock_listen_fd, &listen_event);
+ ret = __epoll_ctl_add_raw(ep, sock_listen_fd, &listen_event);
if (ret < 0)
return ret;
}
- ceq_ev = ep_get_ceq_ev(ep, fd);
- if (!ceq_ev) {
- errno = ENOMEM;
- werrstr("Epoll set cannot grow yet!");
- return -1;
- }
- ep_fd = (struct ep_fd_data*)ceq_ev->user_data;
- if (ep_fd) {
- errno = EEXIST;
- return -1;
- }
- tap_req.fd = fd;
- tap_req.cmd = FDTAP_CMD_ADD;
- /* EPOLLHUP is implicitly set for all epolls. */
- filter = ep_events_to_taps(event->events | EPOLLHUP);
- tap_req.filter = filter;
- tap_req.ev_q = ep->ceq_evq;
- tap_req.ev_id = fd; /* using FD as the CEQ ID */
- ret = sys_tap_fds(&tap_req, 1);
- if (ret != 1)
- return -1;
- ep_fd = malloc(sizeof(struct ep_fd_data));
- ep_fd->fd = fd;
- ep_fd->filter = filter;
- ep_fd->ep_event = *event;
- ep_fd->ep_event.events |= EPOLLHUP;
- ceq_ev->user_data = (uint64_t)ep_fd;
- fire_existing_events(fd, ep_fd->ep_event.events, ep->ceq_evq);
- return 0;
+ return __epoll_ctl_add_raw(ep, fd, event);
}
-static int __epoll_ctl_del(struct epoll_ctlr *ep, int fd,
- struct epoll_event *event)
+static int __epoll_ctl_del_raw(struct epoll_ctlr *ep, int fd,
+ struct epoll_event *event)
{
struct ceq_event *ceq_ev;
struct ep_fd_data *ep_fd;
struct fd_tap_req tap_req = {0};
- int ret, sock_listen_fd, sock_ctl_fd;
- /* If we were dealing with a socket shim FD, we tapped both the listen and
- * the data file and need to untap both of them.
- *
- * We could be called from a close_cb, and we already closed the listen FD.
- * In that case, we don't want to try and open it. If the listen FD isn't
- * open, then we know it isn't in an epoll set. We also know the data FD
- * isn't epolled either, since we always epoll both FDs for rocks. */
- _sock_lookup_rock_fds(fd, FALSE, &sock_listen_fd, &sock_ctl_fd);
- if (sock_listen_fd >= 0) {
- /* It's possible to fail here. Even though we tapped it already, if the
- * deletion was triggered from close callbacks, it's possible for the
- * sock_listen_fd to be closed first, which would have triggered an
- * epoll_ctl_del. When we get around to closing the Rock FD, the listen
- * FD was already closed. */
- __epoll_ctl_del(ep, sock_listen_fd, event);
- }
ceq_ev = ep_get_ceq_ev(ep, fd);
if (!ceq_ev) {
errno = ENOENT;
@@ -470,6 +462,30 @@
return 0;
}
+static int __epoll_ctl_del(struct epoll_ctlr *ep, int fd,
+ struct epoll_event *event)
+{
+ int sock_listen_fd, sock_ctl_fd;
+
+ /* If we were dealing with a socket shim FD, we tapped both the listen and
+ * the data file and need to untap both of them.
+ *
+ * We could be called from a close_cb, and we already closed the listen FD.
+ * In that case, we don't want to try and open it. If the listen FD isn't
+ * open, then we know it isn't in an epoll set. We also know the data FD
+ * isn't epolled either, since we always epoll both FDs for rocks. */
+ _sock_lookup_rock_fds(fd, FALSE, &sock_listen_fd, &sock_ctl_fd);
+ if (sock_listen_fd >= 0) {
+ /* It's possible to fail here. Even though we tapped it already, if the
+ * deletion was triggered from close callbacks, it's possible for the
+ * sock_listen_fd to be closed first, which would have triggered an
+ * epoll_ctl_del. When we get around to closing the Rock FD, the listen
+ * FD was already closed. */
+ __epoll_ctl_del_raw(ep, sock_listen_fd, event);
+ }
+ return __epoll_ctl_del_raw(ep, fd, event);
+}
+
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
{
int ret;