Browse code

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.

Andrei Pelinescu-Onciul authored on 04/05/2011 22:13:36
Showing 1 changed files
... ...
@@ -2167,20 +2167,19 @@ conn_wait_close:
2167 2167
 					su2a(&c->rcv.src_su, sizeof(c->rcv.src_su)),
2168 2168
 					fd, c->flags, strerror(errno), errno);
2169 2169
 	}
2170
+	/* here the connection is for sure in the hash (tcp_main will not
2171
+	   remove it because it's marked as PENDing) and the refcnt is at least
2172
+	   2
2173
+	 */
2170 2174
 	TCPCONN_LOCK;
2171
-		if (c->flags & F_CONN_HASHED){
2172
-			/* if some other parallel tcp_send did send CONN_ERROR to
2173
-			 * tcp_main, the connection might be already detached */
2174
-			_tcpconn_detach(c);
2175
-			c->flags&=~F_CONN_HASHED;
2176
-			TCPCONN_UNLOCK;
2177
-			tcpconn_put(c);
2178
-		}else
2179
-			TCPCONN_UNLOCK;
2175
+		_tcpconn_detach(c);
2176
+		c->flags&=~F_CONN_HASHED;
2177
+		tcpconn_put(c);
2178
+	TCPCONN_UNLOCK;
2180 2179
 	/* dec refcnt -> mark it for destruction */
2181 2180
 	tcpconn_chld_put(c);
2182 2181
 	return n;
2183
-#endif /* TCP_CONNET_WAIT */
2182
+#endif /* TCP_CONNECT_WAIT */
2184 2183
 release_c:
2185 2184
 	tcpconn_chld_put(c); /* release c (dec refcnt & free on 0) */
2186 2185
 end_no_deref:
... ...
@@ -3594,16 +3593,22 @@ inline static int handle_ser_child(struct process_table* p, int fd_i)
3594 3594
 					tcpconn->flags);
3595 3595
 		case CONN_EOF: /* forced EOF after full send, due to send flags */
3596 3596
 #ifdef TCP_CONNECT_WAIT
3597
-			/* if the connection is pending => it might be on the way of
3598
-			 * reaching tcp_main (e.g. CONN_NEW_COMPLETE or
3599
-			 *  CONN_NEW_PENDING_WRITE) =>  it cannot be destroyed here */
3600
-			if ( !(tcpconn->flags & F_CONN_PENDING) &&
3601
-					tcpconn_try_unhash(tcpconn) )
3602
-				tcpconn_put(tcpconn);
3603
-#else /* ! TCP_CONNECT_WAIT */
3597
+			/* if the connection is marked as pending => it might be on
3598
+			 *  the way of reaching tcp_main (e.g. CONN_NEW_COMPLETE or
3599
+			 *  CONN_NEW_PENDING_WRITE) =>  it cannot be destroyed here,
3600
+			 *  it will be destroyed on CONN_NEW_COMPLETE /
3601
+			 *  CONN_NEW_PENDING_WRITE or in the send error case by the
3602
+			 *  sender process */
3603
+			if (unlikely(tcpconn->flags & F_CONN_PENDING)) {
3604
+				if (tcpconn_put(tcpconn))
3605
+					tcpconn_destroy(tcpconn);
3606
+				/* no need for io_watch_del(), if PENDING it should not
3607
+				   be watched for anything in tcp_main */
3608
+				break;
3609
+			}
3610
+#endif /* TCP_CONNECT_WAIT */
3604 3611
 			if ( tcpconn_try_unhash(tcpconn) )
3605 3612
 				tcpconn_put(tcpconn);
3606
-#endif /* TCP_CONNECT_WAIT */
3607 3613
 			if ( ((tcpconn->flags & (F_CONN_WRITE_W|F_CONN_READ_W)) ) &&
3608 3614
 					(tcpconn->s!=-1)){
3609 3615
 				io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING);