Download raw body.
sys/vio: align receive buffers
On Wed, Jun 17, 2026 at 11:30:56AM +0200, Kirill A. Korinsky wrote:
> On Wed, 10 Jun 2026 19:47:48 +0200,
> Kirill A. Korinsky <kirill@korins.ky> wrote:
> >
> > tech@,
> >
> > I'd like to move my diff from bugs@ to tech@.
> >
> > Context: OpenBSD/armv7 in Qemu crashes with vio NIC at alignment fault,
> > See: https://marc.info/?l=openbsd-bugs&m=178084629495768&w=2
> >
> > Why? vio removes the virtio net header before handing packets to
> > the Ethernet input path. With the 12 byte modern virtio net header this
> > leaves the Ethernet frame at an address where the following IP header is not
> > 32 bit aligned; armv7 traps on the resulting unaligned access while
> > processing DHCP traffic.
> >
> > Ok?
> >
>
> Anyone?
this makes sense and looks right to me. however, i always struggle
to comprehend calculating offsets like this, so take that with grain of
salt.
ive very lightly tested this on arm64 and it still works. ok by me.
ps, the virtio net header annoys me at the best of times, this just adds
to that.
> Index: sys/dev/pv/if_vio.c
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/pv/if_vio.c,v
> diff -u -p -r1.79 if_vio.c
> --- sys/dev/pv/if_vio.c 16 Apr 2026 21:00:00 -0000 1.79
> +++ sys/dev/pv/if_vio.c 7 Jun 2026 22:28:23 -0000
> @@ -297,6 +297,7 @@ struct vio_softc {
> uint16_t sc_rxr_lwm;
> int sc_tx_slots_per_req;
> int sc_rx_mbuf_size;
> + int sc_rx_mbuf_offset;
>
> enum vio_ctrl_state sc_ctrl_inuse;
>
> @@ -747,6 +748,8 @@ negotiate:
> } else {
> sc->sc_hdr_size = offsetof(struct virtio_net_hdr, num_buffers);
> }
> + sc->sc_rx_mbuf_offset = (ETHER_ALIGN + sizeof(uint32_t) -
> + sc->sc_hdr_size % sizeof(uint32_t)) % sizeof(uint32_t);
>
> ifp->if_capabilities = 0;
> ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
> @@ -780,9 +783,11 @@ negotiate:
> if (!virtio_has_feature(vsc, VIRTIO_NET_F_MRG_RXBUF)) {
> if (device_mtu)
> sc->sc_rx_mbuf_size = MAX(sc->sc_rx_mbuf_size,
> - device_mtu + sc->sc_hdr_size + ETHER_HDR_LEN);
> + sc->sc_rx_mbuf_offset + device_mtu +
> + sc->sc_hdr_size + ETHER_HDR_LEN);
> ifp->if_hardmtu = MIN(ifp->if_hardmtu,
> - sc->sc_rx_mbuf_size - sc->sc_hdr_size - ETHER_HDR_LEN);
> + sc->sc_rx_mbuf_size - sc->sc_rx_mbuf_offset -
> + sc->sc_hdr_size - ETHER_HDR_LEN);
> }
>
> txsize = ifp->if_hardmtu + sc->sc_hdr_size + ETHER_HDR_LEN;
> @@ -790,13 +795,16 @@ negotiate:
> virtio_has_feature(vsc, VIRTIO_NET_F_HOST_TSO6))
> txsize = MAXMCLBYTES + sc->sc_hdr_size;
>
> - rxsize = ifp->if_hardmtu + sc->sc_hdr_size + ETHER_HDR_LEN;
> + rxsize = ifp->if_hardmtu + sc->sc_rx_mbuf_offset +
> + sc->sc_hdr_size + ETHER_HDR_LEN;
> if (virtio_has_feature(vsc, VIRTIO_NET_F_GUEST_TSO4) ||
> virtio_has_feature(vsc, VIRTIO_NET_F_GUEST_TSO6))
> - rxsize = MAXMCLBYTES + sc->sc_hdr_size;
> + rxsize = MAXMCLBYTES + sc->sc_rx_mbuf_offset +
> + sc->sc_hdr_size;
>
> printf("\n");
> - sc->sc_rxr_lwm = 2 * howmany(rxsize, sc->sc_rx_mbuf_size);
> + sc->sc_rxr_lwm = 2 * howmany(rxsize,
> + sc->sc_rx_mbuf_size - sc->sc_rx_mbuf_offset);
>
> /* defrag for longer mbuf chains */
> tx_max_segments = 16;
> @@ -1438,7 +1446,7 @@ vio_add_rx_mbuf(struct vio_softc *sc, st
> return ENOBUFS;
> vioq->viq_rxmbufs[i] = m;
> m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
> - /* XXX m_adj ETHER_ALIGN ? */
> + m_adj(m, sc->sc_rx_mbuf_offset);
> r = bus_dmamap_load_mbuf(sc->sc_virtio->sc_dmat,
> vioq->viq_rxdmamaps[i], m, BUS_DMA_READ|BUS_DMA_NOWAIT);
> if (r) {
> @@ -1509,7 +1517,8 @@ vio_populate_rx_mbufs(struct vio_softc *
> 0, sc->sc_hdr_size, 0);
> virtio_enqueue_p(vq, slot, vioq->viq_rxdmamaps[slot],
> sc->sc_hdr_size,
> - sc->sc_rx_mbuf_size - sc->sc_hdr_size, 0);
> + sc->sc_rx_mbuf_size - sc->sc_rx_mbuf_offset -
> + sc->sc_hdr_size, 0);
> }
> virtio_enqueue_commit(vsc, vq, slot, 0);
> done = 1;
>
> --
> wbr, Kirill
>
sys/vio: align receive buffers