Browse Source

[virtio] Use separate RX and TX empty header buffers

Some host implementations (notably Google Compute Platform) are known
to unconditionally write back VIRTIO_NET_HDR_F_DATA_VALID to
header->flags for received packets, regardless of the features
negotiated by the driver.  This breaks the transmit datapath by
effectively setting an illegal flag for all subsequent transmitted
packets.

Work around this problem by using separate empty header buffers for
the receive and transmit queues.

Debugged-by: Ladi Prosek <lprosek@redhat.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
0dc4814ca8
1 changed files with 12 additions and 7 deletions
  1. 12
    7
      src/drivers/net/virtio-net.c

+ 12
- 7
src/drivers/net/virtio-net.c View File

@@ -104,8 +104,8 @@ struct virtnet_nic {
104 104
 	/** Pending rx packet count */
105 105
 	unsigned int rx_num_iobufs;
106 106
 
107
-	/** Virtio net packet header, we only need one */
108
-	struct virtio_net_hdr_modern empty_header;
107
+	/** Virtio net dummy packet headers */
108
+	struct virtio_net_hdr_modern empty_header[QUEUE_NB];
109 109
 };
110 110
 
111 111
 /** Add an iobuf to a virtqueue
@@ -120,19 +120,24 @@ static void virtnet_enqueue_iob ( struct net_device *netdev,
120 120
 				  int vq_idx, struct io_buffer *iobuf ) {
121 121
 	struct virtnet_nic *virtnet = netdev->priv;
122 122
 	struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
123
+	struct virtio_net_hdr_modern *header = &virtnet->empty_header[vq_idx];
123 124
 	unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
124 125
 	unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2;
125
-	size_t header_len = virtnet->virtio_version
126
-		? sizeof ( virtnet->empty_header )
127
-		: sizeof ( virtnet->empty_header.legacy );
126
+	size_t header_len = ( virtnet->virtio_version ?
127
+			      sizeof ( *header ) : sizeof ( header->legacy ) );
128 128
 	struct vring_list list[] = {
129 129
 		{
130 130
 			/* Share a single zeroed virtio net header between all
131
-			 * rx and tx packets.  This works because this driver
131
+			 * packets in a ring.  This works because this driver
132 132
 			 * does not use any advanced features so none of the
133 133
 			 * header fields get used.
134
+			 *
135
+			 * Some host implementations (notably Google Compute
136
+			 * Platform) are known to unconditionally write back
137
+			 * to header->flags for received packets.  Work around
138
+			 * this by using separate RX and TX headers.
134 139
 			 */
135
-			.addr = ( char* ) &virtnet->empty_header,
140
+			.addr = ( char* ) header,
136 141
 			.length = header_len,
137 142
 		},
138 143
 		{

Loading…
Cancel
Save