Index: uspace/srv/net/tl/tcp/conn.c
===================================================================
--- uspace/srv/net/tl/tcp/conn.c	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
+++ uspace/srv/net/tl/tcp/conn.c	(revision 0edaf0f65b8b68a7f2f5e4adf75b32af8299101d)
@@ -56,4 +56,5 @@
 
 LIST_INITIALIZE(conn_list);
+FIBRIL_MUTEX_INITIALIZE(conn_list_lock);
 
 static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg);
@@ -81,6 +82,10 @@
 		goto error;
 
+	fibril_mutex_initialize(&conn->lock);
+
+	/* One for the user, one for not being in closed state */
+	atomic_set(&conn->refcnt, 2);
+
 	/* Allocate receive buffer */
-	fibril_mutex_initialize(&conn->rcv_buf_lock);
 	fibril_condvar_initialize(&conn->rcv_buf_cv);
 	conn->rcv_buf_size = RCV_BUF_SIZE;
@@ -93,5 +98,4 @@
 
 	/** Allocate send buffer */
-	fibril_mutex_initialize(&conn->snd_buf_lock);
 	fibril_condvar_initialize(&conn->snd_buf_cv);
 	conn->snd_buf_size = SND_BUF_SIZE;
@@ -115,5 +119,4 @@
 
 	/* Connection state change signalling */
-	fibril_mutex_initialize(&conn->cstate_lock);
 	fibril_condvar_initialize(&conn->cstate_cv);
 
@@ -146,7 +149,12 @@
 /** Destroy connection structure.
  *
- * Connection structure should be destroyed when both of two conditions are
- * met: (1) user has deleted the connection and (2) the connection has entered
- * closed state.
+ * Connection structure should be destroyed when the folowing condtitions
+ * are met:
+ * (1) user has deleted the connection
+ * (2) the connection has entered closed state
+ * (3) nobody is holding references to the connection
+ *
+ * This happens when @a conn->refcnt is zero as we count (1) and (2)
+ * as special references.
  *
  * @param conn		Connection
@@ -166,4 +174,30 @@
 }
 
+/** Add reference to connection.
+ *
+ * Increase connection reference count by one.
+ *
+ * @param conn		Connection
+ */
+void tcp_conn_addref(tcp_conn_t *conn)
+{
+	log_msg(LVL_DEBUG, "%s: tcp_conn_addref(%p)", conn->name, conn);
+	atomic_inc(&conn->refcnt);
+}
+
+/** Remove reference from connection.
+ *
+ * Decrease connection reference count by one.
+ *
+ * @param conn		Connection
+ */
+void tcp_conn_delref(tcp_conn_t *conn)
+{
+	log_msg(LVL_DEBUG, "%s: tcp_conn_delref(%p)", conn->name, conn);
+
+	if (atomic_predec(&conn->refcnt) == 0)
+		tcp_conn_free(conn);
+}
+
 /** Delete connection.
  *
@@ -175,13 +209,8 @@
 void tcp_conn_delete(tcp_conn_t *conn)
 {
-	fibril_mutex_lock(&conn->cstate_lock);
-	conn->deleted = true;
-
-	if (conn->cstate == st_closed) {
-		fibril_mutex_unlock(&conn->cstate_lock);
-		tcp_conn_free(conn);
-	} else {
-		fibril_mutex_unlock(&conn->cstate_lock);
-	}
+	log_msg(LVL_DEBUG, "%s: tcp_conn_delete(%p)", conn->name, conn);
+
+	assert(conn->deleted == false);
+	tcp_conn_delref(conn);
 }
 
@@ -192,5 +221,7 @@
 void tcp_conn_add(tcp_conn_t *conn)
 {
+	fibril_mutex_lock(&conn_list_lock);
 	list_append(&conn->link, &conn_list);
+	fibril_mutex_unlock(&conn_list_lock);
 }
 
@@ -201,18 +232,21 @@
 void tcp_conn_remove(tcp_conn_t *conn)
 {
+	fibril_mutex_lock(&conn_list_lock);
 	list_remove(&conn->link);
+	fibril_mutex_unlock(&conn_list_lock);
 }
 
 static void tcp_conn_state_set(tcp_conn_t *conn, tcp_cstate_t nstate)
 {
-	fibril_mutex_lock(&conn->cstate_lock);
+	tcp_cstate_t old_state;
+
+	old_state = conn->cstate;
 	conn->cstate = nstate;
 	fibril_condvar_broadcast(&conn->cstate_cv);
 
-	if (nstate == st_closed && conn->deleted) {
-		fibril_mutex_unlock(&conn->cstate_lock);
-		tcp_conn_free(conn);
-	} else {
-		fibril_mutex_unlock(&conn->cstate_lock);
+	assert(old_state != st_closed);
+	if (nstate == st_closed) {
+		/* Drop one reference for now being in closed state */
+		tcp_conn_delref(conn);
 	}
 }
@@ -302,11 +336,14 @@
  * A connection is uniquely identified by a socket pair. Look up our
  * connection map and return connection structure based on socket pair.
+ * The connection reference count is bumped by one.
  *
  * @param sp	Socket pair
  * @return	Connection structure or NULL if not found.
  */
-tcp_conn_t *tcp_conn_find(tcp_sockpair_t *sp)
+tcp_conn_t *tcp_conn_find_ref(tcp_sockpair_t *sp)
 {
 	log_msg(LVL_DEBUG, "tcp_conn_find(%p)", sp);
+
+	fibril_mutex_lock(&conn_list_lock);
 
 	list_foreach(conn_list, link) {
@@ -317,8 +354,11 @@
 		    csp->local.addr.ipv4, csp->local.port);
 		if (tcp_sockpair_match(sp, csp)) {
+			tcp_conn_addref(conn);
+			fibril_mutex_unlock(&conn_list_lock);
 			return conn;
 		}
 	}
 
+	fibril_mutex_unlock(&conn_list_lock);
 	return NULL;
 }
@@ -910,6 +950,4 @@
 	tcp_conn_trim_seg_to_wnd(conn, seg);
 
-	fibril_mutex_lock(&conn->rcv_buf_lock);
-
 	/* Determine how many bytes to copy */
 	text_size = tcp_segment_text_size(seg);
@@ -923,5 +961,4 @@
 	/* Signal to the receive function that new data has arrived */
 	fibril_condvar_broadcast(&conn->rcv_buf_cv);
-	fibril_mutex_unlock(&conn->rcv_buf_lock);
 
 	log_msg(LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
@@ -1013,8 +1050,6 @@
 
 		/* Add FIN to the receive buffer */
-		fibril_mutex_lock(&conn->rcv_buf_lock);
 		conn->rcv_buf_fin = true;
 		fibril_condvar_broadcast(&conn->rcv_buf_cv);
-		fibril_mutex_unlock(&conn->rcv_buf_lock);
 
 		tcp_segment_delete(seg);
@@ -1125,6 +1160,10 @@
 	log_msg(LVL_DEBUG, "tw_timeout_func(%p)", conn);
 
+	fibril_mutex_lock(&conn->lock);
+
 	if (conn->cstate == st_closed) {
 		log_msg(LVL_DEBUG, "Connection already closed.");
+		fibril_mutex_unlock(&conn->lock);
+		tcp_conn_delref(conn);
 		return;
 	}
@@ -1133,4 +1172,7 @@
 	tcp_conn_remove(conn);
 	tcp_conn_state_set(conn, st_closed);
+
+	fibril_mutex_unlock(&conn->lock);
+	tcp_conn_delref(conn);
 }
 
