aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2017-01-23 13:23:31 +0000
committerMichael Brown <mcb30@ipxe.org>2017-01-23 13:27:26 +0000
commit0dc4814ca83b8b7ca226c4a9c4bdcc050af2882b (patch)
tree4a8a98344a3d814acd651154f7f90be0ed7ace99
parentb6f524388ba5a02cf95ffa3fb3bfd5f0feae7bb6 (diff)
downloadipxe-0dc4814ca83b8b7ca226c4a9c4bdcc050af2882b.tar.gz
[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>
-rw-r--r--src/drivers/net/virtio-net.c19
1 files changed, 12 insertions, 7 deletions
diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c
index eaf6ed382..40367b89d 100644
--- a/src/drivers/net/virtio-net.c
+++ b/src/drivers/net/virtio-net.c
@@ -104,8 +104,8 @@ struct virtnet_nic {
/** Pending rx packet count */
unsigned int rx_num_iobufs;
- /** Virtio net packet header, we only need one */
- struct virtio_net_hdr_modern empty_header;
+ /** Virtio net dummy packet headers */
+ struct virtio_net_hdr_modern empty_header[QUEUE_NB];
};
/** Add an iobuf to a virtqueue
@@ -120,19 +120,24 @@ static void virtnet_enqueue_iob ( struct net_device *netdev,
int vq_idx, struct io_buffer *iobuf ) {
struct virtnet_nic *virtnet = netdev->priv;
struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
+ struct virtio_net_hdr_modern *header = &virtnet->empty_header[vq_idx];
unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2;
- size_t header_len = virtnet->virtio_version
- ? sizeof ( virtnet->empty_header )
- : sizeof ( virtnet->empty_header.legacy );
+ size_t header_len = ( virtnet->virtio_version ?
+ sizeof ( *header ) : sizeof ( header->legacy ) );
struct vring_list list[] = {
{
/* Share a single zeroed virtio net header between all
- * rx and tx packets. This works because this driver
+ * packets in a ring. This works because this driver
* does not use any advanced features so none of the
* header fields get used.
+ *
+ * Some host implementations (notably Google Compute
+ * Platform) are known to unconditionally write back
+ * to header->flags for received packets. Work around
+ * this by using separate RX and TX headers.
*/
- .addr = ( char* ) &virtnet->empty_header,
+ .addr = ( char* ) header,
.length = header_len,
},
{