Browse Source

[tcp] Truncate TCP window to prevent future packet discards

Whenever memory pressure causes a queued packet to be discarded (and
so retransmitted), reduce the maximum TCP window to a size that would
have prevented the discard.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 12 years ago
parent
commit
a5d16a91af
1 changed files with 20 additions and 3 deletions
  1. 20
    3
      src/net/tcp.c

+ 20
- 3
src/net/tcp.c View File

@@ -97,6 +97,8 @@ struct tcp_connection {
97 97
 	 * Equivalent to Rcv.Wind.Scale in RFC 1323 terminology
98 98
 	 */
99 99
 	uint8_t rcv_win_scale;
100
+	/** Maximum receive window */
101
+	uint32_t max_rcv_win;
100 102
 
101 103
 	/** Transmit queue */
102 104
 	struct list_head tx_queue;
@@ -295,6 +297,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer,
295 297
 	tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN );
296 298
 	tcp_dump_state ( tcp );
297 299
 	tcp->snd_seq = random();
300
+	tcp->max_rcv_win = TCP_MAX_WINDOW_SIZE;
298 301
 	INIT_LIST_HEAD ( &tcp->tx_queue );
299 302
 	INIT_LIST_HEAD ( &tcp->rx_queue );
300 303
 	memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) );
@@ -557,9 +560,7 @@ static int tcp_xmit ( struct tcp_connection *tcp ) {
557 560
 	tcp_process_tx_queue ( tcp, len, iobuf, 0 );
558 561
 
559 562
 	/* Expand receive window if possible */
560
-	max_rcv_win = ( ( freemem * 3 ) / 4 );
561
-	if ( max_rcv_win > TCP_MAX_WINDOW_SIZE )
562
-		max_rcv_win = TCP_MAX_WINDOW_SIZE;
563
+	max_rcv_win = tcp->max_rcv_win;
563 564
 	app_win = xfer_window ( &tcp->xfer );
564 565
 	if ( max_rcv_win > app_win )
565 566
 		max_rcv_win = app_win;
@@ -1299,13 +1300,29 @@ struct tcpip_protocol tcp_protocol __tcpip_protocol = {
1299 1300
 static unsigned int tcp_discard ( void ) {
1300 1301
 	struct tcp_connection *tcp;
1301 1302
 	struct io_buffer *iobuf;
1303
+	struct tcp_rx_queued_header *tcpqhdr;
1304
+	uint32_t max_win;
1302 1305
 	unsigned int discarded = 0;
1303 1306
 
1304 1307
 	/* Try to drop one queued RX packet from each connection */
1305 1308
 	list_for_each_entry ( tcp, &tcp_conns, list ) {
1306 1309
 		list_for_each_entry_reverse ( iobuf, &tcp->rx_queue, list ) {
1310
+
1311
+			/* Limit window to prevent future discards */
1312
+			tcpqhdr = iobuf->data;
1313
+			max_win = ( tcpqhdr->seq - tcp->rcv_ack );
1314
+			if ( max_win < tcp->max_rcv_win ) {
1315
+				DBGC ( tcp, "TCP %p reducing maximum window "
1316
+				       "from %d to %d\n",
1317
+				       tcp, tcp->max_rcv_win, max_win );
1318
+				tcp->max_rcv_win = max_win;
1319
+			}
1320
+
1321
+			/* Remove packet from queue */
1307 1322
 			list_del ( &iobuf->list );
1308 1323
 			free_iob ( iobuf );
1324
+
1325
+			/* Report discard */
1309 1326
 			discarded++;
1310 1327
 			break;
1311 1328
 		}

Loading…
Cancel
Save