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

Reply via email to