On 9/8/26 2:32 PM, Jared Rossi wrote:
>
>
> On 9/3/26 12:24 PM, Zhuoying Cai wrote:
>> The virtio-net device used a fixed header size that did not account for
>> the num_buffers field used in VirtIO 1.0 or for the mergeable receive
>> buffers feature.
>>
>> Use dynamic header sizing: 10 bytes for legacy mode and 12 bytes for
>> VirtIO 1.0 or when VIRTIO_NET_F_MRG_RXBUF is enabled. This ensures
>> correct packet handling across different VirtIO configurations.
>>
>> Signed-off-by: Zhuoying Cai <[email protected]>
>> ---
>> pc-bios/s390-ccw/virtio-net.c | 30 ++++++++++++++++++++++++------
>> 1 file changed, 24 insertions(+), 6 deletions(-)
>>
>> diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
>> index f58f7ffc55..afa728bc32 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"
>>
>> @@ -28,6 +29,7 @@
>> #endif
>>
>> #define VIRTIO_NET_F_MAC_BIT (1 << 5)
>> +#define VIRTIO_NET_F_MRG_RXBUF_BIT (1 << 15)
>
> We define this new feature bit, but it looks like we never set it.
>
> The existing virtio_net_init() code has only:
>
> vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
>
> Should it be updated to VIRTIO_NET_F_MAC_BIT | VIRTIO_NET_F_MRG_RXBUF_BIT?
>
> Is the new feature bit something we want to unconditionally request, only
> request for PCI, or do we simply not care about it?
As of now, we do not care about VIRTIO_NET_F_MRG_RXBUF_BIT. I added it
because the num_buffers field in VirtioNetHdrMrgRxbuf is present when
either condition is true, and we might want to use it in the future.
>>
>> #define VQ_RX 0 /* Receive queue */
>> #define VQ_TX 1 /* Transmit queue */
>> @@ -43,7 +45,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 +75,17 @@ int virtio_net_init(void *mac_addr)
>> return -1;
>> }
>>
>> + virtio_net_hdr_size = ((vdev->guest_features[1] & VIRTIO_F_VERSION_1) ||
>> + (vdev->guest_features[0] &
>> VIRTIO_NET_F_MRG_RXBUF_BIT))
>
> Because we do not set VIRTIO_NET_F_MRG_RXBUF in net_init(), only the first
> half of this check can ever return true.
>
> Maybe that is correct, but in that case we can simplify this to just
> vdev->guest_features[1] & VIRTIO_F_VERSION_1, right?
>
Since VIRTIO_NET_F_MRG_RXBUF is neither negotiated nor used today, I can
remove it entirely and simplify the check to vdev->guest_features[1] &
VIRTIO_F_VERSION_1.
> Regards,
> Jared Rossi
>> + ? 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 +95,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 +126,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;
>