On Wed, Nov 23, 2016 at 00:09 +0100, s...@openbsd.org wrote:
> On Tue, 22 Nov 2016, 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.
> 
> No, that's wrong. vio_*_drain don't need to do anything with the rings 
> because the device is reset before they are called. (the virtio_reset() 
> call). It sounds like vmd does not implement the reset properly. When the 
> device is reset, it
> 
> * must stop all dma activities to the rings
> * must stop interrupts
> * should probably forget about the configured virtqueues.
> 
> It must do that before the io-port write to the status register finishes 
> and returns control to the guest.
> 
> As far as I can see, vio(4) does even the right thing if an interrupt 
> occurs between splnet() and virtio_reset(). It will call vio_rxeof() after 
> the reset, which will remove all used descriptors from the ring. Even if 
> vio_rx_intr() would be called after splx(), there cannot be any used 
> descriptors in the ring, the call to vio_rxeof() will return 0, and 
> vio_rx_intr() will do nothing. But this could be made more explicit (and 
> robust?) by putting a check for IF_RUNNING into vio_rx_intr().
> 
> > 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.
> 
> vio must reset the device because otherwise it could not remove 
> descriptors from the available ring before the device has used them. And 
> the device won't use them unless packets arrive.
> 
> > 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.
> 
> The reinit is necessary after reset.
> 
> Cheers,
> Stefan
> 
> 

Yes, after looking closer at virtio code I agree with you.
However, stop/init is purely OpenBSD specific action.  There
are no provisions or requirements from the hardware really.
Thus we can treat UP/DOWN as purely software state and don't
stop/reinit the PCI device.

Reply via email to