[tcp] Allow out-of-order receive queue to be discarded
authorMichael Brown <mcb30@ipxe.org>
Wed, 21 Jul 2010 11:01:50 +0000 (12:01 +0100)
committerMarty Connor <mdc@etherboot.org>
Sun, 1 Aug 2010 20:15:22 +0000 (16:15 -0400)
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>
Signed-off-by: Marty Connor <mdc@etherboot.org>
src/include/gpxe/list.h
src/net/tcp.c

index 22ba201..02b0f6e 100644 (file)
@@ -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
  *
index 637bfce..d2b2885 100644 (file)
@@ -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