[gPXE-devel] [PATCHv4 01/10] [tcp] Receive and Close flow adjustment

Guo-Fu Tseng cooldavid at cooldavid.org
Sat Jul 17 21:26:06 EDT 2010


From: Guo-Fu Tseng <cooldavid at cooldavid.org>

The original Receive flow of TCP stack pass the data to upper layer
while the TCP state transition and response still in progress. But when
upper layer received some specific data, it might send data through the
current TCP connection, or close the current TCP connection. This issue
makes the TCP state hard to maintain.

After we separated the xfer interface closing routines from tcp_close,
we have the opportunity to handle the TCP state, and shutdown the
deliver interface at different but more suitable place.

While receiving data, we deliver the data to upper layer after we FULLY
updated/handled the TCP state. So that any actions that the upper layer
might do to the TCP stack would be at safer timing.

Signed-off-by: Guo-Fu Tseng <cooldavid at cooldavid.org>
---
 src/net/tcp.c |   73 ++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 46 insertions(+), 27 deletions(-)

diff --git a/src/net/tcp.c b/src/net/tcp.c
index f9fd409..dc66267 100644
--- a/src/net/tcp.c
+++ b/src/net/tcp.c
@@ -260,18 +260,13 @@ static int tcp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
  * @v tcp		TCP connection
  * @v rc		Reason for close
  *
- * Closes the data transfer interface.  If the TCP state machine is in
- * a suitable state, the connection will be deleted.
+ * If the TCP state machine is in a suitable state, the
+ * connection will be deleted.
  */
-static void tcp_close ( struct tcp_connection *tcp, int rc ) {
+static void tcp_close ( struct tcp_connection *tcp ) {
 	struct io_buffer *iobuf;
 	struct io_buffer *tmp;
 
-	/* Close data transfer interface */
-	xfer_nullify ( &tcp->xfer );
-	xfer_close ( &tcp->xfer, rc );
-	tcp->xfer_closed = 1;
-
 	/* If we are in CLOSED, or have otherwise not yet received a
 	 * SYN (i.e. we are in LISTEN or SYN_SENT), just delete the
 	 * connection.
@@ -310,6 +305,21 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) {
 	}
 }
 
+/**
+ * Shutdown TCP xfer interface
+ *
+ * @v tcp		TCP connection
+ * @v rc		Reason for close
+ *
+ * Closes the data transfer interface.
+ */
+static void tcp_xfer_shutdown ( struct tcp_connection *tcp, int rc ) {
+	/* Close data transfer interface */
+	xfer_nullify ( &tcp->xfer );
+	xfer_close ( &tcp->xfer, rc );
+	tcp->xfer_closed = 1;
+}
+
 /***************************************************************************
  *
  * Transmit data path
@@ -540,7 +550,8 @@ static void tcp_expired ( struct retry_timer *timer, int over ) {
 		 */
 		tcp->tcp_state = TCP_CLOSED;
 		tcp_dump_state ( tcp );
-		tcp_close ( tcp, -ETIMEDOUT );
+		tcp_close ( tcp );
+		tcp_xfer_shutdown ( tcp, -ETIMEDOUT );
 	} else {
 		/* Otherwise, retransmit the packet */
 		tcp_xmit ( tcp, 0 );
@@ -790,37 +801,30 @@ static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
  * @v tcp		TCP connection
  * @v seq		SEQ value (in host-endian order)
  * @v iobuf		I/O buffer
- * @ret rc		Return status code
+ * @ret rc		Return I/O buffer with data, if any
  *
  * This function takes ownership of the I/O buffer.
  */
-static int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq,
-			 struct io_buffer *iobuf ) {
+static struct io_buffer* tcp_rx_data ( struct tcp_connection *tcp,
+					uint32_t seq,
+					struct io_buffer *iobuf ) {
 	uint32_t already_rcvd;
 	uint32_t len;
-	int rc;
 
 	/* Ignore duplicate or out-of-order data */
 	already_rcvd = ( tcp->rcv_ack - seq );
 	len = iob_len ( iobuf );
 	if ( already_rcvd >= len ) {
 		free_iob ( iobuf );
-		return 0;
+		return NULL;
 	}
 	iob_pull ( iobuf, already_rcvd );
 	len -= already_rcvd;
 
-	/* Deliver data to application */
-	if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 ) {
-		DBGC ( tcp, "TCP %p could not deliver %08x..%08x: %s\n",
-		       tcp, seq, ( seq + len ), strerror ( rc ) );
-		return rc;
-	}
-
 	/* Acknowledge new data */
 	tcp_rx_seq ( tcp, len );
 
-	return 0;
+	return iobuf;
 }
 
 /**
@@ -841,7 +845,7 @@ static int tcp_rx_fin ( struct tcp_connection *tcp, uint32_t seq ) {
 	tcp_rx_seq ( tcp, 1 );
 
 	/* Close connection */
-	tcp_close ( tcp, 0 );
+	tcp_close ( tcp );
 
 	return 0;
 }
@@ -871,7 +875,8 @@ static int tcp_rx_rst ( struct tcp_connection *tcp, uint32_t seq ) {
 	/* Abort connection */
 	tcp->tcp_state = TCP_CLOSED;
 	tcp_dump_state ( tcp );
-	tcp_close ( tcp, -ECONNRESET );
+	tcp_close ( tcp );
+	tcp_xfer_shutdown ( tcp, -ECONNRESET );
 
 	DBGC ( tcp, "TCP %p connection reset by peer\n", tcp );
 	return -ECONNRESET;
@@ -983,7 +988,7 @@ static int tcp_rx ( struct io_buffer *iobuf,
 	}
 
 	/* Handle new data, if any */
-	tcp_rx_data ( tcp, seq, iob_disown ( iobuf ) );
+	iobuf = tcp_rx_data ( tcp, seq, iobuf );
 	seq += len;
 
 	/* Handle FIN, if present */
@@ -1013,6 +1018,17 @@ static int tcp_rx ( struct io_buffer *iobuf,
 	tcp_xmit ( tcp, ( ( start_seq != seq ) ||
 			  ( ( seq - tcp->rcv_ack ) > tcp->rcv_win ) ) );
 
+	/* Deliver data to application, if any */
+	if ( iobuf && ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 ) {
+		DBGC ( tcp, "TCP %p could not deliver: %s\n",
+		       tcp, strerror ( rc ) );
+	}
+
+	/* Shutdown xfer interface if received FIN from remote peer */
+	if ( TCP_FLAGS_RCVD ( tcp->tcp_state ) & TCP_FIN ) {
+		tcp_xfer_shutdown ( tcp, 0 );
+	}
+
 	/* If this packet was the last we expect to receive, set up
 	 * timer to expire and cause the connection to be freed.
 	 */
@@ -1052,8 +1068,11 @@ static void tcp_xfer_close ( struct xfer_interface *xfer, int rc ) {
 	struct tcp_connection *tcp =
 		container_of ( xfer, struct tcp_connection, xfer );
 
-	/* Close data transfer interface */
-	tcp_close ( tcp, rc );
+	/* Close TCP Connection */
+	tcp_close ( tcp );
+
+	/* Shutdown xfer interface */
+	tcp_xfer_shutdown ( tcp, rc );
 
 	/* Transmit FIN, if possible */
 	tcp_xmit ( tcp, 0 );
-- 
1.7.1



More information about the gPXE-devel mailing list