david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[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>
This commit is contained in:
Michael Brown 2017-01-23 13:23:31 +00:00
parent b6f524388b
commit 0dc4814ca8
1 changed files with 12 additions and 7 deletions

View File

@ -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,
},
{