Bug 203022

Summary: [patch] Can't destroy vlan dev with vtnet parent that is down and doing vlanhwfilter
Product: Base System Reporter: groos
Component: kernAssignee: Bryan Venteicher <bryanv>
Status: New ---    
Severity: Affects Some People CC: bryanv, thj
Priority: --- Keywords: patch
Version: CURRENT   
Hardware: Any   
OS: Any   

Description groos 2015-09-10 19:58:26 UTC
The ifconfig command hangs when trying to destroy a vlan device when all of the following are true:

* Vlan device has a vtnet device as parent
* The vtnet device is down
* The vtnet device has vlanhwfilter enabled

Running FreeBSD-11.0-CURRENT-amd64-20150826-r287169-disc1.iso in VirtualBox 4.2.12, using vtnet network devices.

To reproduce:

[hub] /root # ifconfig vtnet0 up
[hub] /root # ifconfig vtnet0 vlanhwfilter
[hub] /root # ifconfig vlan10 create
[hub] /root # ifconfig vlan10 vlan 6 vlandev vtnet0
[hub] /root # ifconfig vtnet0 down
[hub] /root # ifconfig vlan10 destroy

Inducing a textdump at this point reveals:

Tracing command ifconfig pid 29804 tid 100092 td 0xfffffe0001a90490
cpustop_handler() at cpustop_handler+0x28/frame 0xffffff8000227db0
ipi_nmi_handler() at ipi_nmi_handler+0x3f/frame 0xffffff8000227dd0
trap() at trap+0x15c/frame 0xffffff8000227f20
nmi_calltrap() at nmi_calltrap+0x8/frame 0xffffff8000227f20
--- trap 0x13, rip = 0xffffffff807dab5e, rsp = 0xffffff8000227fe0, rbp = 0xffffff801945f750 ---
Xapic_isr1() at Xapic_isr1+0x8e/frame 0xffffff801945f750
vtnet_exec_vlan_filter() at vtnet_exec_vlan_filter+0x95/frame 0xffffff801945f7d0
vtnet_update_vlan_filter() at vtnet_update_vlan_filter+0xf3/frame 0xffffff801945f800
vlan_unconfig_locked() at vlan_unconfig_locked+0x267/frame 0xffffff801945f850
vlan_unconfig() at vlan_unconfig+0x35/frame 0xffffff801945f870
vlan_clone_destroy() at vlan_clone_destroy+0x28/frame 0xffffff801945f8a0
if_clone_destroyif() at if_clone_destroyif+0xa9/frame 0xffffff801945f8d0
if_clone_destroy() at if_clone_destroy+0xcc/frame 0xffffff801945f8f0
ifioctl() at ifioctl+0x2be/frame 0xffffff801945f9e0
kern_ioctl() at kern_ioctl+0xb3/frame 0xffffff801945fa30
sys_ioctl() at sys_ioctl+0x101/frame 0xffffff801945fb10
amd64_syscall() at amd64_syscall+0x4dc/frame 0xffffff801945fc30
Xfast_syscall() at Xfast_syscall+0xf7/frame 0xffffff801945fc30
--- syscall (54, FreeBSD ELF64, sys_ioctl), rip = 0x20f50b4c, rsp = 0x7fffffffe2c8, rbp = 0x7fffffffeddf ---

Some printf's reveals we are stuck doing virtqueue_poll:

vtnet_exec_ctrl_cmd(struct vtnet_softc *sc, void *cookie, 
    struct sglist *sg, int readable, int writable)
{
        printf("%s:%i\n", __FUNCTION__, __LINE__);
        struct virtqueue *vq;
 
        vq = sc->vtnet_ctrl_vq;
 
        printf("%s:%i\n", __FUNCTION__, __LINE__);
        VTNET_CORE_LOCK_ASSERT(sc);
        printf("%s:%i\n", __FUNCTION__, __LINE__);
        KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_VQ,
            ("%s: CTRL_VQ feature not negotiated", __func__));
 
        printf("%s:%i\n", __FUNCTION__, __LINE__);
        if (!virtqueue_empty(vq))
                return;
        printf("%s:%i\n", __FUNCTION__, __LINE__);
        if (virtqueue_enqueue(vq, cookie, sg, readable, writable) != 0)
                return;
        printf("%s:%i\n", __FUNCTION__, __LINE__);
 
        /*
         * Poll for the response, but the command is likely already
         * done when we return from the notify.
         */
        printf("%s:%i\n", __FUNCTION__, __LINE__);
        virtqueue_notify(vq);
        printf("%s:%i\n", __FUNCTION__, __LINE__);
Stuck>> virtqueue_poll(vq, NULL);
        printf("%s:%i\n", __FUNCTION__, __LINE__);
}

My naive fix:

diff --git a/freebsd/sys/dev/virtio/network/if_vtnet.c b/freebsd/sys/dev/virtio/network/if_vtnet.c
index 7309110..35ae253 100644
--- a/freebsd/sys/dev/virtio/network/if_vtnet.c
+++ b/freebsd/sys/dev/virtio/network/if_vtnet.c
@@ -3402,6 +3414,7 @@ vtnet_update_vlan_filter(struct vtnet_softc *sc, int add, uint16_t tag)
                sc->vtnet_vlan_filter[idx] &= ~(1 << bit);
 
        if (ifp->if_capenable & IFCAP_VLAN_HWFILTER &&
+           ifp->if_drv_flags & IFF_DRV_RUNNING &&
            vtnet_exec_vlan_filter(sc, add, tag) != 0) {
                device_printf(sc->vtnet_dev,
                    "cannot %s VLAN %d %s the host filter table\n",