@@ -1141,4 +1183,5 @@
 void tcp_conn_tw_timer_set(tcp_conn_t *conn)
 {
+	tcp_conn_addref(conn);
 	fibril_timer_set(conn->tw_timer, TIME_WAIT_TIMEOUT, tw_timeout_func,
 	    (void *)conn);
@@ -1151,5 +1194,6 @@
 void tcp_conn_tw_timer_clear(tcp_conn_t *conn)
 {
-	fibril_timer_clear(conn->tw_timer);
+	if (fibril_timer_clear(conn->tw_timer) == fts_active)
+		tcp_conn_delref(conn);
 }
 
Index: uspace/srv/net/tl/tcp/conn.h
===================================================================
--- uspace/srv/net/tl/tcp/conn.h	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
+++ uspace/srv/net/tl/tcp/conn.h	(revision 0edaf0f65b8b68a7f2f5e4adf75b32af8299101d)
@@ -46,5 +46,7 @@
 extern void tcp_conn_fin_sent(tcp_conn_t *);
 extern void tcp_conn_ack_of_fin_rcvd(tcp_conn_t *);
-extern tcp_conn_t *tcp_conn_find(tcp_sockpair_t *);
+extern tcp_conn_t *tcp_conn_find_ref(tcp_sockpair_t *);
+extern void tcp_conn_addref(tcp_conn_t *);
+extern void tcp_conn_delref(tcp_conn_t *);
 extern bool tcp_conn_got_syn(tcp_conn_t *);
 extern void tcp_conn_segment_arrived(tcp_conn_t *, tcp_segment_t *);
Index: uspace/srv/net/tl/tcp/tcp_type.h
===================================================================
--- uspace/srv/net/tl/tcp/tcp_type.h	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
+++ uspace/srv/net/tl/tcp/tcp_type.h	(revision 0edaf0f65b8b68a7f2f5e4adf75b32af8299101d)
@@ -162,4 +162,9 @@
 	acpass_t ap;
 
+	/** Protects access to connection structure */
+	fibril_mutex_t lock;
+	/** Reference count */
+	atomic_t refcnt;
+
 	/** Connection state */
 	tcp_cstate_t cstate;
@@ -168,6 +173,4 @@
 	/** True if connection was deleted by user */
 	bool deleted;
-	/** Protects @c cstate */
-	fibril_mutex_t cstate_lock;
 	/** Signalled when @c cstate changes */
 	fibril_condvar_t cstate_cv;
@@ -193,6 +196,4 @@
 	/** Receive buffer contains FIN */
 	bool rcv_buf_fin;
-	/** Receive buffer lock */
-	fibril_mutex_t rcv_buf_lock;
 	/** Receive buffer CV. Broadcast when new data is inserted */
 	fibril_condvar_t rcv_buf_cv;
@@ -206,6 +207,4 @@
 	/** Send buffer contains FIN */
 	bool snd_buf_fin;
-	/** Send buffer lock */
-	fibril_mutex_t snd_buf_lock;
 	/** Send buffer CV. Broadcast when space is made available in buffer */
 	fibril_condvar_t snd_buf_cv;
Index: uspace/srv/net/tl/tcp/tqueue.c
===================================================================
--- uspace/srv/net/tl/tcp/tqueue.c	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
+++ uspace/srv/net/tl/tcp/tqueue.c	(revision 0edaf0f65b8b68a7f2f5e4adf75b32af8299101d)
@@ -167,6 +167,4 @@
 	log_msg(LVL_DEBUG, "%s: tcp_tqueue_new_data()", conn->name);
 
-	fibril_mutex_lock(&conn->snd_buf_lock);
-
 	/* Number of free sequence numbers in send window */
 	avail_wnd = (conn->snd_una + conn->snd_wnd) - conn->snd_nxt;
@@ -178,8 +176,6 @@
 	    xfer_seqlen);
 
-	if (xfer_seqlen == 0) {
-		fibril_mutex_unlock(&conn->snd_buf_lock);
-		return;
-	}
+	if (xfer_seqlen == 0)
+		return;
 
 	/* XXX Do not always send immediately */
@@ -198,5 +194,4 @@
 	seg = tcp_segment_make_data(ctrl, conn->snd_buf, data_size);
 	if (seg == NULL) {
-		fibril_mutex_unlock(&conn->snd_buf_lock);
 		log_msg(LVL_ERROR, "Memory allocation failure.");
 		return;
@@ -212,5 +207,4 @@
 
 	fibril_condvar_broadcast(&conn->snd_buf_cv);
-	fibril_mutex_unlock(&conn->snd_buf_lock);
 
 	if (send_fin)
@@ -323,6 +317,10 @@
 	log_msg(LVL_DEBUG, "### %s: retransmit_timeout_func(%p)", conn->name, conn);
 
+	fibril_mutex_lock(&conn->lock);
+
 	if (conn->cstate == st_closed) {
 		log_msg(LVL_DEBUG, "Connection already closed.");
+		fibril_mutex_unlock(&conn->lock);
+		tcp_conn_delref(conn);
 		return;
 	}
@@ -331,4 +329,6 @@
 	if (link == NULL) {
 		log_msg(LVL_DEBUG, "Nothing to retransmit");
+		fibril_mutex_unlock(&conn->lock);
+		tcp_conn_delref(conn);
 		return;
 	}
@@ -339,4 +339,6 @@
 	if (rt_seg == NULL) {
 		log_msg(LVL_ERROR, "Memory allocation failed.");
+		fibril_mutex_unlock(&conn->lock);
+		tcp_conn_delref(conn);
 		/* XXX Handle properly */
 		return;
@@ -348,4 +350,7 @@
 	/* Reset retransmission timer */
 	tcp_tqueue_timer_set(tqe->conn);
+
+	fibril_mutex_unlock(&conn->lock);
+	tcp_conn_delref(conn);
 }
 
@@ -355,5 +360,8 @@
 	log_msg(LVL_DEBUG, "### %s: tcp_tqueue_timer_set()", conn->name);
 
-	(void) retransmit_timeout_func;
+	/* Clear first to make sure we update refcnt correctly */
+	tcp_tqueue_timer_clear(conn);
+
+	tcp_conn_addref(conn);
 	fibril_timer_set(conn->retransmit.timer, RETRANSMIT_TIMEOUT,
 	    retransmit_timeout_func, (void *) conn);
@@ -365,5 +373,6 @@
 	log_msg(LVL_DEBUG, "### %s: tcp_tqueue_timer_clear()", conn->name);
 
-	fibril_timer_clear(conn->retransmit.timer);
+	if (fibril_timer_clear(conn->retransmit.timer) == fts_active)
+		tcp_conn_delref(conn);
 }
 
