]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: connection: centralize handling of nolinger in fd management
authorWilly Tarreau <w@1wt.eu>
Sun, 15 Dec 2013 13:19:38 +0000 (14:19 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 16 Dec 2013 01:23:52 +0000 (02:23 +0100)
Right now we see many places doing their own setsockopt(SO_LINGER).
Better only do it just before the close() in fd_delete(). For this
we add a new flag on the file descriptor, indicating if it's safe or
not to linger. If not (eg: after a connect()), then the setsockopt()
call is automatically performed before a close().

The flag automatically turns to safe when receiving a read0.

include/proto/connection.h
include/proto/fd.h
include/types/fd.h
src/checks.c
src/fd.c
src/frontend.c
src/proto_tcp.c
src/stream_interface.c

index a36906d18b395fdf8fa684e3dde5d135f572355b..be9d712f6b4c91e00ad54caac73138a9b0763c5d 100644 (file)
@@ -434,6 +434,11 @@ static inline void conn_sock_read0(struct connection *c)
 {
        c->flags |= CO_FL_SOCK_RD_SH;
        __conn_sock_stop_recv(c);
+       /* we don't risk keeping ports unusable if we found the
+        * zero from the other side.
+        */
+       if (c->flags & CO_FL_CTRL_READY)
+               fdtab[c->t.sock.fd].linger_risk = 0;
 }
 
 static inline void conn_data_read0(struct connection *c)
index 3b1365d86e4ad4bd6e4f758ba8ce9821f1fcdec7..7fe616e381b3787216aac45e19147130b9838a42 100644 (file)
@@ -226,6 +226,7 @@ static inline void fd_insert(int fd)
 {
        fdtab[fd].ev = 0;
        fdtab[fd].new = 1;
+       fdtab[fd].linger_risk = 0;
        if (fd + 1 > maxfd)
                maxfd = fd + 1;
 }
index 56a8c5c28bfab771422d12669e9839a1bc1c6448..701edfc59aa3e1fc197733bcb4712e134a59640c 100644 (file)
@@ -74,6 +74,7 @@ struct fdtab {
        unsigned char ev;                    /* event seen in return of poll() : FD_POLL_* */
        unsigned char new:1;                 /* 1 if this fd has just been created */
        unsigned char updated:1;             /* 1 if this fd is already in the update list */
+       unsigned char linger_risk:1;         /* 1 if we must kill lingering before closing */
 };
 
 /* less often used information */
index 7f195c9ea0db26fefc957820125c94391c5322dd..8014a668b6a501ef24d24a417ccc728f1a8c4889 100644 (file)
@@ -1443,10 +1443,9 @@ static int wake_srv_chk(struct connection *conn)
                /* We're here because nobody wants to handle the error, so we
                 * sure want to abort the hard way.
                 */
-               if ((conn->flags & CO_FL_CTRL_READY) && !(conn->flags & CO_FL_SOCK_RD_SH)) {
-                       if (conn->flags & CO_FL_WAIT_RD || !conn->ctrl->drain || !conn->ctrl->drain(conn->t.sock.fd))
-                               setsockopt(conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
-                                          (struct linger *) &nolinger, sizeof(struct linger));
+               if (conn_ctrl_ready(conn) && !(conn->flags & CO_FL_SOCK_RD_SH)) {
+                       if (!(conn->flags & CO_FL_WAIT_RD) && conn->ctrl->drain && conn->ctrl->drain(conn->t.sock.fd))
+                               fdtab[conn->t.sock.fd].linger_risk = 0;
                }
                conn_force_close(conn);
        }
@@ -1663,10 +1662,9 @@ static struct task *process_chk(struct task *t)
                         * as a failed response coupled with "observe layer7" caused the
                         * server state to be suddenly changed.
                         */
-                       if ((conn->flags & CO_FL_CTRL_READY) && !(conn->flags & CO_FL_SOCK_RD_SH)) {
-                               if (conn->flags & CO_FL_WAIT_RD || !conn->ctrl->drain || !conn->ctrl->drain(conn->t.sock.fd))
-                                       setsockopt(conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
-                                                  (struct linger *) &nolinger, sizeof(struct linger));
+                       if (conn_ctrl_ready(conn) && !(conn->flags & CO_FL_SOCK_RD_SH)) {
+                               if (!(conn->flags & CO_FL_WAIT_RD) && conn->ctrl->drain && conn->ctrl->drain(conn->t.sock.fd))
+                                       fdtab[conn->t.sock.fd].linger_risk = 0;
                        }
                        conn_force_close(conn);
                }
index 5df0b45dfdc7b05b7383338a25556cf445e00e2e..7e691146b59a743281a91469aae9451d598d8c4d 100644 (file)
--- a/src/fd.c
+++ b/src/fd.c
@@ -116,6 +116,11 @@ unsigned int *fd_updt = NULL;  // FD updates list
  */
 void fd_delete(int fd)
 {
+       if (fdtab[fd].linger_risk) {
+               /* this is generally set when connecting to servers */
+               setsockopt(fd, SOL_SOCKET, SO_LINGER,
+                          (struct linger *) &nolinger, sizeof(struct linger));
+       }
        if (cur_poller.clo)
                cur_poller.clo(fd);
 
index 591c1f21ad89ac5f40624602fe418adf8be6cd22..f94c6ab2b231a3704821d57b17de0d9d937f2d79 100644 (file)
@@ -85,8 +85,8 @@ int frontend_accept(struct session *s)
                                   (char *) &one, sizeof(one));
 
                if (s->fe->options & PR_O_TCP_NOLING)
-                       setsockopt(cfd, SOL_SOCKET, SO_LINGER,
-                                  (struct linger *) &nolinger, sizeof(struct linger));
+                       fdtab[cfd].linger_risk = 1;
+
 #if defined(TCP_MAXSEG)
                if (s->listener->maxseg < 0) {
                        /* we just want to reduce the current MSS by that value */
index 2b6fbc74516325eac220c423bc3e58fa0303dc3b..c1b0d7bdd6ec8ea4cb00bd0b1fb519a6cb1569ae 100644 (file)
@@ -475,6 +475,7 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
                conn->flags |= CO_FL_SEND_PROXY;
 
        conn_ctrl_init(conn);       /* registers the FD */
+       fdtab[fd].linger_risk = 1;  /* close hard if needed */
        conn_sock_want_send(conn);  /* for connect status */
 
        if (conn_xprt_init(conn) < 0) {
index f2650d3647ff773276c23855bad7bd186cd37519..d819ede827479305f6b2e6b209fc5d3ab8378eae 100644 (file)
@@ -812,10 +812,6 @@ static void stream_int_shutw_conn(struct stream_interface *si)
                        /* quick close, the socket is alredy shut anyway */
                }
                else if (si->flags & SI_FL_NOLINGER) {
-                       if (conn_ctrl_ready(conn)) {
-                               setsockopt(conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
-                                          (struct linger *) &nolinger, sizeof(struct linger));
-                       }
                        /* unclean data-layer shutdown */
                        if (conn->xprt && conn->xprt->shutw)
                                conn->xprt->shutw(conn, 0);
@@ -1285,12 +1281,6 @@ void stream_sock_read0(struct stream_interface *si)
 
        if (si->flags & SI_FL_NOHALF) {
                /* we want to immediately forward this close to the write side */
-               if (si->flags & SI_FL_NOLINGER) {
-                       si->flags &= ~SI_FL_NOLINGER;
-                       if (conn_ctrl_ready(conn))
-                               setsockopt(conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
-                                          (struct linger *) &nolinger, sizeof(struct linger));
-               }
                /* force flag on ssl to keep session in cache */
                if (conn->xprt->shutw)
                        conn->xprt->shutw(conn, 0);
@@ -1298,6 +1288,8 @@ void stream_sock_read0(struct stream_interface *si)
        }
 
        /* otherwise that's just a normal read shutdown */
+       if (conn_ctrl_ready(conn))
+               fdtab[conn->t.sock.fd].linger_risk = 0;
        __conn_data_stop_recv(conn);
        return;