Module Name:    src
Committed By:   ozaki-r
Date:           Mon May 16 01:16:24 UTC 2016

Modified Files:
        src/sys/net: if.c if.h

Log Message:
Replace ifnet_lock with if_get and if_put

ifnet_lock is a dedicated method to safely destroy an interface over running
ioctl operations. Replace it with a more generic mechanism using psref(9).


To generate a diff of this commit:
cvs rdiff -u -r1.335 -r1.336 src/sys/net/if.c
cvs rdiff -u -r1.205 -r1.206 src/sys/net/if.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if.c
diff -u src/sys/net/if.c:1.335 src/sys/net/if.c:1.336
--- src/sys/net/if.c:1.335	Mon May 16 01:06:31 2016
+++ src/sys/net/if.c	Mon May 16 01:16:24 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.c,v 1.335 2016/05/16 01:06:31 ozaki-r Exp $	*/
+/*	$NetBSD: if.c,v 1.336 2016/05/16 01:16:24 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc.
@@ -90,7 +90,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.335 2016/05/16 01:06:31 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.336 2016/05/16 01:16:24 ozaki-r Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_inet.h"
@@ -192,10 +192,6 @@ pfil_head_t *	if_pfil;
 static kauth_listener_t if_listener;
 
 static int doifioctl(struct socket *, u_long, void *, struct lwp *);
-static int ifioctl_attach(struct ifnet *);
-static void ifioctl_detach(struct ifnet *);
-static void ifnet_lock_enter(struct ifnet_lock *);
-static void ifnet_lock_exit(struct ifnet_lock *);
 static void if_detach_queues(struct ifnet *, struct ifqueue *);
 static void sysctl_sndq_setup(struct sysctllog **, const char *,
     struct ifaltq *);
@@ -348,10 +344,6 @@ int
 if_nullioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
 
-	/* Wake ifioctl_detach(), who may wait for all threads to
-	 * quit the critical section.
-	 */
-	cv_signal(&ifp->if_ioctl_lock->il_emptied);
 	return ENXIO;
 }
 
@@ -653,6 +645,7 @@ if_initialize(ifnet_t *ifp)
 
 	PSLIST_ENTRY_INIT(ifp, if_pslist_entry);
 	psref_target_init(&ifp->if_psref, ifnet_psref_class);
+	ifp->if_ioctl_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE);
 
 	IFNET_LOCK();
 	if_getindex(ifp);
