On Tue, Nov 22, 2016 at 09:45:30PM +0100, Mike Belopuhov wrote: > vmd hackers and users seem to be able to trigger the assertion > below every time they do down and then up (with a dhclient for > instance): > > panic: kernel diagnostic assertion "m != NULL" failed: file > "/usr/src/sys/dev/pci/if_vio.c", line 1008 > Starting stack trace... > panic() at panic+0x10b > __assert() at __assert+0x25 > vio_rxeof() at vio_rxeof+0x1db > vio_rx_intr() at vio_rx_intr+0x28 > virtio_check_vqs() at virtio_check_vqs+0x8c > virtio_pci_legacy_intr() at virtio_pci_legacy_intr+0x71 > intr_handler() at intr_handler+0x67 > Xintr_legacy7() at Xintr_legacy7+0xdd > --- interrupt --- > > The culprit is that vio_rx_drain and vio_tx_drain functions > are not properly implemented: they m_freem mbufs attached to > ring slots but do nothing about unconfiguring the backend > virtqueue. This results in us thinking that the ring is > empty when we UP the interface the next time, but then a > completion event arrives for the slot that doesn't have an > associated mbuf anymore. > > To fix this properly a virtqueue draining code has to be > implemented, possibly based on what FreeBSD has. However, > there's nothing really wrong with leaving the rings the > way they are and "resuming" on UP, vio doesn't destroy > them anyway. > > The other part of the diff is removing virtio_reinit_start > and virtio_negotiate_features. I'm not sure why does it > make sense to re-negotiate features on stop, but reinit > functions mess around the receive ring and if I just leave > them there, the interface will not be operational on UP. > > So far this has been tested under vmd by rzalamena@ and > reyk@ but it would be nice to get it tested under the KVM. >
This has been on my to-do list to look at for a while, thanks a ton mikeb for tackling this. -ml > > > diff --git sys/dev/pci/if_vio.c sys/dev/pci/if_vio.c > index 97af2a2..96b5fdf 100644 > --- sys/dev/pci/if_vio.c > +++ sys/dev/pci/if_vio.c > @@ -596,10 +596,13 @@ vio_attach(struct device *parent, struct device *self, > void *aux) > ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); > vsc->sc_config_change = vio_config_change; > timeout_set(&sc->sc_txtick, vio_txtick, &sc->sc_vq[VQTX]); > timeout_set(&sc->sc_rxtick, vio_rxtick, &sc->sc_vq[VQRX]); > > + if_rxr_init(&sc->sc_rx_ring, 2 * ((ifp->if_hardmtu / MCLBYTES) + 1), > + sc->sc_vq[VQRX].vq_num); > + > if_attach(ifp); > ether_ifattach(ifp); > > return; > > @@ -664,12 +667,10 @@ int > vio_init(struct ifnet *ifp) > { > struct vio_softc *sc = ifp->if_softc; > > vio_stop(ifp, 0); > - if_rxr_init(&sc->sc_rx_ring, 2 * ((ifp->if_hardmtu / MCLBYTES) + 1), > - sc->sc_vq[VQRX].vq_num); > vio_populate_rx_mbufs(sc); > ifp->if_flags |= IFF_RUNNING; > ifq_clr_oactive(&ifp->if_snd); > vio_iff(sc); > vio_link_state(ifp); > @@ -689,21 +690,15 @@ vio_stop(struct ifnet *ifp, int disable) > /* only way to stop I/O and DMA is resetting... */ > virtio_reset(vsc); > vio_rxeof(sc); > if (vsc->sc_nvqs >= 3) > vio_ctrleof(&sc->sc_vq[VQCTL]); > - vio_tx_drain(sc); > - if (disable) > - vio_rx_drain(sc); > > - virtio_reinit_start(vsc); > - virtio_negotiate_features(vsc, vsc->sc_features, NULL); > virtio_start_vq_intr(vsc, &sc->sc_vq[VQRX]); > virtio_stop_vq_intr(vsc, &sc->sc_vq[VQTX]); > if (vsc->sc_nvqs >= 3) > virtio_start_vq_intr(vsc, &sc->sc_vq[VQCTL]); > - virtio_reinit_end(vsc); > if (vsc->sc_nvqs >= 3) { > if (sc->sc_ctrl_inuse != FREE) > sc->sc_ctrl_inuse = RESET; > wakeup(&sc->sc_ctrl_inuse); > } >