Index: uspace/srv/net/tl/tcp/ucall.c
===================================================================
--- uspace/srv/net/tl/tcp/ucall.c	(revision 7a8c1c4e5b8ffa09d9d43a81d222f21253b15586)
+++ uspace/srv/net/tl/tcp/ucall.c	(revision 0edaf0f65b8b68a7f2f5e4adf75b32af8299101d)
@@ -83,9 +83,9 @@
 	/* Wait for connection to be established or reset */
 	log_msg(LVL_DEBUG, "tcp_uc_open: Wait for connection.");
-	fibril_mutex_lock(&nconn->cstate_lock);
+	fibril_mutex_lock(&nconn->lock);
 	while (nconn->cstate == st_listen ||
 	    nconn->cstate == st_syn_sent ||
 	    nconn->cstate == st_syn_received) {
-		fibril_condvar_wait(&nconn->cstate_cv, &nconn->cstate_lock);
+		fibril_condvar_wait(&nconn->cstate_cv, &nconn->lock);
 	}
 
@@ -93,9 +93,9 @@
 		log_msg(LVL_DEBUG, "tcp_uc_open: Connection was reset.");
 		assert(nconn->cstate == st_closed);
-		fibril_mutex_unlock(&nconn->cstate_lock);
+		fibril_mutex_unlock(&nconn->lock);
 		return TCP_ERESET;
 	}
 
-	fibril_mutex_unlock(&nconn->cstate_lock);
+	fibril_mutex_unlock(&nconn->lock);
 	log_msg(LVL_DEBUG, "tcp_uc_open: Connection was established.");
 
@@ -113,6 +113,10 @@
 	log_msg(LVL_DEBUG, "%s: tcp_uc_send()", conn->name);
 
-	if (conn->cstate == st_closed)
+	fibril_mutex_lock(&conn->lock);
+
+	if (conn->cstate == st_closed) {
+		fibril_mutex_unlock(&conn->lock);
 		return TCP_ENOTEXIST;
+	}
 
 	if (conn->cstate == st_listen) {
@@ -121,8 +125,7 @@
 	}
 
-	fibril_mutex_lock(&conn->snd_buf_lock);
 
 	if (conn->snd_buf_fin) {
-		fibril_mutex_unlock(&conn->snd_buf_lock);
+		fibril_mutex_unlock(&conn->lock);
 		return TCP_ECLOSING;
 	}
@@ -133,11 +136,10 @@
 			log_msg(LVL_DEBUG, "%s: buf_free == 0, waiting.",
 			    conn->name);
-			fibril_condvar_wait(&conn->snd_buf_cv,
-			    &conn->snd_buf_lock);
+			fibril_condvar_wait(&conn->snd_buf_cv, &conn->lock);
 			buf_free = conn->snd_buf_size - conn->snd_buf_used;
 		}
 
 		if (conn->reset) {
-			fibril_mutex_unlock(&conn->snd_buf_lock);
+			fibril_mutex_unlock(&conn->lock);
 			return TCP_ERESET;
 		}
@@ -151,11 +153,9 @@
 		size -= xfer_size;
 
-		fibril_mutex_unlock(&conn->snd_buf_lock);
 		tcp_tqueue_new_data(conn);
-		fibril_mutex_lock(&conn->snd_buf_lock);
-	}
-
-	fibril_mutex_unlock(&conn->snd_buf_lock);
+	}
+
 	tcp_tqueue_new_data(conn);
+	fibril_mutex_unlock(&conn->lock);
 
 	return TCP_EOK;
@@ -170,18 +170,18 @@
 	log_msg(LVL_DEBUG, "%s: tcp_uc_receive()", conn->name);
 
