tevent: Start to fix the epoll backend to support 2 fd events on the same fd correctly.
authorJeremy Allison <jra@samba.org>
Thu, 14 Feb 2013 21:50:56 +0000 (13:50 -0800)
committerStefan Metzmacher <metze@samba.org>
Fri, 1 Mar 2013 18:54:43 +0000 (19:54 +0100)
Add a utility function epoll_add_multiplex_fd() and
a new flag EPOLL_ADDITIONAL_FD_FLAG_HAS_MPX.

This will be called by epoll_add_event() to merge two
fde events with the same file descriptor.

Signed-off-by: Jeremy Allison <jra@samba.org>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
lib/tevent/tevent_epoll.c

index adea677096b0e8bde64c36a08f3a79e6d5165134..c2b743dcd1f3f3b53fe0e729d26098f94ce3bfda 100644 (file)
@@ -4,7 +4,8 @@
    main select loop and event handling - epoll implementation
 
    Copyright (C) Andrew Tridgell       2003-2005
-   Copyright (C) Stefan Metzmacher     2005-2009
+   Copyright (C) Stefan Metzmacher     2005-2013
+   Copyright (C) Jeremy Allison                2013
 
      ** NOTE! The following LGPL license applies to the tevent
      ** library. This does NOT imply that all of Samba is released
@@ -48,6 +49,7 @@ struct epoll_event_context {
 #define EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT     (1<<0)
 #define EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR  (1<<1)
 #define EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR     (1<<2)
+#define EPOLL_ADDITIONAL_FD_FLAG_HAS_MPX       (1<<3)
 
 #ifdef TEST_PANIC_FALLBACK
 
@@ -263,6 +265,99 @@ static void epoll_check_reopen(struct epoll_event_context *epoll_ev)
        epoll_ev->panic_state = NULL;
 }
 
+/*
+ epoll cannot add the same file descriptor twice, once
+ with read, once with write which is allowed by the
+ tevent backend. Multiplex the existing fde, flag it
+ as such so we can search for the correct fde on
+ event triggering.
+*/
+
+static int epoll_add_multiplex_fd(struct epoll_event_context *epoll_ev,
+                                 struct tevent_fd *add_fde)
+{
+       struct epoll_event event;
+       struct tevent_fd *mpx_fde;
+       int ret;
+
+       /* Find the existing fde that caused the EEXIST error. */
+       for (mpx_fde = epoll_ev->ev->fd_events; mpx_fde; mpx_fde = mpx_fde->next) {
+               if (mpx_fde->fd != add_fde->fd) {
+                       continue;
+               }
+
+               if (mpx_fde == add_fde) {
+                       continue;
+               }
+
+               break;
+       }
+       if (mpx_fde == NULL) {
+               tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
+                            "can't find multiplex fde for fd[%d]",
+                            add_fde->fd);
+               return -1;
+       }
+
+       if (mpx_fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_MPX) {
+               /* Logic error. Can't have more than 2 multiplexed fde's. */
+               tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
+                            "multiplex fde for fd[%d] is already multiplexed\n",
+                            mpx_fde->fd);
+               return -1;
+       }
+
+       /*
+        * The multiplex fde must have the same fd, and also
+        * already have an epoll event attached.
+        */
+       if (!(mpx_fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT)) {
+               /* Logic error. Can't have more than 2 multiplexed fde's. */
+               tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL,
+                            "multiplex fde for fd[%d] has no event\n",
+                            mpx_fde->fd);
+               return -1;
+       }
+
+       /* Modify the mpx_fde to add in the new flags. */
+       ZERO_STRUCT(event);
+       event.events = epoll_map_flags(mpx_fde->flags);
+       event.events |= epoll_map_flags(add_fde->flags);
+       event.data.ptr = mpx_fde;
+       ret = epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_MOD, mpx_fde->fd, &event);
+       if (ret != 0 && errno == EBADF) {
+               tevent_debug(epoll_ev->ev, TEVENT_DEBUG_ERROR,
+                            "EPOLL_CTL_MOD EBADF for "
+                            "add_fde[%p] mpx_fde[%p] fd[%d] - disabling\n",
+                            add_fde, mpx_fde, add_fde->fd);
+               DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde);
+               mpx_fde->event_ctx = NULL;
+               DLIST_REMOVE(epoll_ev->ev->fd_events, add_fde);
+               add_fde->event_ctx = NULL;
+               return 0;
+       } else if (ret != 0) {
+               return ret;
+       }
+
+       /*
+        * Make each fde->additional_data pointers point at each other
+        * so we can look them up from each other. They are now paired.
+        */
+       mpx_fde->additional_data = (struct tevent_fd *)add_fde;
+       add_fde->additional_data = (struct tevent_fd *)mpx_fde;
+
+       /* Now flag both fde's as being multiplexed. */
+       mpx_fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_MPX;
+       add_fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_MPX;
+
+       /* we need to keep the GOT_ERROR flag */
+       if (mpx_fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR) {
+               add_fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR;
+       }
+
+       return 0;
+}
+
 /*
  add the epoll event to the given fd_event
 */