summaryrefslogtreecommitdiffstats
path: root/src/net/tcp.c
diff options
context:
space:
mode:
authorMichael Brown2010-07-21 13:01:50 +0200
committerMichael Brown2010-07-21 13:01:50 +0200
commit1d3b6619e5e35eecc29efcef6eb1dd3564a2eb45 (patch)
tree8dbfdea795e921626cf40bdbfde9bbeb01b2a185 /src/net/tcp.c
parent[malloc] Add cache discard mechanism (diff)
downloadipxe-1d3b6619e5e35eecc29efcef6eb1dd3564a2eb45.tar.gz
ipxe-1d3b6619e5e35eecc29efcef6eb1dd3564a2eb45.tar.xz
ipxe-1d3b6619e5e35eecc29efcef6eb1dd3564a2eb45.zip
[tcp] Allow out-of-order receive queue to be discarded
Allow packets in the receive queue to be discarded in order to free up memory. This avoids a potential deadlock condition in which the missing packet can never be received because the receive queue is occupying all of the memory available for further RX buffers. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net/tcp.c')
-rw-r--r--src/net/tcp.c41
1 files changed, 38 insertions, 3 deletions
diff --git a/src/net/tcp.c b/src/net/tcp.c
index 35acd0a1..28e8399e 100644
--- a/src/net/tcp.c
+++ b/src/net/tcp.c
@@ -1004,14 +1004,21 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq,
*/
static void tcp_process_rx_queue ( struct tcp_connection *tcp ) {
struct io_buffer *iobuf;
- struct io_buffer *tmp;
struct tcp_rx_queued_header *tcpqhdr;
uint32_t seq;
unsigned int flags;
size_t len;
- /* Process all applicable received buffers */
- list_for_each_entry_safe ( iobuf, tmp, &tcp->rx_queue, list ) {
+ /* Process all applicable received buffers. Note that we
+ * cannot use list_for_each_entry() to iterate over the RX
+ * queue, since tcp_discard() may remove packets from the RX
+ * queue while we are processing.
+ */
+ while ( ! list_empty ( &tcp->rx_queue ) ) {
+ list_for_each_entry ( iobuf, &tcp->rx_queue, list )
+ break;
+
+ /* Stop processing when we hit the first gap */
tcpqhdr = iobuf->data;
if ( tcp_cmp ( tcpqhdr->seq, tcp->rcv_ack ) > 0 )
break;
@@ -1183,6 +1190,34 @@ struct tcpip_protocol tcp_protocol __tcpip_protocol = {
.tcpip_proto = IP_TCP,
};
+/**
+ * Discard some cached TCP data
+ *
+ * @ret discarded Number of cached items discarded
+ */
+static unsigned int tcp_discard ( void ) {
+ struct tcp_connection *tcp;
+ struct io_buffer *iobuf;
+ unsigned int discarded = 0;
+
+ /* Try to drop one queued RX packet from each connection */
+ list_for_each_entry ( tcp, &tcp_conns, list ) {
+ list_for_each_entry_reverse ( iobuf, &tcp->rx_queue, list ) {
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+ discarded++;
+ break;
+ }
+ }
+
+ return discarded;
+}
+
+/** TCP cache discarder */
+struct cache_discarder tcp_cache_discarder __cache_discarder = {
+ .discard = tcp_discard,
+};
+
/***************************************************************************
*
* Data transfer interface