-	if (conn->cstate == st_closed)
+	fibril_mutex_lock(&conn->lock);
+
+	if (conn->cstate == st_closed) {
+		fibril_mutex_unlock(&conn->lock);
 		return TCP_ENOTEXIST;
-
-	fibril_mutex_lock(&conn->rcv_buf_lock);
+	}
 
 	/* Wait for data to become available */
 	while (conn->rcv_buf_used == 0 && !conn->rcv_buf_fin && !conn->reset) {
 		log_msg(LVL_DEBUG, "tcp_uc_receive() - wait for data");
-		fibril_condvar_wait(&conn->rcv_buf_cv, &conn->rcv_buf_lock);
+		fibril_condvar_wait(&conn->rcv_buf_cv, &conn->lock);
 	}
 
 	if (conn->rcv_buf_used == 0) {
-		fibril_mutex_unlock(&conn->rcv_buf_lock);
-
 		*rcvd = 0;
 		*xflags = 0;
@@ -189,8 +189,10 @@
 		if (conn->rcv_buf_fin) {
 			/* End of data, peer closed connection */
+			fibril_mutex_unlock(&conn->lock);
 			return TCP_ECLOSING;
 		} else {
 			/* Connection was reset */
 			assert(conn->reset);
+			fibril_mutex_unlock(&conn->lock);
 			return TCP_ERESET;
 		}
@@ -208,6 +210,4 @@
 	conn->rcv_wnd += xfer_size;
 
-	fibril_mutex_unlock(&conn->rcv_buf_lock);
-
 	/* TODO */
 	*xflags = 0;
@@ -219,4 +219,6 @@
 	    conn->name, xfer_size);
 
+	fibril_mutex_unlock(&conn->lock);
+
 	return TCP_EOK;
 }
@@ -227,13 +229,20 @@
 	log_msg(LVL_DEBUG, "%s: tcp_uc_close()", conn->name);
 
-	if (conn->cstate == st_closed)
+	fibril_mutex_lock(&conn->lock);
+
+	if (conn->cstate == st_closed) {
+		fibril_mutex_unlock(&conn->lock);
 		return TCP_ENOTEXIST;
-
-	if (conn->snd_buf_fin)
+	}
+
+	if (conn->snd_buf_fin) {
+		fibril_mutex_unlock(&conn->lock);
 		return TCP_ECLOSING;
+	}
 
 	conn->snd_buf_fin = true;
 	tcp_tqueue_new_data(conn);
 
+	fibril_mutex_unlock(&conn->lock);
 	return TCP_EOK;
 }
@@ -276,21 +285,32 @@
 	    sp->local.addr.ipv4, sp->local.port);
 
-	conn = tcp_conn_find(sp);
-	if (conn != NULL && conn->cstate != st_closed) {
-		if (conn->ident.foreign.addr.ipv4 == TCP_IPV4_ANY)
-			conn->ident.foreign.addr.ipv4 = sp->foreign.addr.ipv4;
-		if (conn->ident.foreign.port == TCP_PORT_ANY)
-			conn->ident.foreign.port = sp->foreign.port;
-		if (conn->ident.local.addr.ipv4 == TCP_IPV4_ANY)
-			conn->ident.local.addr.ipv4 = sp->local.addr.ipv4;
-
-		tcp_conn_segment_arrived(conn, seg);
-	} else {
-		if (conn == NULL)
-			log_msg(LVL_WARN, "No connection found.");
-		else
-			log_msg(LVL_WARN, "Connection is closed.");
+	conn = tcp_conn_find_ref(sp);
+	if (conn == NULL) {
+		log_msg(LVL_WARN, "No connection found.");
 		tcp_unexpected_segment(sp, seg);
-	}
+		return;
+	}
+
+	fibril_mutex_lock(&conn->lock);
+
+	if (conn->cstate == st_closed) {
+		log_msg(LVL_WARN, "Connection is closed.");
+		tcp_unexpected_segment(sp, seg);
+		fibril_mutex_unlock(&conn->lock);
+		tcp_conn_delref(conn);
+		return;
+	}
+
+	if (conn->ident.foreign.addr.ipv4 == TCP_IPV4_ANY)
+		conn->ident.foreign.addr.ipv4 = sp->foreign.addr.ipv4;
+	if (conn->ident.foreign.port == TCP_PORT_ANY)
+		conn->ident.foreign.port = sp->foreign.port;
+	if (conn->ident.local.addr.ipv4 == TCP_IPV4_ANY)
+		conn->ident.local.addr.ipv4 = sp->local.addr.ipv4;
+
+	tcp_conn_segment_arrived(conn, seg);
+
+	fibril_mutex_unlock(&conn->lock);
+	tcp_conn_delref(conn);
 }
 