@@ -665,8 +658,12 @@ if_initialize(ifnet_t *ifp)
 void
 if_register(ifnet_t *ifp)
 {
-	if (ifioctl_attach(ifp) != 0)
-		panic("%s: ifioctl_attach() failed", __func__);
+	/*
+	 * If the driver has not supplied its own if_ioctl, then
+	 * supply the default.
+	 */
+	if (ifp->if_ioctl == NULL)
+		ifp->if_ioctl = ifioctl_common;
 
 	sysctl_sndq_setup(&ifp->if_sysctl_log, ifp->if_xname, &ifp->if_snd);
 
@@ -1047,6 +1044,9 @@ if_detach(struct ifnet *ifp)
 	s = splnet();
 
 	sysctl_teardown(&ifp->if_sysctl_log);
+	mutex_enter(ifp->if_ioctl_lock);
+	ifp->if_ioctl = if_nullioctl;
+	mutex_exit(ifp->if_ioctl_lock);
 
 	IFNET_LOCK();
 	ifindex2ifnet[ifp->if_index] = NULL;
@@ -1059,6 +1059,9 @@ if_detach(struct ifnet *ifp)
 	psref_target_destroy(&ifp->if_psref, ifnet_psref_class);
 	PSLIST_ENTRY_DESTROY(ifp, if_pslist_entry);
 
+	mutex_obj_free(ifp->if_ioctl_lock);
+	ifp->if_ioctl_lock = NULL;
+
 	if (ifp->if_slowtimo != NULL) {
 		ifp->if_slowtimo = NULL;
 		callout_halt(ifp->if_slowtimo_ch, NULL);
@@ -1191,8 +1194,6 @@ again:
 	/* Announce that the interface is gone. */
 	rt_ifannouncemsg(ifp, IFAN_DEPARTURE);
 
-	ifioctl_detach(ifp);
-
 	IF_AFDATA_LOCK_DESTROY(ifp);
 
 	softint_disestablish(ifp->if_link_si);
@@ -1300,13 +1301,18 @@ if_clone_create(const char *name)
 {
 	struct if_clone *ifc;
 	int unit;
+	struct ifnet *ifp;
+	struct psref psref;
 
 	ifc = if_clone_lookup(name, &unit);
 	if (ifc == NULL)
 		return EINVAL;
 
-	if (ifunit(name) != NULL)
+	ifp = if_get(name, &psref);
+	if (ifp != NULL) {
+		if_put(ifp, &psref);
 		return EEXIST;
+	}
 
 	return (*ifc->ifc_create)(ifc, unit);
 }
@@ -1319,17 +1325,29 @@ if_clone_destroy(const char *name)
 {
 	struct if_clone *ifc;
 	struct ifnet *ifp;
+	struct psref psref;
 
 	ifc = if_clone_lookup(name, NULL);
 	if (ifc == NULL)
 		return EINVAL;
 
-	ifp = ifunit(name);
+	if (ifc->ifc_destroy == NULL)
+		return EOPNOTSUPP;
+
+	ifp = if_get(name, &psref);
 	if (ifp == NULL)
 		return ENXIO;
 
-	if (ifc->ifc_destroy == NULL)
-		return EOPNOTSUPP;
+	/* We have to disable ioctls here */
+	mutex_enter(ifp->if_ioctl_lock);
+	ifp->if_ioctl = if_nullioctl;
+	mutex_exit(ifp->if_ioctl_lock);
+
+	/*
+	 * We cannot call ifc_destroy with holding ifp.
+	 * Releasing ifp here is safe thanks to if_clone_mtx.
+	 */
+	if_put(ifp, &psref);
 
 	return (*ifc->ifc_destroy)(ifp);
 }
@@ -2421,32 +2439,6 @@ ifaddrpref_ioctl(struct socket *so, u_lo
 	}
 }
 
-static void
-ifnet_lock_enter(struct ifnet_lock *il)
-{
-	uint64_t *nenter;
-
-	/* Before trying to acquire the mutex, increase the count of threads
-	 * who have entered or who wait to enter the critical section.
-	 * Avoid one costly locked memory transaction by keeping a count for
-	 * each CPU.
-	 */
-	nenter = percpu_getref(il->il_nenter);
-	(*nenter)++;
-	percpu_putref(il->il_nenter);
-	mutex_enter(&il->il_lock);
-}
-
-static void
-ifnet_lock_exit(struct ifnet_lock *il)
-{
-	/* Increase the count of threads who have exited the critical
-	 * section.  Increase while we still hold the lock.
-	 */
-	il->il_nexit++;
-	mutex_exit(&il->il_lock);
-}
-
 /*
  * Interface ioctls.
  */
@@ -2465,6 +2457,8 @@ doifioctl(struct socket *so, u_long cmd,
 	struct oifreq *oifr = NULL;
 #endif
 	int r;
+	struct psref psref;
+	int bound = curlwp->l_pflag & LP_BOUND;
 
 	switch (cmd) {
 #ifdef COMPAT_OIFREQ
@@ -2493,24 +2487,29 @@ doifioctl(struct socket *so, u_long cmd,
 #endif
 		ifr = data;
 
-	ifp = ifunit(ifr->ifr_name);
-
 	switch (cmd) {
 	case SIOCIFCREATE:
 	case SIOCIFDESTROY:
+		curlwp->l_pflag |= LP_BOUND;
 		if (l != NULL) {
+			ifp = if_get(ifr->ifr_name, &psref);
 			error = kauth_authorize_network(l->l_cred,
 			    KAUTH_NETWORK_INTERFACE,
 			    KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp,
 			    (void *)cmd, NULL);
-			if (error != 0)
+			if (ifp != NULL)
+				if_put(ifp, &psref);
+			if (error != 0) {
+				curlwp->l_pflag ^= bound ^ LP_BOUND;
 				return error;
+			}
 		}
 		mutex_enter(&if_clone_mtx);
 		r = (cmd == SIOCIFCREATE) ?
 			if_clone_create(ifr->ifr_name) :
 			if_clone_destroy(ifr->ifr_name);
 		mutex_exit(&if_clone_mtx);
+		curlwp->l_pflag ^= bound ^ LP_BOUND;
 		return r;
 
 	case SIOCIFGCLONERS:
@@ -2521,8 +2520,12 @@ doifioctl(struct socket *so, u_long cmd,
 		}
 	}
 
-	if (ifp == NULL)
+	curlwp->l_pflag |= LP_BOUND;
+	ifp = if_get(ifr->ifr_name, &psref);
+	if (ifp == NULL) {
+		curlwp->l_pflag ^= bound ^ LP_BOUND;
 		return ENXIO;
+	}
 
 	switch (cmd) {
 	case SIOCALIFADDR:
@@ -2557,13 +2560,14 @@ doifioctl(struct socket *so, u_long cmd,
 			    KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp,
 			    (void *)cmd, NULL);
 			if (error != 0)
-				return error;
+				goto out;
 		}
 	}
 
 	oif_flags = ifp->if_flags;
 
-	ifnet_lock_enter(ifp->if_ioctl_lock);
+	mutex_enter(ifp->if_ioctl_lock);
+
 	error = (*ifp->if_ioctl)(ifp, cmd, data);
 	if (error != ENOTTY)
 		;
@@ -2590,100 +2594,13 @@ doifioctl(struct socket *so, u_long cmd,
 		ifreqn2o(oifr, ifr);
 #endif
 
-	ifnet_lock_exit(ifp->if_ioctl_lock);
+	mutex_exit(ifp->if_ioctl_lock);
+out:
+	if_put(ifp, &psref);
+	curlwp->l_pflag ^= bound ^ LP_BOUND;
 	return error;
 }
 
-/* This callback adds to the sum in `arg' the number of
- * threads on `ci' who have entered or who wait to enter the
- * critical section.
- */
-static void
-ifnet_lock_sum(void *p, void *arg, struct cpu_info *ci)
-{
-	uint64_t *sum = arg, *nenter = p;
-
-	*sum += *nenter;
-}
-
-/* Return the number of threads who have entered or who wait
- * to enter the critical section on all CPUs.
- */
-static uint64_t
-ifnet_lock_entrances(struct ifnet_lock *il)
-{
-	uint64_t sum = 0;
-
-	percpu_foreach(il->il_nenter, ifnet_lock_sum, &sum);
-
-	return sum;
-}
-
-static int
-ifioctl_attach(struct ifnet *ifp)
-{
-	struct ifnet_lock *il;
-
-	/* If the driver has not supplied its own if_ioctl, then
-	 * supply the default.
-	 */
-	if (ifp->if_ioctl == NULL)
-		ifp->if_ioctl = ifioctl_common;
-
-	/* Create an ifnet_lock for synchronizing ifioctls. */
-	if ((il = kmem_zalloc(sizeof(*il), KM_SLEEP)) == NULL)
-		return ENOMEM;
-
-	il->il_nenter = percpu_alloc(sizeof(uint64_t));
-	if (il->il_nenter == NULL) {
-		kmem_free(il, sizeof(*il));
-		return ENOMEM;
-	}
-
-	mutex_init(&il->il_lock, MUTEX_DEFAULT, IPL_NONE);
-	cv_init(&il->il_emptied, ifp->if_xname);
-
-	ifp->if_ioctl_lock = il;
-
-	return 0;
-}
-
-/*
- * This must not be called until after `ifp' has been withdrawn from the
- * ifnet tables so that ifioctl() cannot get a handle on it by calling
- * ifunit().
- */
-static void
-ifioctl_detach(struct ifnet *ifp)
-{
-	struct ifnet_lock *il;
-
-	il = ifp->if_ioctl_lock;
-	mutex_enter(&il->il_lock);
-	/* Install if_nullioctl to make sure that any thread that
-	 * subsequently enters the critical section will quit it
-	 * immediately and signal the condition variable that we
-	 * wait on, below.
-	 */
-	ifp->if_ioctl = if_nullioctl;
-	/* Sleep while threads are still in the critical section or
-	 * wait to enter it.
-	 */
-	while (ifnet_lock_entrances(il) != il->il_nexit)
-		cv_wait(&il->il_emptied, &il->il_lock);
-	/* At this point, we are the only thread still in the critical
-	 * section, and no new thread can get a handle on the ifioctl
-	 * lock, so it is safe to free its memory.
-	 */
-	mutex_exit(&il->il_lock);
-	ifp->if_ioctl_lock = NULL;
-	percpu_free(il->il_nenter, sizeof(uint64_t));
-	il->il_nenter = NULL;
-	cv_destroy(&il->il_emptied);
-	mutex_destroy(&il->il_lock);
-	kmem_free(il, sizeof(*il));
-}
-
 /*
  * Return interface configuration
  * of system.  List may be used
@@ -2906,6 +2823,7 @@ if_addr_init(ifnet_t *ifp, struct ifaddr
 	if (ifp->if_initaddr != NULL)
 		rc = (*ifp->if_initaddr)(ifp, ifa, src);
 	else if (src ||
+		/* FIXME: may not hold if_ioctl_lock */
 	         (rc = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, ifa)) == ENOTTY)
 		rc = (*ifp->if_ioctl)(ifp, SIOCINITIFADDR, ifa);
 
@@ -2972,6 +2890,7 @@ if_flags_set(ifnet_t *ifp, const short f
 		memset(&ifr, 0, sizeof(ifr));
 
 		ifr.ifr_flags = flags & ~IFF_CANTCHANGE;
+		/* FIXME: may not hold if_ioctl_lock */
 		rc = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, &ifr);
 
 		if (rc != 0 && cantflags != 0)

Index: src/sys/net/if.h
diff -u src/sys/net/if.h:1.205 src/sys/net/if.h:1.206
--- src/sys/net/if.h:1.205	Mon May 16 01:06:31 2016
+++ src/sys/net/if.h	Mon May 16 01:16:24 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.h,v 1.205 2016/05/16 01:06:31 ozaki-r Exp $	*/
+/*	$NetBSD: if.h,v 1.206 2016/05/16 01:16:24 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@@ -211,32 +211,11 @@ struct ifqueue {
 	kmutex_t	*ifq_lock;
 };
 
-struct ifnet_lock;
-
 #ifdef _KERNEL
-#include <sys/condvar.h>
 #include <sys/percpu.h>
 #include <sys/callout.h>
 #include <sys/rwlock.h>
 
-struct ifnet_lock {
-	kmutex_t il_lock;	/* Protects the critical section. */
-	uint64_t il_nexit;	/* Counts threads across all CPUs who
-				 * have exited the critical section.
-				 * Access to il_nexit is synchronized
-				 * by il_lock.
-				 */
-	percpu_t *il_nenter;	/* Counts threads on each CPU who have
-				 * entered or who wait to enter the
-				 * critical section protected by il_lock.
-				 * Synchronization is not required.
-				 */
-	kcondvar_t il_emptied;	/* The ifnet_lock user must arrange for
-				 * the last threads in the critical
-				 * section to signal this condition variable
-				 * before they leave.
-				 */
-};
 #endif /* _KERNEL */
 
 /*
@@ -351,7 +330,7 @@ typedef struct ifnet {
 	int (*if_mcastop)(struct ifnet *, const unsigned long,
 	    const struct sockaddr *);
 	int (*if_setflags)(struct ifnet *, const short);
-	struct ifnet_lock *if_ioctl_lock;
+	kmutex_t	*if_ioctl_lock;
 #ifdef _KERNEL /* XXX kvm(3) */
 	struct callout *if_slowtimo_ch;
 	struct krwlock	*if_afdata_lock;

Reply via email to