]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: listener: improve detection of non-working accept4()
authorWilly Tarreau <w@1wt.eu>
Fri, 31 Jan 2014 18:40:19 +0000 (19:40 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 31 Jan 2014 18:40:19 +0000 (19:40 +0100)
On ARM, glibc does not implement accept4() and simply returns ENOSYS
which was not caught as a reason to fall back to accept(), resulting
in a spinning process since poll() would call again.

Let's change the error detection mechanism to save the broken status
of the syscall into a local variable that is used to fall back to the
legacy accept().

In addition to this, since the code was becoming a bit messy, the
accept4() was removed, so now the fallback code and the legacy code
are the same. This will also increase bug report accuracy if needed.

This is 1.5-specific, no backport is needed.

src/listener.c
src/session.c

index 1ce35de8cde9edc41038917d9af6efe4bed5c1ba..05bb7435aa84ef50853cb65d6490c35e42f52a2f 100644 (file)
@@ -257,6 +257,9 @@ void listener_accept(int fd)
        int max_accept = l->maxaccept ? l->maxaccept : 1;
        int cfd;
        int ret;
+#ifdef USE_ACCEPT4
+       static int accept4_broken;
+#endif
 
        if (unlikely(l->nbconn >= l->maxconn)) {
                listener_full(l);
@@ -346,15 +349,17 @@ void listener_accept(int fd)
                }
 
 #ifdef USE_ACCEPT4
-               cfd = accept4(fd, (struct sockaddr *)&addr, &laddr, SOCK_NONBLOCK);
-               if (unlikely(cfd == -1 && errno == EINVAL)) {
-                       /* unsupported syscall, fallback to normal accept()+fcntl() */
+               /* only call accept4() if it's known to be safe, otherwise
+                * fallback to the legacy accept() + fcntl().
+                */
+               if (unlikely(accept4_broken ||
+                       ((cfd = accept4(fd, (struct sockaddr *)&addr, &laddr, SOCK_NONBLOCK)) == -1 &&
+                       (errno == ENOSYS || errno == EINVAL || errno == EBADF) &&
+                       (accept4_broken = 1))))
+#endif
                        if ((cfd = accept(fd, (struct sockaddr *)&addr, &laddr)) != -1)
                                fcntl(cfd, F_SETFL, O_NONBLOCK);
-               }
-#else
-               cfd = accept(fd, (struct sockaddr *)&addr, &laddr);
-#endif
+
                if (unlikely(cfd == -1)) {
                        switch (errno) {
                        case EAGAIN:
index 1faf0c347635d8cbc656cffeda8a2cb643db76e8..aae0c69f8fb0b365c7385b7d65c20899449e03d1 100644 (file)
@@ -148,14 +148,6 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr)
                goto out_free_session;
        }
 
-#ifndef USE_ACCEPT4
-       /* Adjust some socket options if the connection was accepted by a plain
-        * accept() syscall.
-        */
-       if (unlikely(fcntl(cfd, F_SETFL, O_NONBLOCK) == -1))
-               goto out_free_session;
-#endif
-
        /* monitor-net and health mode are processed immediately after TCP
         * connection rules. This way it's possible to block them, but they
         * never use the lower data layers, they send directly over the socket,