diff options
author | Michael Brown | 2010-07-21 13:01:50 +0200 |
---|---|---|
committer | Michael Brown | 2010-07-21 13:01:50 +0200 |
commit | 1d3b6619e5e35eecc29efcef6eb1dd3564a2eb45 (patch) | |
tree | 8dbfdea795e921626cf40bdbfde9bbeb01b2a185 /src/net/tcp.c | |
parent | [malloc] Add cache discard mechanism (diff) | |
download | ipxe-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.c | 41 |
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 |