From 1d3b6619e5e35eecc29efcef6eb1dd3564a2eb45 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 21 Jul 2010 12:01:50 +0100 Subject: [PATCH] [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 --- src/include/ipxe/list.h | 12 ++++++++++++ src/net/tcp.c | 41 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/include/ipxe/list.h b/src/include/ipxe/list.h index 743a3e26..a40fb681 100644 --- a/src/include/ipxe/list.h +++ b/src/include/ipxe/list.h @@ -162,6 +162,18 @@ static inline int list_empty ( const struct list_head *head ) { &pos->member != (head); \ pos = list_entry ( pos->member.next, typeof ( *pos ), member ) ) +/** + * Iterate over entries in a list in reverse order + * + * @v pos The type * to use as a loop counter + * @v head The head for your list + * @v member The name of the list_struct within the struct + */ +#define list_for_each_entry_reverse( pos, head, member ) \ + for ( pos = list_entry ( (head)->prev, typeof ( *pos ), member ); \ + &pos->member != (head); \ + pos = list_entry ( pos->member.prev, typeof ( *pos ), member ) ) + /** * Iterate over entries in a list, safe against deletion of entries * 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