diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -126,6 +126,7 @@ #include #include #include +#include #include #include #include @@ -2983,8 +2984,8 @@ return (sosetopt(so, &sopt)); } -int -sosetopt(struct socket *so, struct sockopt *sopt) +static inline int +sosetopt_locked(struct socket *so, struct sockopt *sopt) { int error, optval; struct linger l; @@ -2995,7 +2996,6 @@ struct mac extmac; #endif - CURVNET_SET(so->so_vnet); error = 0; if (sopt->sopt_level != SOL_SOCKET) { if (so->so_proto->pr_ctloutput != NULL) @@ -3182,10 +3182,24 @@ (void)(*so->so_proto->pr_ctloutput)(so, sopt); } bad: - CURVNET_RESTORE(); return (error); } +int +sosetopt(struct socket *so, struct sockopt *sopt) +{ + struct epoch_tracker et; + int retval; + + epoch_enter_sleepable(net_epoch_sleepable, &et); + CURVNET_SET(so->so_vnet); + retval = sosetopt_locked(so, sopt); + CURVNET_RESTORE(); + epoch_exit_sleepable(net_epoch_sleepable, &et); + + return (retval); +} + /* * Helper routine for getsockopt. */ @@ -3216,8 +3230,8 @@ return (error); } -int -sogetopt(struct socket *so, struct sockopt *sopt) +static inline int +sogetopt_locked(struct socket *so, struct sockopt *sopt) { int error, optval; struct linger l; @@ -3226,14 +3240,12 @@ struct mac extmac; #endif - CURVNET_SET(so->so_vnet); error = 0; if (sopt->sopt_level != SOL_SOCKET) { if (so->so_proto->pr_ctloutput != NULL) error = (*so->so_proto->pr_ctloutput)(so, sopt); else error = ENOPROTOOPT; - CURVNET_RESTORE(); return (error); } else { switch (sopt->sopt_name) { @@ -3388,10 +3400,24 @@ #ifdef MAC bad: #endif - CURVNET_RESTORE(); return (error); } +int +sogetopt(struct socket *so, struct sockopt *sopt) +{ + struct epoch_tracker et; + int retval; + + epoch_enter_sleepable(net_epoch_sleepable, &et); + CURVNET_SET(so->so_vnet); + retval = sogetopt_locked(so, sopt); + CURVNET_RESTORE(); + epoch_exit_sleepable(net_epoch_sleepable, &et); + + return (retval); +} + int soopt_getm(struct sockopt *sopt, struct mbuf **mp) { diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -110,6 +110,8 @@ offsetof(struct ifreq, ifr_ifru), "gap between ifr_name and ifr_ifru"); __read_mostly epoch_t net_epoch_preempt; +__read_mostly epoch_t net_epoch_sleepable; + #ifdef COMPAT_FREEBSD32 #include #include @@ -283,6 +285,7 @@ static int if_detach_internal(struct ifnet *, int, struct if_clone **); static void if_siocaddmulti(void *, int); static void if_link_ifnet(struct ifnet *); +static void if_wait_sleepable(void); static bool if_unlink_ifnet(struct ifnet *, bool); #ifdef VIMAGE static int if_vmove(struct ifnet *, struct vnet *); @@ -494,6 +497,13 @@ IFNET_WUNLOCK(); } +static void +if_wait_sleepable(void) +{ + /* Wait for pending user-space calls to complete. */ + epoch_wait_sleepable(net_epoch_sleepable); +} + static bool if_unlink_ifnet(struct ifnet *ifp, bool vmove) { @@ -554,6 +564,8 @@ } IFNET_WUNLOCK(); + if_wait_sleepable(); + for (int j = 0; j < i; j++) { if_vmove(pending[j], pending[j]->if_home_vnet); } @@ -1015,6 +1027,7 @@ { net_epoch_preempt = epoch_alloc("Net preemptible", EPOCH_PREEMPT); + net_epoch_sleepable = epoch_alloc("Net sleepable", EPOCH_SLEEPABLE); } SYSINIT(ifepochalloc, SI_SUB_EPOCH, SI_ORDER_ANY, if_epochalloc, NULL); @@ -1140,6 +1153,8 @@ CURVNET_SET_QUIET(ifp->if_vnet); found = if_unlink_ifnet(ifp, false); if (found) { + if_wait_sleepable(); + sx_xlock(&ifnet_detach_sxlock); if_detach_internal(ifp, 0, NULL); sx_xunlock(&ifnet_detach_sxlock); @@ -1437,6 +1452,8 @@ found = if_unlink_ifnet(ifp, true); MPASS(found); + if_wait_sleepable(); + /* Move the interface into the child jail/vnet. */ error = if_vmove(ifp, pr->pr_vnet); @@ -1494,6 +1511,9 @@ /* Get interface back from child jail/vnet. */ found = if_unlink_ifnet(ifp, true); MPASS(found); + + if_wait_sleepable(); + error = if_vmove(ifp, vnet_dst); CURVNET_RESTORE(); @@ -2891,8 +2911,8 @@ /* * Interface ioctls. */ -int -ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td) +static inline int +ifioctl_locked(struct socket *so, u_long cmd, caddr_t data, struct thread *td) { #ifdef COMPAT_FREEBSD32 union { @@ -3116,6 +3136,19 @@ return (error); } +int +ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td) +{ + struct epoch_tracker et; + int retval; + + epoch_enter_sleepable(net_epoch_sleepable, &et); + retval = ifioctl_locked(so, cmd, data, td); + epoch_exit_sleepable(net_epoch_sleepable, &et); + + return (retval); +} + /* * The code common to handling reference counted flags, * e.g., in ifpromisc() and if_allmulti(). diff --git a/sys/sys/epoch.h b/sys/sys/epoch.h --- a/sys/sys/epoch.h +++ b/sys/sys/epoch.h @@ -115,8 +115,10 @@ /* * Globally recognized epochs in the FreeBSD kernel. */ -/* Network preemptible epoch, declared in sys/net/if.c. */ + +/* Network epochs, declared in sys/net/if.c. */ extern epoch_t net_epoch_preempt; +extern epoch_t net_epoch_sleepable; #define NET_EPOCH_ENTER(et) epoch_enter_preempt(net_epoch_preempt, &(et)) #define NET_EPOCH_EXIT(et) epoch_exit_preempt(net_epoch_preempt, &(et)) #define NET_EPOCH_WAIT() epoch_wait_preempt(net_epoch_preempt)