From 0dc4814ca83b8b7ca226c4a9c4bdcc050af2882b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jan 2017 13:23:31 +0000 Subject: [PATCH] [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 Signed-off-by: Michael Brown --- src/drivers/net/virtio-net.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index eaf6ed38..40367b89 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, }, {