@@ -198,6 +198,7 @@ enum virtio_trans {
};
struct virtio_device {
+ bool legacy;
bool use_vhost;
void *virtio;
struct virtio_ops *ops;
@@ -330,6 +330,7 @@ int virtio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
switch (trans) {
case VIRTIO_PCI:
+ vdev->legacy = true;
virtio = calloc(sizeof(struct virtio_pci), 1);
if (!virtio)
return -ENOMEM;
@@ -343,6 +344,7 @@ int virtio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
r = vdev->ops->init(kvm, dev, vdev, device_id, subsys_id, class);
break;
case VIRTIO_MMIO:
+ vdev->legacy = true;
virtio = calloc(sizeof(struct virtio_mmio), 1);
if (!virtio)
return -ENOMEM;
@@ -81,6 +81,15 @@ static bool has_virtio_feature(struct net_dev *ndev, u32 feature)
return ndev->vdev.features & (1 << feature);
}
+static int virtio_net_hdr_len(struct net_dev *ndev)
+{
+ if (has_virtio_feature(ndev, VIRTIO_NET_F_MRG_RXBUF) ||
+ !ndev->vdev.legacy)
+ return sizeof(struct virtio_net_hdr_mrg_rxbuf);
+
+ return sizeof(struct virtio_net_hdr);
+}
+
static void *virtio_net_rx_thread(void *p)
{
struct iovec iov[VIRTIO_NET_QUEUE_SIZE];
@@ -133,7 +142,13 @@ static void *virtio_net_rx_thread(void *p)
head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
}
- if (has_virtio_feature(ndev, VIRTIO_NET_F_MRG_RXBUF))
+ /*
+ * The device MUST set num_buffers, except in the case
+ * where the legacy driver did not negotiate
+ * VIRTIO_NET_F_MRG_RXBUF and the field does not exist.
+ */
+ if (has_virtio_feature(ndev, VIRTIO_NET_F_MRG_RXBUF) ||
+ !ndev->vdev.legacy)
hdr->num_buffers = virtio_host_to_guest_u16(vq, num_buffers);
virt_queue__used_idx_advance(vq, num_buffers);
@@ -301,9 +316,7 @@ static bool virtio_net__tap_init(struct net_dev *ndev)
const struct virtio_net_params *params = ndev->params;
bool skipconf = !!params->tapif;
- hdr_len = has_virtio_feature(ndev, VIRTIO_NET_F_MRG_RXBUF) ?
- sizeof(struct virtio_net_hdr_mrg_rxbuf) :
- sizeof(struct virtio_net_hdr);
+ hdr_len = virtio_net_hdr_len(ndev);
if (ioctl(ndev->tap_fd, TUNSETVNETHDRSZ, &hdr_len) < 0)
pr_warning("Config tap device TUNSETVNETHDRSZ error");
@@ -521,9 +534,7 @@ static void virtio_net_start(struct net_dev *ndev)
virtio_net__vhost_set_features(ndev) != 0)
die_perror("VHOST_SET_FEATURES failed");
} else {
- ndev->info.vnet_hdr_len = has_virtio_feature(ndev, VIRTIO_NET_F_MRG_RXBUF) ?
- sizeof(struct virtio_net_hdr_mrg_rxbuf) :
- sizeof(struct virtio_net_hdr);
+ ndev->info.vnet_hdr_len = virtio_net_hdr_len(ndev);
uip_init(&ndev->info);
}
}
The virtio_net header contains a 'num_buffers' field, used when the VIRTIO_NET_F_MRG_RXBUF feature is negotiated. The legacy driver does not present this field when the feature is not negotiated. In that case the header is 2 bytes smaller. When using the modern virtio transport, the header always contains the field and in addition the device MUST set it to 1 when the VIRTIO_NET_F_MRG_RXBUF is not negotiated. Prepare for modern virtio support by enabling this case once the 'legacy' flag is switched off. Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com> --- include/kvm/virtio.h | 1 + virtio/core.c | 2 ++ virtio/net.c | 25 ++++++++++++++++++------- 3 files changed, 21 insertions(+), 7 deletions(-)