Browse Source

[tcp] Gracefully close connections during shutdown

We currently do not wait for a received FIN before exiting to boot a
loaded OS.  In the common case of booting from an HTTP server, this
means that the TCP connection is left consuming resources on the
server side: the server will retransmit the FIN several times before
giving up.

Fix by initiating a graceful close of all TCP connections and waiting
(for up to one second) for all connections to finish closing
gracefully (i.e. for the outgoing FIN to have been sent and ACKed, and
for the incoming FIN to have been received and ACKed at least once).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
38afcc51ea
2 changed files with 63 additions and 1 deletions
  1. 7
    0
      src/include/ipxe/tcp.h
  2. 56
    1
      src/net/tcp.c

+ 7
- 0
src/include/ipxe/tcp.h View File

420
 	return ( ( seq - start ) < len );
420
 	return ( ( seq - start ) < len );
421
 }
421
 }
422
 
422
 
423
+/** TCP finish wait time
424
+ *
425
+ * Currently set to one second, since we should not allow a slowly
426
+ * responding server to substantially delay a call to shutdown().
427
+ */
428
+#define TCP_FINISH_TIMEOUT ( 1 * TICKS_PER_SEC )
429
+
423
 extern struct tcpip_protocol tcp_protocol __tcpip_protocol;
430
 extern struct tcpip_protocol tcp_protocol __tcpip_protocol;
424
 
431
 
425
 #endif /* _IPXE_TCP_H */
432
 #endif /* _IPXE_TCP_H */

+ 56
- 1
src/net/tcp.c View File

1501
 	.discard = tcp_discard,
1501
 	.discard = tcp_discard,
1502
 };
1502
 };
1503
 
1503
 
1504
+/**
1505
+ * Find first TCP connection that has not yet been closed
1506
+ *
1507
+ * @ret tcp		First unclosed connection, or NULL
1508
+ */
1509
+static struct tcp_connection * tcp_first_unclosed ( void ) {
1510
+	struct tcp_connection *tcp;
1511
+
1512
+	/* Find first connection which has not yet been closed */
1513
+	list_for_each_entry ( tcp, &tcp_conns, list ) {
1514
+		if ( ! ( tcp->flags & TCP_XFER_CLOSED ) )
1515
+			return tcp;
1516
+	}
1517
+	return NULL;
1518
+}
1519
+
1520
+/**
1521
+ * Find first TCP connection that has not yet finished all operations
1522
+ *
1523
+ * @ret tcp		First unfinished connection, or NULL
1524
+ */
1525
+static struct tcp_connection * tcp_first_unfinished ( void ) {
1526
+	struct tcp_connection *tcp;
1527
+
1528
+	/* Find first connection which has not yet closed gracefully,
1529
+	 * or which still has a pending transmission (e.g. to ACK the
1530
+	 * received FIN).
1531
+	 */
1532
+	list_for_each_entry ( tcp, &tcp_conns, list ) {
1533
+		if ( ( ! TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) ||
1534
+		     process_running ( &tcp->process ) ) {
1535
+			return tcp;
1536
+		}
1537
+	}
1538
+	return NULL;
1539
+}
1540
+
1504
 /**
1541
 /**
1505
  * Shut down all TCP connections
1542
  * Shut down all TCP connections
1506
  *
1543
  *
1507
  */
1544
  */
1508
 static void tcp_shutdown ( int booting __unused ) {
1545
 static void tcp_shutdown ( int booting __unused ) {
1509
 	struct tcp_connection *tcp;
1546
 	struct tcp_connection *tcp;
1547
+	unsigned long start;
1548
+	unsigned long elapsed;
1549
+
1550
+	/* Initiate a graceful close of all connections, allowing for
1551
+	 * the fact that the connection list may change as we do so.
1552
+	 */
1553
+	while ( ( tcp = tcp_first_unclosed() ) ) {
1554
+		DBGC ( tcp, "TCP %p closing for shutdown\n", tcp );
1555
+		tcp_close ( tcp, -ECANCELED );
1556
+	}
1557
+
1558
+	/* Wait for all connections to finish closing gracefully */
1559
+	start = currticks();
1560
+	while ( ( tcp = tcp_first_unfinished() ) &&
1561
+		( ( elapsed = ( currticks() - start ) ) < TCP_FINISH_TIMEOUT )){
1562
+		step();
1563
+	}
1510
 
1564
 
1565
+	/* Forcibly close any remaining connections */
1511
 	while ( ( tcp = list_first_entry ( &tcp_conns, struct tcp_connection,
1566
 	while ( ( tcp = list_first_entry ( &tcp_conns, struct tcp_connection,
1512
 					   list ) ) != NULL ) {
1567
 					   list ) ) != NULL ) {
1513
 		tcp->tcp_state = TCP_CLOSED;
1568
 		tcp->tcp_state = TCP_CLOSED;
1517
 }
1572
 }
1518
 
1573
 
1519
 /** TCP shutdown function */
1574
 /** TCP shutdown function */
1520
-struct startup_fn tcp_startup_fn __startup_fn ( STARTUP_EARLY ) = {
1575
+struct startup_fn tcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
1521
 	.shutdown = tcp_shutdown,
1576
 	.shutdown = tcp_shutdown,
1522
 };
1577
 };
1523
 
1578
 

Loading…
Cancel
Save