Changes done by pseudo-driver *after* ether_ifattach() must be undone *before* ether_ifdetach(). Otherwise it is impossible to ensure we're leaving a stacked pseudo-interface in a correct state.
Since I don't want to modify every single driver calling ether_ifdetach() I'm using a new function, if_deactivate() to undo all the pseudo-driver stuff. It is safe to call this function multiple times because after the first time the parent interface won't have any pseudo-interface attached to it. I'm leaving splnet() below, even if pseudo-interfaces that need it should take care of raising it, to be on the safe side. More cleanup might happen later. This is a trivial change needed for upcoming if_input() work. Ok? Index: net/if.c =================================================================== RCS file: /cvs/src/sys/net/if.c,v retrieving revision 1.327 diff -u -p -r1.327 if.c --- net/if.c 7 Apr 2015 10:46:20 -0000 1.327 +++ net/if.c 9 Apr 2015 09:15:37 -0000 @@ -521,22 +521,12 @@ nettxintr(void) splx(s); } -/* - * Detach an interface from everything in the kernel. Also deallocate - * private resources. - */ void -if_detach(struct ifnet *ifp) +if_deactivate(struct ifnet *ifp) { - struct ifaddr *ifa; - struct ifg_list *ifg; - int s = splnet(); - struct domain *dp; + int s; - ifp->if_flags &= ~IFF_OACTIVE; - ifp->if_start = if_detached_start; - ifp->if_ioctl = if_detached_ioctl; - ifp->if_watchdog = NULL; + s = splnet(); /* * Call detach hooks from head to tail. To make sure detach @@ -545,12 +535,6 @@ if_detach(struct ifnet *ifp) */ dohooks(ifp->if_detachhooks, HOOK_REMOVE | HOOK_FREE); - /* Remove the watchdog timeout */ - timeout_del(ifp->if_slowtimo); - - /* Remove the link state task */ - task_del(systq, ifp->if_linkstatetask); - #if NBRIDGE > 0 /* Remove the interface from any bridge it is part of. */ if (ifp->if_bridgeport) @@ -562,6 +546,36 @@ if_detach(struct ifnet *ifp) if (ifp->if_carp && ifp->if_type != IFT_CARP) carp_ifdetach(ifp); #endif + + splx(s); +} + +/* + * Detach an interface from everything in the kernel. Also deallocate + * private resources. + */ +void +if_detach(struct ifnet *ifp) +{ + struct ifaddr *ifa; + struct ifg_list *ifg; + struct domain *dp; + int s; + + /* Undo pseudo-driver changes. */ + if_deactivate(ifp); + + s = splnet(); + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_start = if_detached_start; + ifp->if_ioctl = if_detached_ioctl; + ifp->if_watchdog = NULL; + + /* Remove the watchdog timeout */ + timeout_del(ifp->if_slowtimo); + + /* Remove the link state task */ + task_del(systq, ifp->if_linkstatetask); #if NBPFILTER > 0 bpfdetach(ifp); Index: net/if.h =================================================================== RCS file: /cvs/src/sys/net/if.h,v retrieving revision 1.161 diff -u -p -r1.161 if.h --- net/if.h 18 Mar 2015 12:23:15 -0000 1.161 +++ net/if.h 9 Apr 2015 09:15:37 -0000 @@ -446,6 +446,7 @@ void if_attach(struct ifnet *); void if_attachdomain(void); void if_attachtail(struct ifnet *); void if_attachhead(struct ifnet *); +void if_deactivate(struct ifnet *); void if_detach(struct ifnet *); void if_down(struct ifnet *); void if_downall(void); Index: net/if_ethersubr.c =================================================================== RCS file: /cvs/src/sys/net/if_ethersubr.c,v retrieving revision 1.191 diff -u -p -r1.191 if_ethersubr.c --- net/if_ethersubr.c 7 Apr 2015 10:46:20 -0000 1.191 +++ net/if_ethersubr.c 9 Apr 2015 09:16:45 -0000 @@ -802,6 +802,9 @@ ether_ifdetach(struct ifnet *ifp) struct ifih *ether_ifih; struct ether_multi *enm; + /* Undo pseudo-driver changes. */ + if_deactivate(ifp); + ether_ifih = SLIST_FIRST(&ifp->if_inputs); SLIST_REMOVE_HEAD(&ifp->if_inputs, ifih_next);