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. 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); }