Module: sip-router
Branch: master
Commit: 76cb63c6f120c22a134babc8948611819b9081c3
URL:
http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=76cb63c…
Author: Andrei Pelinescu-Onciul <andrei(a)iptel.org>
Committer: Andrei Pelinescu-Onciul <andrei(a)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.
---
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);