svn commit: r191416 - head/sys/net

Robert Watson rwatson at FreeBSD.org
Thu Apr 23 09:32:31 UTC 2009


Author: rwatson
Date: Thu Apr 23 09:32:30 2009
New Revision: 191416
URL: http://svn.freebsd.org/changeset/base/191416

Log:
  Add a new interface flag, IFF_DYING, which is set when a device driver
  calls if_free(), and remains set if the refcount is elevated.  IF_DYING
  skips the bit in the if_flags bitmask previously used by IFF_NEEDSGIANT,
  so that an MFC can be done without changing which bit is used, as
  IFF_NEEDSGIANT is still present in 7.x.
  
  ifnet_byindex_ref() checks for IFF_DYING and returns NULL if it is set,
  preventing new references from by acquired by index, preventing
  monitoring sysctls from seeing it.  Other lookup mechanisms currently
  do not check IFF_DYING, but may need to in the future.
  
  MFC after:	3 weeks

Modified:
  head/sys/net/if.c
  head/sys/net/if.h

Modified: head/sys/net/if.c
==============================================================================
--- head/sys/net/if.c	Thu Apr 23 09:11:37 2009	(r191415)
+++ head/sys/net/if.c	Thu Apr 23 09:32:30 2009	(r191416)
@@ -229,7 +229,7 @@ ifnet_byindex_ref(u_short idx)
 
 	IFNET_RLOCK();
 	ifp = ifnet_byindex_locked(idx);
-	if (ifp == NULL) {
+	if (ifp == NULL || (ifp->if_flags & IFF_DYING)) {
 		IFNET_RUNLOCK();
 		return (NULL);
 	}
@@ -526,56 +526,72 @@ if_alloc(u_char type)
 }
 
 /*
- * Free the struct ifnet, the associated index, and the layer 2 common
- * structure if needed.  All the work is done in if_free_type().
- *
- * Do not add code to this function!  Add it to if_free_type().
+ * Do the actual work of freeing a struct ifnet, associated index, and layer
+ * 2 common structure.  This call is made when the last reference to an
+ * interface is released.
  */
-void
-if_free(struct ifnet *ifp)
+static void
+if_free_internal(struct ifnet *ifp)
 {
 
-	if_free_type(ifp, ifp->if_alloctype);
+	KASSERT((ifp->if_flags & IFF_DYING),
+	    ("if_free_internal: interface not dying"));
+
+	IFNET_WLOCK();
+	KASSERT(ifp == ifnet_byindex_locked(ifp->if_index),
+	    ("%s: freeing unallocated ifnet", ifp->if_xname));
+
+	ifnet_setbyindex(ifp->if_index, NULL);
+	while (V_if_index > 0 && ifnet_byindex_locked(V_if_index) == NULL)
+		V_if_index--;
+	IFNET_WUNLOCK();
+
+	if (if_com_free[ifp->if_alloctype] != NULL)
+		if_com_free[ifp->if_alloctype](ifp->if_l2com,
+		    ifp->if_alloctype);
+
+	IF_ADDR_LOCK_DESTROY(ifp);
+	free(ifp, M_IFNET);
 }
 
 /*
- * Do the actual work of freeing a struct ifnet, associated index, and
- * layer 2 common structure.  This version should only be called by
- * intefaces that switch their type after calling if_alloc().
+ * This version should only be called by intefaces that switch their type
+ * after calling if_alloc().  if_free_type() will go away again now that we
+ * have if_alloctype to cache the original allocation type.  For now, assert
+ * that they match, since we require that in practice.
  */
 void
 if_free_type(struct ifnet *ifp, u_char type)
 {
 	INIT_VNET_NET(curvnet); /* ifp->if_vnet can be NULL here ! */
 
-	/*
-	 * Some drivers modify if_type, so we can't rely on it being the
-	 * same in free as it was in alloc.  Now that we have if_alloctype,
-	 * we should just use that, but drivers expect to pass a type.
-	 */
 	KASSERT(ifp->if_alloctype == type,
 	    ("if_free_type: type (%d) != alloctype (%d)", type,
 	    ifp->if_alloctype));
 
+	ifp->if_flags |= IFF_DYING;			/* XXX: Locking */
 	if (!refcount_release(&ifp->if_refcount))
 		return;
+	if_free_internal(ifp);
+}
 
-	IFNET_WLOCK();
-	KASSERT(ifp == ifnet_byindex_locked(ifp->if_index),
-	    ("%s: freeing unallocated ifnet", ifp->if_xname));
-	ifnet_setbyindex(ifp->if_index, NULL);
-	while (V_if_index > 0 && ifnet_byindex_locked(V_if_index) == NULL)
-		V_if_index--;
-	IFNET_WUNLOCK();
-
-	if (if_com_free[ifp->if_alloctype] != NULL)
-		if_com_free[ifp->if_alloctype](ifp->if_l2com,
-		    ifp->if_alloctype);
+/*
+ * This is the normal version of if_free(), used by device drivers to free a
+ * detached network interface.  The contents of if_free_type() will move into
+ * here when if_free_type() goes away.
+ */
+void
+if_free(struct ifnet *ifp)
+{
 
-	IF_ADDR_LOCK_DESTROY(ifp);
-	free(ifp, M_IFNET);
+	if_free_type(ifp, ifp->if_alloctype);
 }
 
+/*
+ * Interfaces to keep an ifnet type-stable despite the possibility of the
+ * driver calling if_free().  If there are additional references, we defer
+ * freeing the underlying data structure.
+ */
 void
 if_ref(struct ifnet *ifp)
 {
@@ -588,7 +604,9 @@ void
 if_rele(struct ifnet *ifp)
 {
 
-	if_free(ifp);
+	if (!refcount_release(&ifp->if_refcount))
+		return;
+	if_free_internal(ifp);
 }
 
 void

Modified: head/sys/net/if.h
==============================================================================
--- head/sys/net/if.h	Thu Apr 23 09:11:37 2009	(r191415)
+++ head/sys/net/if.h	Thu Apr 23 09:32:30 2009	(r191416)
@@ -149,6 +149,7 @@ struct if_data {
 #define	IFF_PPROMISC	0x20000		/* (n) user-requested promisc mode */
 #define	IFF_MONITOR	0x40000		/* (n) user-requested monitor mode */
 #define	IFF_STATICARP	0x80000		/* (n) static ARP */
+#define	IFF_DYING	0x200000	/* (n) interface is winding down */
 
 /*
  * Old names for driver flags so that user space tools can continue to use
@@ -162,7 +163,8 @@ struct if_data {
 /* flags set internally only: */
 #define	IFF_CANTCHANGE \
 	(IFF_BROADCAST|IFF_POINTOPOINT|IFF_DRV_RUNNING|IFF_DRV_OACTIVE|\
-	    IFF_SIMPLEX|IFF_MULTICAST|IFF_ALLMULTI|IFF_SMART|IFF_PROMISC)
+	    IFF_SIMPLEX|IFF_MULTICAST|IFF_ALLMULTI|IFF_SMART|IFF_PROMISC|\
+	    IFF_DYING)
 
 /*
  * Values for if_link_state.


More information about the svn-src-head mailing list