summaryrefslogtreecommitdiffstats
path: root/sys/net/if.c
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2009-04-23 09:32:30 +0000
committerrwatson <rwatson@FreeBSD.org>2009-04-23 09:32:30 +0000
commitc797841f0da1dbdd3f6280d977851c0b16d96d9c (patch)
tree7934d88798732f0f162eb9c5b1ea77a0f2be4c4b /sys/net/if.c
parenta0b32f475368730067f063113ffbc4315e12ad91 (diff)
downloadFreeBSD-src-c797841f0da1dbdd3f6280d977851c0b16d96d9c.zip
FreeBSD-src-c797841f0da1dbdd3f6280d977851c0b16d96d9c.tar.gz
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
Diffstat (limited to 'sys/net/if.c')
-rw-r--r--sys/net/if.c78
1 files changed, 48 insertions, 30 deletions
diff --git a/sys/net/if.c b/sys/net/if.c
index 4d4b676..d81d06b 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -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
OpenPOWER on IntegriCloud