Browse Source

[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>
tags/v1.20.1
Michael Brown 14 years ago
parent
commit
1d3b6619e5
2 changed files with 50 additions and 3 deletions
  1. 12
    0
      src/include/ipxe/list.h
  2. 38
    3
      src/net/tcp.c

+ 12
- 0
src/include/ipxe/list.h View File

@@ -162,6 +162,18 @@ static inline int list_empty ( const struct list_head *head ) {
162 162
 	      &pos->member != (head);					      \
163 163
 	      pos = list_entry ( pos->member.next, typeof ( *pos ), member ) )
164 164
 
165
+/**
166
+ * Iterate over entries in a list in reverse order
167
+ *
168
+ * @v pos	The type * to use as a loop counter
169
+ * @v head	The head for your list
170
+ * @v member	The name of the list_struct within the struct
171
+ */
172
+#define list_for_each_entry_reverse( pos, head, member )		      \
173
+	for ( pos = list_entry ( (head)->prev, typeof ( *pos ), member );     \
174
+	      &pos->member != (head);					      \
175
+	      pos = list_entry ( pos->member.prev, typeof ( *pos ), member ) )
176
+
165 177
 /**
166 178
  * Iterate over entries in a list, safe against deletion of entries
167 179
  *

+ 38
- 3
src/net/tcp.c View File

@@ -1004,14 +1004,21 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq,
1004 1004
  */
1005 1005
 static void tcp_process_rx_queue ( struct tcp_connection *tcp ) {
1006 1006
 	struct io_buffer *iobuf;
1007
-	struct io_buffer *tmp;
1008 1007
 	struct tcp_rx_queued_header *tcpqhdr;
1009 1008
 	uint32_t seq;
1010 1009
 	unsigned int flags;
1011 1010
 	size_t len;
1012 1011
 
1013
-	/* Process all applicable received buffers */
1014
-	list_for_each_entry_safe ( iobuf, tmp, &tcp->rx_queue, list ) {
1012
+	/* Process all applicable received buffers.  Note that we
1013
+	 * cannot use list_for_each_entry() to iterate over the RX
1014
+	 * queue, since tcp_discard() may remove packets from the RX
1015
+	 * queue while we are processing.
1016
+	 */
1017
+	while ( ! list_empty ( &tcp->rx_queue ) ) {
1018
+		list_for_each_entry ( iobuf, &tcp->rx_queue, list )
1019
+			break;
1020
+
1021
+		/* Stop processing when we hit the first gap */
1015 1022
 		tcpqhdr = iobuf->data;
1016 1023
 		if ( tcp_cmp ( tcpqhdr->seq, tcp->rcv_ack ) > 0 )
1017 1024
 			break;
@@ -1183,6 +1190,34 @@ struct tcpip_protocol tcp_protocol __tcpip_protocol = {
1183 1190
 	.tcpip_proto = IP_TCP,
1184 1191
 };
1185 1192
 
1193
+/**
1194
+ * Discard some cached TCP data
1195
+ *
1196
+ * @ret discarded	Number of cached items discarded
1197
+ */
1198
+static unsigned int tcp_discard ( void ) {
1199
+	struct tcp_connection *tcp;
1200
+	struct io_buffer *iobuf;
1201
+	unsigned int discarded = 0;
1202
+
1203
+	/* Try to drop one queued RX packet from each connection */
1204
+	list_for_each_entry ( tcp, &tcp_conns, list ) {
1205
+		list_for_each_entry_reverse ( iobuf, &tcp->rx_queue, list ) {
1206
+			list_del ( &iobuf->list );
1207
+			free_iob ( iobuf );
1208
+			discarded++;
1209
+			break;
1210
+		}
1211
+	}
1212
+
1213
+	return discarded;
1214
+}
1215
+
1216
+/** TCP cache discarder */
1217
+struct cache_discarder tcp_cache_discarder __cache_discarder = {
1218
+	.discard = tcp_discard,
1219
+};
1220
+
1186 1221
 /***************************************************************************
1187 1222
  *
1188 1223
  * Data transfer interface

Loading…
Cancel
Save