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