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 7 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
 	/** Pending rx packet count */
104
 	/** Pending rx packet count */
105
 	unsigned int rx_num_iobufs;
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
 /** Add an iobuf to a virtqueue
111
 /** Add an iobuf to a virtqueue
120
 				  int vq_idx, struct io_buffer *iobuf ) {
120
 				  int vq_idx, struct io_buffer *iobuf ) {
121
 	struct virtnet_nic *virtnet = netdev->priv;
121
 	struct virtnet_nic *virtnet = netdev->priv;
122
 	struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
122
 	struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
123
+	struct virtio_net_hdr_modern *header = &virtnet->empty_header[vq_idx];
123
 	unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
124
 	unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
124
 	unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2;
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
 	struct vring_list list[] = {
128
 	struct vring_list list[] = {
129
 		{
129
 		{
130
 			/* Share a single zeroed virtio net header between all
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
 			 * does not use any advanced features so none of the
132
 			 * does not use any advanced features so none of the
133
 			 * header fields get used.
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
 			.length = header_len,
141
 			.length = header_len,
137
 		},
142
 		},
138
 		{
143
 		{

Loading…
Cancel
Save