[sr-dev] git:3.1: tcp: fix multiple hash removal attempts

Andrei Pelinescu-Onciul andrei at iptel.org
Thu May 5 00:47:40 CEST 2011


Module: sip-router
Branch: 3.1
Commit: 521b16ea814d68f9d872e2f6e1b0f081f285254b
URL:    http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=521b16ea814d68f9d872e2f6e1b0f081f285254b

Author: Andrei Pelinescu-Onciul <andrei at iptel.org>
Committer: Andrei Pelinescu-Onciul <andrei at iptel.org>
Date:   Thu May  5 00:13:36 2011 +0200

tcp: fix multiple hash removal attempts

In some corner cases (pending new tcp connection created in async
mode, other processes try to append data to it but fail and the
initial send fails after that) it was possible to attempt removing
the connection from the hash and the local timer multiple times.
(cherry picked from commit 76cb63c6f120c22a134babc8948611819b9081c3)

---

 tcp_main.c |   41 +++++++++++++++++++++++------------------
 1 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/tcp_main.c b/tcp_main.c
index 31fc0fa..0c9db6b 100644
--- a/tcp_main.c
+++ b/tcp_main.c
@@ -2167,20 +2167,19 @@ conn_wait_close:
 					su2a(&c->rcv.src_su, sizeof(c->rcv.src_su)),
 					fd, c->flags, strerror(errno), errno);
 	}
+	/* here the connection is for sure in the hash (tcp_main will not
+	   remove it because it's marked as PENDing) and the refcnt is at least
+	   2
+	 */
 	TCPCONN_LOCK;
-		if (c->flags & F_CONN_HASHED){
-			/* if some other parallel tcp_send did send CONN_ERROR to
-			 * tcp_main, the connection might be already detached */
-			_tcpconn_detach(c);
-			c->flags&=~F_CONN_HASHED;
-			TCPCONN_UNLOCK;
-			tcpconn_put(c);
-		}else
-			TCPCONN_UNLOCK;
+		_tcpconn_detach(c);
+		c->flags&=~F_CONN_HASHED;
+		tcpconn_put(c);
+	TCPCONN_UNLOCK;
 	/* dec refcnt -> mark it for destruction */
 	tcpconn_chld_put(c);
 	return n;
-#endif /* TCP_CONNET_WAIT */
+#endif /* TCP_CONNECT_WAIT */
 release_c:
 	tcpconn_chld_put(c); /* release c (dec refcnt & free on 0) */
 end_no_deref:
@@ -3594,16 +3593,22 @@ inline static int handle_ser_child(struct process_table* p, int fd_i)
 					tcpconn->flags);
 		case CONN_EOF: /* forced EOF after full send, due to send flags */
 #ifdef TCP_CONNECT_WAIT
-			/* if the connection is pending => it might be on the way of
-			 * reaching tcp_main (e.g. CONN_NEW_COMPLETE or
-			 *  CONN_NEW_PENDING_WRITE) =>  it cannot be destroyed here */
-			if ( !(tcpconn->flags & F_CONN_PENDING) &&
-					tcpconn_try_unhash(tcpconn) )
-				tcpconn_put(tcpconn);
-#else /* ! TCP_CONNECT_WAIT */
+			/* if the connection is marked as pending => it might be on
+			 *  the way of reaching tcp_main (e.g. CONN_NEW_COMPLETE or
+			 *  CONN_NEW_PENDING_WRITE) =>  it cannot be destroyed here,
+			 *  it will be destroyed on CONN_NEW_COMPLETE /
+			 *  CONN_NEW_PENDING_WRITE or in the send error case by the
+			 *  sender process */
+			if (unlikely(tcpconn->flags & F_CONN_PENDING)) {
+				if (tcpconn_put(tcpconn))
+					tcpconn_destroy(tcpconn);
+				/* no need for io_watch_del(), if PENDING it should not
+				   be watched for anything in tcp_main */
+				break;
+			}
+#endif /* TCP_CONNECT_WAIT */
 			if ( tcpconn_try_unhash(tcpconn) )
 				tcpconn_put(tcpconn);
-#endif /* TCP_CONNECT_WAIT */
 			if ( ((tcpconn->flags & (F_CONN_WRITE_W|F_CONN_READ_W)) ) &&
 					(tcpconn->s!=-1)){
 				io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING);




More information about the sr-dev mailing list