diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -645,6 +645,8 @@ ifq_init(&ifp->if_snd, ifp); refcount_init(&ifp->if_refcount, 1); /* Index reference. */ + refcount_init(&ifp->if_slowref, 1); + for (int i = 0; i < IFCOUNTERS; i++) ifp->if_counters[i] = counter_u64_alloc(M_WAITOK); ifp->if_get_counter = if_get_counter_default; @@ -753,6 +755,33 @@ NET_EPOCH_CALL(if_destroy, &ifp->if_epoch_ctx); } +/* + * Keep track of slow path configuration events. + * Returns true on success and false on failure. + */ +bool +if_slow_ref(struct ifnet *ifp) +{ + return (refcount_acquire_if_not_zero(&ifp->if_slowref)); +} + +void +if_slow_drain(struct ifnet *ifp) +{ + if (refcount_release(&ifp->if_slowref)) + return; + + while (refcount_load(&ifp->if_slowref) != 0) + pause("W", hz); +} + +void +if_slow_unref(struct ifnet *ifp) +{ + if (refcount_release(&ifp->if_slowref)) + return; +} + void ifq_init(struct ifaltq *ifq, struct ifnet *ifp) { @@ -2459,8 +2488,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; @@ -2887,6 +2916,19 @@ return (error); } +int +ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) +{ + int error; + + if (if_slow_ref(ifp) == false) + return (ENXIO); + error = ifhwioctl_sub(cmd, ifp, data, td); + if_slow_unref(ifp); + return (error); +} + + #ifdef COMPAT_FREEBSD32 struct ifconf32 { int32_t ifc_len; 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 @@ -312,6 +312,7 @@ void *if_linkmib; /* link-type-specific MIB data */ size_t if_linkmiblen; /* length of above data */ u_int if_refcount; /* reference count */ + u_int if_slowref; /* reference count (slow path) */ /* These fields are shared with struct if_data. */ uint8_t if_type; /* ethernet, tokenring, etc */ @@ -658,6 +659,9 @@ int if_printf(struct ifnet *, const char *, ...) __printflike(2, 3); void if_ref(struct ifnet *); void if_rele(struct ifnet *); +bool if_slow_ref(struct ifnet *) __result_use_check; +void if_slow_drain(struct ifnet *); +void if_slow_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 *);