]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[xen] Provide 18 4kB receive buffers to work around xen-netback bug
authorMichael Brown <mcb30@ipxe.org>
Fri, 19 May 2017 01:50:21 +0000 (02:50 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 19 May 2017 01:56:53 +0000 (02:56 +0100)
The Xen network backend (xen-netback) suffered from a regression
between upstream Linux kernels 3.18 and 4.2 inclusive, which would
cause packet reception to fail unless at least 18 receive buffers were
available.  This bug was fixed in kernel commit 1d5d485 ("xen-netback:
require fewer guest Rx slots when not using GSO").

Work around this bug in affected versions of xen-netback by providing
the requisite 18 receive buffers.

Reported-by: Taylor Schneider <tschneider@live.com>
Tested-by: Taylor Schneider <tschneider@live.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/net/netfront.c
src/drivers/net/netfront.h

index 2f4bbf2a0df6aa85e150ff90422fe286d490f547..b6205542b2d1139221299f955b94df731db5430b 100644 (file)
@@ -511,15 +511,12 @@ static void netfront_refill_rx ( struct net_device *netdev ) {
        struct xen_device *xendev = netfront->xendev;
        struct io_buffer *iobuf;
        struct netif_rx_request *request;
+       unsigned int refilled = 0;
        int notify;
        int rc;
 
-       /* Do nothing if ring is already full */
-       if ( netfront_ring_is_full ( &netfront->rx ) )
-               return;
-
        /* Refill ring */
-       do {
+       while ( netfront_ring_fill ( &netfront->rx ) < NETFRONT_RX_FILL ) {
 
                /* Allocate I/O buffer */
                iobuf = alloc_iob ( PAGE_SIZE );
@@ -543,13 +540,17 @@ static void netfront_refill_rx ( struct net_device *netdev ) {
 
                /* Move to next descriptor */
                netfront->rx_fring.req_prod_pvt++;
+               refilled++;
 
-       } while ( ! netfront_ring_is_full ( &netfront->rx ) );
+       }
 
        /* Push new descriptors and notify backend if applicable */
-       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->rx_fring, notify );
-       if ( notify )
-               netfront_send_event ( netfront );
+       if ( refilled ) {
+               RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->rx_fring,
+                                                     notify );
+               if ( notify )
+                       netfront_send_event ( netfront );
+       }
 }
 
 /**
index 38fd0a77e644934ead22d60e6d2b49953e801b8a..c95ed26457ffcc72e2273773ab87777784ea15bf 100644 (file)
@@ -16,7 +16,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define NETFRONT_NUM_TX_DESC 16
 
 /** Number of receive ring entries */
-#define NETFRONT_NUM_RX_DESC 8
+#define NETFRONT_NUM_RX_DESC 32
+
+/** Receive ring fill level
+ *
+ * The xen-netback driver from kernels 3.18 to 4.2 inclusive have a
+ * bug (CA-163395) which prevents packet reception if fewer than 18
+ * receive descriptors are available.  This was fixed in upstream
+ * kernel commit d5d4852 ("xen-netback: require fewer guest Rx slots
+ * when not using GSO").
+ *
+ * We provide 18 receive descriptors to avoid unpleasant silent
+ * failures on these kernel versions.
+ */
+#define NETFRONT_RX_FILL 18
 
 /** Grant reference indices */
 enum netfront_ref_index {
@@ -88,6 +101,21 @@ netfront_init_ring ( struct netfront_ring *ring, const char *ref_key,
        ring->ids = ids;
 }
 
+/**
+ * Calculate descriptor ring fill level
+ *
+ * @v ring             Descriptor ring
+ * @v fill             Fill level
+ */
+static inline __attribute__ (( always_inline )) unsigned int
+netfront_ring_fill ( struct netfront_ring *ring ) {
+       unsigned int fill_level;
+
+       fill_level = ( ring->id_prod - ring->id_cons );
+       assert ( fill_level <= ring->count );
+       return fill_level;
+}
+
 /**
  * Check whether or not descriptor ring is full
  *
@@ -96,11 +124,8 @@ netfront_init_ring ( struct netfront_ring *ring, const char *ref_key,
  */
 static inline __attribute__ (( always_inline )) int
 netfront_ring_is_full ( struct netfront_ring *ring ) {
-       unsigned int fill_level;
 
-       fill_level = ( ring->id_prod - ring->id_cons );
-       assert ( fill_level <= ring->count );
-       return ( fill_level >= ring->count );
+       return ( netfront_ring_fill ( ring ) >= ring->count );
 }
 
 /**
@@ -112,7 +137,7 @@ netfront_ring_is_full ( struct netfront_ring *ring ) {
 static inline __attribute__ (( always_inline )) int
 netfront_ring_is_empty ( struct netfront_ring *ring ) {
 
-       return ( ring->id_prod == ring->id_cons );
+       return ( netfront_ring_fill ( ring ) == 0 );
 }
 
 /** A netfront NIC */