The virtio-net device used a fixed header size that did not account for the num_buffers field used in VirtIO 1.0.
Use dynamic header sizing: 10 bytes for legacy mode and 12 bytes for VirtIO 1.0. This ensures correct packet handling across different VirtIO configurations. The VirtioNetHdr and VirtioNetHdrMrgRxbuf structures replicate the same field layout as struct virtio_net_hdr and struct virtio_net_hdr_mrg_rxbuf defined in inux/uapi/linux/virtio_net.h. Signed-off-by: Zhuoying Cai <[email protected]> --- pc-bios/s390-ccw/virtio-net.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c index f58f7ffc55..ffdf7ba831 100644 --- a/pc-bios/s390-ccw/virtio-net.c +++ b/pc-bios/s390-ccw/virtio-net.c @@ -20,6 +20,7 @@ #include "s390-ccw.h" #include "virtio.h" #include "virtio-ccw.h" +#include "virtio-pci.h" #include "s390-time.h" #include "helper.h" @@ -43,7 +44,18 @@ struct VirtioNetHdr { }; typedef struct VirtioNetHdr VirtioNetHdr; +struct VirtioNetHdrMrgRxbuf { + struct VirtioNetHdr hdr; + uint16_t num_buffers; /* Only with VIRTIO_NET_F_MRG_RXBUF or VIRTIO1 */ +}; +typedef struct VirtioNetHdrMrgRxbuf VirtioNetHdrMrgRxbuf; + +/* Header sizes for different modes */ +#define VIRTIO_NET_HDR_SIZE_LEGACY sizeof(VirtioNetHdr) +#define VIRTIO_NET_HDR_SIZE_V1 sizeof(VirtioNetHdrMrgRxbuf) + static uint16_t rx_last_idx; /* Last index in receive queue "used" ring */ +static int virtio_net_hdr_size; int virtio_net_init(void *mac_addr) { @@ -62,12 +74,16 @@ int virtio_net_init(void *mac_addr) return -1; } + virtio_net_hdr_size = vdev->guest_features[1] & VIRTIO_F_VERSION_1 + ? VIRTIO_NET_HDR_SIZE_V1 + : VIRTIO_NET_HDR_SIZE_LEGACY; + memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN); for (i = 0; i < 64; i++) { - buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr)); + buf = malloc(ETH_MTU_SIZE + virtio_net_hdr_size); IPL_assert(buf != NULL, "Can not allocate memory for receive buffers"); - vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr), + vring_send_buf(rxvq, buf, ETH_MTU_SIZE + virtio_net_hdr_size, VRING_DESC_F_WRITE); } vring_notify(rxvq); @@ -77,14 +93,14 @@ int virtio_net_init(void *mac_addr) int send(int fd, const void *buf, int len, int flags) { - VirtioNetHdr tx_hdr; + VirtioNetHdrMrgRxbuf tx_hdr; VDev *vdev = virtio_get_device(); VRing *txvq = &vdev->vrings[VQ_TX]; /* Set up header - we do not use anything special, so simply clear it */ memset(&tx_hdr, 0, sizeof(tx_hdr)); - vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT); + vring_send_buf(txvq, &tx_hdr, virtio_net_hdr_size, VRING_DESC_F_NEXT); vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN); while (!vr_poll(txvq)) { yield(); @@ -108,13 +124,13 @@ int recv(int fd, void *buf, int maxlen, int flags) return 0; } - len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr); + len = rxvq->used->ring[rx_last_idx % rxvq->num].len - virtio_net_hdr_size; if (len > maxlen) { puts("virtio-net: Receive buffer too small"); len = maxlen; } id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num; - pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr)); + pkt = (uint8_t *)(rxvq->desc[id].addr + virtio_net_hdr_size); #if DEBUG_VIRTIO_NET /* Dump packet */ int i; -- 2.55.0
