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@xxxxxxx> --- include/kvm/virtio.h | 1 + virtio/core.c | 2 ++ virtio/net.c | 25 ++++++++++++++++++------- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/include/kvm/virtio.h b/include/kvm/virtio.h index 2da5e4f6..8c05bae2 100644 --- a/include/kvm/virtio.h +++ b/include/kvm/virtio.h @@ -198,6 +198,7 @@ enum virtio_trans { }; struct virtio_device { + bool legacy; bool use_vhost; void *virtio; struct virtio_ops *ops; diff --git a/virtio/core.c b/virtio/core.c index 568667f2..09abbf40 100644 --- a/virtio/core.c +++ b/virtio/core.c @@ -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; diff --git a/virtio/net.c b/virtio/net.c index 70002f72..985642f6 100644 --- a/virtio/net.c +++ b/virtio/net.c @@ -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); } } -- 2.36.1