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 | |
| 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')
| -rw-r--r-- | src/include/ipxe/list.h | 12 | ||||
| -rw-r--r-- | 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 743a3e260..a40fb681e 100644 --- a/src/include/ipxe/list.h +++ b/src/include/ipxe/list.h @@ -163,6 +163,18 @@ static inline int list_empty ( const struct list_head *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 * * @v pos The type * to use as a loop counter diff --git a/src/net/tcp.c b/src/net/tcp.c index 35acd0a19..28e8399ea 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 |
