diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1125,9 +1125,38 @@ sx_assert(&ifnet_detach_sxlock, SX_XLOCKED); + /* + * Ideally the interface has been in down state before it been detached, + * but that requires lots of modification of drivers, and also it is a + * little safer to make it down here, as the interface has already been + * removed from the global network interface list. + */ + if ((ifp->if_flags & IFF_UP) != 0) { + if_down(ifp); + + /* Give the driver a chance to teardown */ + if (ifp->if_ioctl != NULL) { + struct ifreq ifr; + + ifr.ifr_flags = ifp->if_flags & 0xffff; + ifr.ifr_flagshigh = ifp->if_flags >> 16; + (void)(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); + } + } + /* * At this point we know the interface still was on the ifnet list * and we removed it so we are in a stable state. + * + * Note that, NET_EPOCH_WAIT() blocks the current thread but does not + * block other threads from entering net epoch. Well it is a nice + * synchronization point, that, threads those enter net epoch will see + * writes before NET_EPOCH_WAIT(), e.g. the ~IFF_UP flag. + * + * Other threads, typically the input / output path, should check the + * interface's up state, aka IFF_UP flag, after (re)entering net epoch. + * Otherwise it is possible to reference an interface in detaching + * progress and see consistency, e.g. NULL if_afdata[AF_INET6]. */ NET_EPOCH_WAIT(); @@ -1149,8 +1178,6 @@ taskqueue_drain(taskqueue_swi, &ifp->if_linktask); taskqueue_drain(taskqueue_swi, &ifp->if_addmultitask); - if_down(ifp); - #ifdef VIMAGE /* * On VNET shutdown abort here as the stack teardown will do all