From: Stefan Fritsch Subject: Re: vio: Support MTU feature To: Brent Cook Cc: tech@openbsd.org Date: Sun, 15 Feb 2026 11:15:34 +0100 Hi Brent, Am 15.02.26 um 08:04 schrieb Brent Cook: > Hi Stefan, > > I was wondering if you could say more about how to use this. Did you use > a particular Apple hypervisor framework frontend? Helg did all the testing and debugging. He used VirtualBuddy. For more details, you would need to ask him. Cheers, Stefan > > On Wed, Jan 14, 2026 at 6:26 AM Stefan Fritsch > wrote: > > Hi, > > here is the final bit to make OpenBSD work on Apple Virtualization, > which > refuses to work if the MTU feature is not negotiated: > > Add support for the VIRTIO_NET_F_MTU which allows to get the hardmtu > from > the hypervisor. Also set the current mtu to the same value. The virtio > standard is not clear if that is recommended, but Linux does this, too. > > Use ETHER_MAX_HARDMTU_LEN as upper hardmtu limit instead of > MAXMCLBYTES, > as this seems to be more correct. > > If the hypervisor requests a MTU larger than ETHER_MAX_HARDMTU_LEN, > redo > feature negotiation without VIRTIO_NET_F_MTU. > > Input and testing from @helg > > ok? > > Cheers, > Stefan > > diff --git a/sys/dev/pv/if_vio.c b/sys/dev/pv/if_vio.c > index ae138c0ee59..b2e0435fb85 100644 > --- a/sys/dev/pv/if_vio.c > +++ b/sys/dev/pv/if_vio.c > @@ -294,6 +294,7 @@ struct vio_softc { >         struct intrmap          *sc_intrmap; >         struct vio_queue        *sc_q; >         uint16_t                sc_nqueues; > +       uint16_t                sc_rxr_lwm; >         int                     sc_tx_slots_per_req; >         int                     sc_rx_mbuf_size; > > @@ -367,7 +368,7 @@ int vio_ctrl_start(struct vio_softc *, uint8_t, > uint8_t, int, int *); >  int    vio_ctrl_submit(struct vio_softc *, int); >  void   vio_ctrl_finish(struct vio_softc *); >  void   vio_ctrl_wakeup(struct vio_softc *, enum vio_ctrl_state); > -int    vio_alloc_mem(struct vio_softc *, int); > +int    vio_alloc_mem(struct vio_softc *, int, bus_size_t); >  int    vio_alloc_dmamem(struct vio_softc *); >  void   vio_free_dmamem(struct vio_softc *); > > @@ -466,12 +467,10 @@ vio_free_dmamem(struct vio_softc *sc) >   *   viq_txmbufs[slot]:                mbuf pointer array for sent > frames >   */ >  int > -vio_alloc_mem(struct vio_softc *sc, int tx_max_segments) > +vio_alloc_mem(struct vio_softc *sc, int tx_max_segments, bus_size_t > txsize) >  { >         struct virtio_softc     *vsc = sc->sc_virtio; > -       struct ifnet            *ifp = &sc->sc_ac.ac_if; >         size_t                   allocsize, rxqsize, txqsize, > offset = 0; > -       bus_size_t               txsize; >         caddr_t                  kva; >         int                      i, qidx, r; > > @@ -528,11 +527,6 @@ vio_alloc_mem(struct vio_softc *sc, int > tx_max_segments) >         } >         KASSERT(offset == allocsize); > > -       if (virtio_has_feature(vsc, VIRTIO_NET_F_HOST_TSO4) || > -           virtio_has_feature(vsc, VIRTIO_NET_F_HOST_TSO6)) > -               txsize = MAXMCLBYTES + sc->sc_hdr_size + ETHER_HDR_LEN; > -       else > -               txsize = ifp->if_hardmtu + sc->sc_hdr_size + > ETHER_HDR_LEN; > >         for (qidx = 0; qidx < sc->sc_nqueues; qidx++) { >                 struct vio_queue *vioq = &sc->sc_q[qidx]; > @@ -635,7 +629,9 @@ vio_attach(struct device *parent, struct device > *self, void *aux) >         struct vio_softc *sc = (struct vio_softc *)self; >         struct virtio_softc *vsc = (struct virtio_softc *)parent; >         struct virtio_attach_args *va = aux; > -       int i, r, tx_max_segments, want_tso = 1; > +       int i, r, tx_max_segments, want_tso = 1, want_mtu = 1; > +       unsigned int device_mtu = 0; > +       bus_size_t txsize, rxsize; >         struct ifnet *ifp = &sc->sc_ac.ac_if; > >         if (vsc->sc_child != NULL) { > @@ -665,6 +661,8 @@ negotiate: >                 vsc->sc_driver_features |= VIRTIO_NET_F_HOST_TSO4; >                 vsc->sc_driver_features |= VIRTIO_NET_F_HOST_TSO6; >         } > +       if (want_mtu) > +               vsc->sc_driver_features |= VIRTIO_NET_F_MTU; >         if (va->va_nintr > 3 && ncpus > 1) >                 vsc->sc_driver_features |= VIRTIO_NET_F_MQ; > > @@ -690,6 +688,24 @@ negotiate: >                 goto negotiate; >         } > > +       if (virtio_has_feature(vsc, VIRTIO_NET_F_MTU)) { > +               device_mtu = virtio_read_device_config_2(vsc, > +                   VIRTIO_NET_CONFIG_MTU); > +               if (device_mtu > ETHER_MAX_HARDMTU_LEN) { > +                       /* > +                        * According to the standard we MUST supply > rx buffers > +                        * for the MTU length, but we only support up to > +                        * ETHER_MAX_HARDMTU_LEN. > +                        */ > +                       printf("%s: mtu %u not supported\n", > +                           sc->sc_dev.dv_xname, device_mtu); > +                       device_mtu = 0; > +                       want_mtu = 0; > +                       virtio_reset(vsc); > +                       goto negotiate; > +               } > +       } > + >         if (virtio_has_feature(vsc, VIRTIO_NET_F_MQ)) { >                 i = virtio_read_device_config_2(vsc, >                     VIRTIO_NET_CONFIG_MAX_QUEUES); > @@ -724,7 +740,7 @@ negotiate: >                 vio_get_lladdr(&sc->sc_ac, vsc); >         else >                 ether_fakeaddr(ifp); > -       printf(", address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr)); > +       printf(", address %s", ether_sprintf(sc->sc_ac.ac_enaddr)); > >         if (virtio_has_feature(vsc, VIRTIO_NET_F_MRG_RXBUF) || >             vsc->sc_version_1) { > @@ -757,11 +773,31 @@ negotiate: >                 sc->sc_rx_mbuf_size = 4 * 1024; >         } > > -       if (virtio_has_feature(vsc, VIRTIO_NET_F_MRG_RXBUF)) > -               ifp->if_hardmtu = MAXMCLBYTES; > -       else > -               ifp->if_hardmtu = sc->sc_rx_mbuf_size - sc- > >sc_hdr_size - > -                   ETHER_HDR_LEN; > +       ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN; > +       if (device_mtu) { > +               printf(", mtu %u", device_mtu); > +               ifp->if_hardmtu = device_mtu; > +       } > +       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); > +               ifp->if_hardmtu = MIN(ifp->if_hardmtu, > +                   sc->sc_rx_mbuf_size - sc->sc_hdr_size - > ETHER_HDR_LEN); > +       } > + > +       txsize = ifp->if_hardmtu + sc->sc_hdr_size + ETHER_HDR_LEN; > +       if (virtio_has_feature(vsc, VIRTIO_NET_F_HOST_TSO4) || > +           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; > +       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; > + > +       printf("\n"); > +       sc->sc_rxr_lwm = 2 * howmany(rxsize, sc->sc_rx_mbuf_size); > >         /* defrag for longer mbuf chains */ >         tx_max_segments = 16; > @@ -864,7 +900,7 @@ negotiate: >                 } >         } > > -       if (vio_alloc_mem(sc, tx_max_segments) < 0) > +       if (vio_alloc_mem(sc, tx_max_segments, txsize) < 0) >                 goto err; > >         strlcpy(ifp->if_xname, self->dv_xname, IFNAMSIZ); > @@ -891,6 +927,8 @@ negotiate: >         if_attach(ifp); >         ether_ifattach(ifp); >         vio_link_state(ifp); > +       if (device_mtu) > +               ifp->if_mtu = device_mtu; > >         if_attach_queues(ifp, sc->sc_nqueues); >         if_attach_iqueues(ifp, sc->sc_nqueues); > @@ -1025,8 +1063,7 @@ vio_init(struct ifnet *ifp) >                 struct vio_queue *vioq = &sc->sc_q[qidx]; > >                 mtx_enter(&vioq->viq_rxmtx); > -               if_rxr_init(&vioq->viq_rxring, > -                   2 * ((ifp->if_hardmtu / sc->sc_rx_mbuf_size) + 1), > +               if_rxr_init(&vioq->viq_rxring, sc->sc_rxr_lwm, >                     vioq->viq_rxvq->vq_num); >                 vio_populate_rx_mbufs(sc, vioq); >                 ifq_clr_oactive(vioq->viq_ifq); >