diff --git a/sys/dev/usb/net/if_usie.c b/sys/dev/usb/net/if_usie.c --- a/sys/dev/usb/net/if_usie.c +++ b/sys/dev/usb/net/if_usie.c @@ -483,6 +483,7 @@ usbd_transfer_unsetup(sc->sc_if_xfer, USIE_IF_N_XFER); bpfdetach(sc->sc_ifp); if_detach(sc->sc_ifp); + if_ioctl_drain(sc->sc_ifp); if_free(sc->sc_ifp); sc->sc_ifp = NULL; } diff --git a/sys/dev/usb/net/uhso.c b/sys/dev/usb/net/uhso.c --- a/sys/dev/usb/net/uhso.c +++ b/sys/dev/usb/net/uhso.c @@ -693,8 +693,9 @@ uhso_if_stop(sc); bpfdetach(sc->sc_ifp); if_detach(sc->sc_ifp); - if_free(sc->sc_ifp); mtx_unlock(&sc->sc_mtx); + if_ioctl_drain(sc->sc_ifp); + if_free(sc->sc_ifp); usbd_transfer_unsetup(sc->sc_if_xfer, UHSO_IFNET_MAX); } diff --git a/sys/dev/usb/net/usb_ethernet.c b/sys/dev/usb/net/usb_ethernet.c --- a/sys/dev/usb/net/usb_ethernet.c +++ b/sys/dev/usb/net/usb_ethernet.c @@ -292,6 +292,7 @@ /* free unit */ free_unr(ueunit, ue->ue_unit); if (ue->ue_ifp != NULL) { + if_ioctl_drain(ue->ue_ifp); if_free(ue->ue_ifp); ue->ue_ifp = NULL; } @@ -311,6 +312,9 @@ ifp = ue->ue_ifp; if (ifp != NULL) { + /* drain all IOCTLs */ + if_ioctl_drain(ifp); + /* we are not running any more */ UE_LOCK(ue); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; diff --git a/sys/dev/usb/usb_pf.c b/sys/dev/usb/usb_pf.c --- a/sys/dev/usb/usb_pf.c +++ b/sys/dev/usb/usb_pf.c @@ -232,6 +232,7 @@ USB_BUS_UNLOCK(ubus); bpfdetach(ifp); if_detach(ifp); + if_ioctl_drain(ifp); if_free(ifp); ifc_free_unit(ifc, unit); diff --git a/sys/net/if.h b/sys/net/if.h --- a/sys/net/if.h +++ b/sys/net/if.h @@ -163,6 +163,7 @@ #define IFF_DYING 0x200000 /* (n) interface is winding down */ #define IFF_RENAMING 0x400000 /* (n) interface is being renamed */ #define IFF_NOGROUP 0x800000 /* (n) interface is not part of any groups */ +#define IFF_IOCTL_DRAINED 0x1000000 /* (n) control path has been drained */ /* * Old names for driver flags so that user space tools can continue to use @@ -177,7 +178,7 @@ #define IFF_CANTCHANGE \ (IFF_BROADCAST|IFF_POINTOPOINT|IFF_DRV_RUNNING|IFF_DRV_OACTIVE|\ IFF_SIMPLEX|IFF_MULTICAST|IFF_ALLMULTI|IFF_PROMISC|\ - IFF_DYING|IFF_CANTCONFIG|IFF_KNOWSEPOCH) + IFF_DYING|IFF_CANTCONFIG|IFF_KNOWSEPOCH|IFF_IOCTL_DRAINED) /* * Values for if_link_state. diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -325,6 +325,9 @@ SX_SYSINIT_FLAGS(ifnet_detach, &ifnet_detach_sxlock, "ifnet_detach_sx", SX_RECURSE); +static struct sx ifnet_ioctl_sxlock; +SX_SYSINIT_FLAGS(ifnet_ioctl, &ifnet_ioctl_sxlock, "ifnet_ioctl_sx", SX_RECURSE); + /* * The allocation of network interfaces is a rather non-atomic affair; we * need to select an index before we are ready to expose the interface for @@ -769,6 +772,39 @@ NET_EPOCH_CALL(if_destroy, &ifp->if_epoch_ctx); } +/* + * Keep track of if_ioctl configuration events. + * Returns true on success and false on failure. + */ +bool +if_ioctl_ref(struct ifnet *ifp) +{ + bool retval; + + if (ifp->if_flags & IFF_IOCTL_DRAINED) + return (false); + + sx_slock(&ifnet_ioctl_sxlock); + retval = (ifp->if_flags & IFF_IOCTL_DRAINED) == 0; + if (!retval) + sx_sunlock(&ifnet_ioctl_sxlock); + return (retval); +} + +void +if_ioctl_drain(struct ifnet *ifp) +{ + sx_xlock(&ifnet_ioctl_sxlock); + ifp->if_flags |= IFF_IOCTL_DRAINED; + sx_xunlock(&ifnet_ioctl_sxlock); +} + +void +if_ioctl_unref(struct ifnet *ifp) +{ + sx_sunlock(&ifnet_ioctl_sxlock); +} + void ifq_init(struct ifaltq *ifq, struct ifnet *ifp) { @@ -2458,8 +2494,8 @@ /* * Hardware specific interface ioctls. */ -int -ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) +static inline int +ifhwioctl_sub(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) { struct ifreq *ifr; int error = 0, do_ifup = 0; @@ -2883,6 +2919,18 @@ return (error); } +int +ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) +{ + int error; + + if (if_ioctl_ref(ifp) == false) + return (ENXIO); + error = ifhwioctl_sub(cmd, ifp, data, td); + if_ioctl_unref(ifp); + return (error); +} + /* * Interface ioctls. */ diff --git a/sys/net/if_var.h b/sys/net/if_var.h --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -663,6 +663,9 @@ void if_ref(struct ifnet *); void if_rele(struct ifnet *); bool __result_use_check if_try_ref(struct ifnet *); +bool __result_use_check if_ioctl_ref(struct ifnet *); +void if_ioctl_drain(struct ifnet *); +void if_ioctl_unref(struct ifnet *); int if_setlladdr(struct ifnet *, const u_char *, int); int if_tunnel_check_nesting(struct ifnet *, struct mbuf *, uint32_t, int); void if_up(struct ifnet *);