Module: sip-router
Branch: andrei/tcp_tls_changes
Commit: d01e95b109558e283d91674858af959db9f83f59
URL:    
http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=d01e95b109558e283d91674858af959db9f83f59

Author: Andrei Pelinescu-Onciul <[email protected]>
Committer: Andrei Pelinescu-Onciul <[email protected]>
Date:   Fri Jun 18 15:01:01 2010 +0200

io_wait: fix kqueue io_wait_add & POLLIN

A "goto error" was placed outside the error handling "if",
resulting in any io_watch_add(), that tried to enable write
watching on a new FD, returning failure (fortunately this kind
of io_watch_add() usage doesn't happen very often, usually write
watch is enabled via io_watch_chg() on FDs already
io_watch_add()'ed for reading).

Only POLL_KQUEUE was affect by this bug, meaning the default on
all *bsd and darwin.

---

 io_wait.h |  105 +++++++++++++++++++++++++++++++++++++------------------------
 1 files changed, 64 insertions(+), 41 deletions(-)

diff --git a/io_wait.h b/io_wait.h
index 01df1e6..e04efd8 100644
--- a/io_wait.h
+++ b/io_wait.h
@@ -148,7 +148,9 @@ struct io_wait_handler{
        int flags;
        struct fd_map* fd_hash;
        int fd_no; /*  current index used in fd_array and the passed size for 
-                                  ep_array & kq_array*/
+                                  ep_array (for kq_array at least
+                                   max(twice the size, kq_changes_size) should 
be
+                                  be passed). */
        int max_fd_no; /* maximum fd no, is also the size of fd_array,
                                                       fd_hash  and ep_array*/
        /* common stuff for POLL, SIGIO_RT and SELECT
@@ -170,6 +172,7 @@ struct io_wait_handler{
        struct kevent* kq_array;   /* used for the eventlist*/
        struct kevent* kq_changes; /* used for the changelist */
        size_t kq_nchanges;
+       size_t kq_array_size;   /* array size */
        size_t kq_changes_size; /* size of the changes array */
 #endif
 #ifdef HAVE_DEVPOLL
@@ -259,34 +262,33 @@ static inline int kq_ev_change(io_wait_h* h, int fd, int 
filter, int flag,
 again:
                n=kevent(h->kq_fd, h->kq_changes, h->kq_nchanges, 0, 0, &tspec);
                if (unlikely(n == -1)){
-                       if (likely(errno == EBADF)) {
+                       if (unlikely(errno == EINTR)) goto again;
+                       else {
+                               /* for a detailed explanation of what follows 
see
+                                  io_wait_loop_kqueue EV_ERROR case */
+                               if (unlikely(!(errno == EBADF || errno == 
ENOENT)))
+                                       BUG("kq_ev_change: kevent flush changes 
failed"
+                                                       " (unexpected error): 
%s [%d]\n",
+                                                       strerror(errno), errno);
+                                       /* ignore error even if it's not a 
EBADF/ENOENT */
                                /* one of the file descriptors is bad, probably 
already
                                   closed => try to apply changes one-by-one */
                                for (r = 0; r < h->kq_nchanges; r++) {
 retry2:
                                        n = kevent(h->kq_fd, &h->kq_changes[r], 
1, 0, 0, &tspec);
                                        if (n==-1) {
-                                               if (errno == EBADF)
-                                                       continue; /* skip over 
it */
-                                               if (errno == EINTR)
+                                               if (unlikely(errno == EINTR))
                                                        goto retry2;
-                                               LOG(L_ERR, "ERROR: 
io_watch_add: kevent flush changes"
-                                                                       " 
failed: %s [%d]\n",
-                                                                               
strerror(errno), errno);
-                                               /* shift the array */
-                                               memmove(&h->kq_changes[0], 
&h->kq_changes[r+1],
-                                                                       
sizeof(h->kq_changes[0])*
-                                                                               
(h->kq_nchanges-r-1));
-                                               h->kq_nchanges-=(r+1);
-                                               return -1;
+                                       /* for a detailed explanation of what 
follows see
+                                               io_wait_loop_kqueue EV_ERROR 
case */
+                                               if (unlikely(!(errno == EBADF 
|| errno == ENOENT)))
+                                                       BUG("kq_ev_change: 
kevent flush changes failed:"
+                                                                       " 
(unexpected error) %s [%d] (%d/%d)\n",
+                                                                               
strerror(errno), errno,
+                                                                               
r, h->kq_nchanges);
+                                               continue; /* skip over it */
                                        }
                                }
-                       } else if (errno == EINTR) goto again;
-                       else {
-                               LOG(L_ERR, "ERROR: io_watch_add: kevent flush 
changes"
-                                               " failed: %s [%d]\n", 
strerror(errno), errno);
-                               h->kq_nchanges=0; /* reset changes array */
-                               return -1;
                        }
                }
                h->kq_nchanges=0; /* changes array is empty */
@@ -499,7 +501,7 @@ again2:
                case POLL_KQUEUE:
                        if (likely( events & POLLIN)){
                                if (unlikely(kq_ev_change(h, fd, EVFILT_READ, 
EV_ADD, e)==-1))
-                               goto error;
+                                       goto error;
                        }
                        if (unlikely( events & POLLOUT)){
                                if (unlikely(kq_ev_change(h, fd, EVFILT_WRITE, 
EV_ADD, e)==-1))
@@ -507,8 +509,8 @@ again2:
                                        if (likely(events & POLLIN)){
                                                kq_ev_change(h, fd, 
EVFILT_READ, EV_DELETE, 0);
                                        }
+                                       goto error;
                                }
-                               goto error;
                        }
                        break;
 #endif
@@ -1116,19 +1118,20 @@ inline static int io_wait_loop_kqueue(io_wait_h* h, int 
t, int repeat)
        do {
 again:
                n=kevent(h->kq_fd, h->kq_changes, apply_changes,  h->kq_array,
-                                       h->fd_no, &tspec);
+                                       h->kq_array_size, &tspec);
                if (unlikely(n==-1)){
-                       if (errno==EINTR) goto again; /* signal, ignore it */
-                       else if (errno==EBADF) {
+                       if (unlikely(errno==EINTR)) goto again; /* signal, 
ignore it */
+                       else {
+                               /* for a detailed explanation of what follows 
see below
+                                  the EV_ERROR case */
+                               if (unlikely(!(errno==EBADF || errno==ENOENT)))
+                                       BUG("io_wait_loop_kqueue: kevent: 
unexpected error"
+                                               " %s [%d]\n", strerror(errno), 
errno);
                                /* some of the FDs in kq_changes are bad 
(already closed)
                                   and there is not enough space in kq_array to 
return all
                                   of them back */
-                               apply_changes = h->fd_no;
+                               apply_changes = h->kq_array_size;
                                goto again;
-                       }else{
-                               LOG(L_ERR, "ERROR: io_wait_loop_kqueue: kevent:"
-                                               " %s [%d]\n", strerror(errno), 
errno);
-                               goto error;
                        }
                }
                /* remove applied changes */
@@ -1148,14 +1151,13 @@ again:
                                        r, n, h->kq_array[r].ident, 
(long)h->kq_array[r].udata,
                                        h->kq_array[r].flags);
 #endif
-                       if (unlikely((h->kq_array[r].flags & EV_ERROR) &&
-                                                       (h->kq_array[r].data == 
EBADF ||
-                                                        h->kq_array[r].udata 
== 0))){
+                       if (unlikely((h->kq_array[r].flags & EV_ERROR) ||
+                                                        h->kq_array[r].udata 
== 0)){
                                /* error in changes: we ignore it if it has to 
do with a
                                   bad fd or update==0. It can be caused by 
trying to remove an
                                   already closed fd: race between adding 
something to the
-                                  changes array, close() and applying the 
changes.
-                                  E.g. for ser tcp: tcp_main sends a fd to 
child fore reading
+                                  changes array, close() and applying the 
changes (EBADF).
+                                  E.g. for ser tcp: tcp_main sends a fd to 
child for reading
                                    => deletes it from the watched fds => the 
changes array
                                        will contain an EV_DELETE for it. 
Before the changes
                                        are applied (they are at the end of the 
main io_wait loop,
@@ -1163,6 +1165,16 @@ again:
                                        to tcp_main by a sender (send fail) is 
processed and causes
                                        the fd to be closed. When the changes 
are applied =>
                                        error for the EV_DELETE attempt of a 
closed fd.
+                                       Something similar can happen when a fd 
is scheduled
+                                       for removal, is close()'ed before being 
removed and
+                                       re-opened(a new sock. get the same fd). 
When the
+                                       watched fd changes will be applied the 
fd will be valid
+                                       (so no EBADF), but it's not already 
watch => ENOENT.
+                                       We report a BUG for the other errors 
(there's nothing
+                                       constructive we can do if we get an 
error we don't know 
+                                       how to handle), but apart from that we 
ignore it in the
+                                       idea that it is better apply the rest 
of the changes,
+                                       rather then dropping all of them.
                                */
                                /*
                                        example EV_ERROR for trying to delete a 
read watched fd,
@@ -1176,9 +1188,12 @@ again:
                                                udata = 0x0
                                        }
                                */
-                               if (h->kq_array[r].data != EBADF)
-                                       LOG(L_INFO, "INFO: io_wait_loop_kqueue: 
kevent error on "
-                                                       "fd %ld: %s [%ld]\n", 
(long)h->kq_array[r].ident,
+                               if (h->kq_array[r].data != EBADF &&
+                                               h->kq_array[r].data != ENOENT)
+                                       BUG("io_wait_loop_kqueue: kevent 
unexpected error on "
+                                                       "fd %ld udata %lx: %s 
[%ld]\n",
+                                                       
(long)h->kq_array[r].ident,
+                                                       
(long)h->kq_array[r].udata,
                                                        
strerror(h->kq_array[r].data),
                                                        
(long)h->kq_array[r].data);
                        }else{
@@ -1186,20 +1201,28 @@ again:
                                if (likely(h->kq_array[r].filter==EVFILT_READ)){
                                        revents=POLLIN |
                                                (((int)!(h->kq_array[r].flags & 
EV_EOF)-1)&POLLHUP) |
-                                               (((int)!(h->kq_array[r].flags & 
EV_ERROR)-1)&POLLERR);
+                                               (((int)!((h->kq_array[r].flags 
& EV_EOF) &&
+                                                                       
h->kq_array[r].fflags != 0) - 1)&POLLERR);
                                        while(fm->type && (fm->events & 
revents) && 
                                                        (handle_io(fm, revents, 
-1)>0) && repeat);
                                }else if (h->kq_array[r].filter==EVFILT_WRITE){
                                        revents=POLLOUT |
                                                (((int)!(h->kq_array[r].flags & 
EV_EOF)-1)&POLLHUP) |
-                                               (((int)!(h->kq_array[r].flags & 
EV_ERROR)-1)&POLLERR);
+                                               (((int)!((h->kq_array[r].flags 
& EV_EOF) &&
+                                                                       
h->kq_array[r].fflags != 0) - 1)&POLLERR);
                                        while(fm->type && (fm->events & 
revents) && 
                                                        (handle_io(fm, revents, 
-1)>0) && repeat);
+                               }else{
+                                       BUG("io_wait_loop_kqueue: unknown 
filter: kqueue: event "
+                                                       "%d/%d: fd=%d, 
filter=%d, flags=0x%x, fflags=0x%x,"
+                                                       " data=%lx, 
udata=%lx\n",
+                                       r, n, h->kq_array[r].ident, 
h->kq_array[r].filter,
+                                       h->kq_array[r].flags, 
h->kq_array[r].fflags, 
+                                       (long)h->kq_array[r].data, 
(long)h->kq_array[r].udata);
                                }
                        }
                }
        } while(unlikely(orig_changes));
-error:
        return n;
 }
 #endif


_______________________________________________
sr-dev mailing list
[email protected]
http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev

Reply via email to