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
This commit is contained in:
Robert Watson 2009-04-23 09:32:30 +00:00
parent 5e51cafc60
commit 242a8e72eb
2 changed files with 53 additions and 33 deletions

View File

@ -229,7 +229,7 @@ ifnet_byindex_ref(u_short idx)
IFNET_RLOCK(); IFNET_RLOCK();
ifp = ifnet_byindex_locked(idx); ifp = ifnet_byindex_locked(idx);
if (ifp == NULL) { if (ifp == NULL || (ifp->if_flags & IFF_DYING)) {
IFNET_RUNLOCK(); IFNET_RUNLOCK();
return (NULL); return (NULL);
} }
@ -526,43 +526,21 @@ if_alloc(u_char type)
} }
/* /*
* Free the struct ifnet, the associated index, and the layer 2 common * Do the actual work of freeing a struct ifnet, associated index, and layer
* structure if needed. All the work is done in if_free_type(). * 2 common structure. This call is made when the last reference to an
* * interface is released.
* Do not add code to this function! Add it to if_free_type().
*/ */
void static void
if_free(struct ifnet *ifp) 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"));
/*
* 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().
*/
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));
if (!refcount_release(&ifp->if_refcount))
return;
IFNET_WLOCK(); IFNET_WLOCK();
KASSERT(ifp == ifnet_byindex_locked(ifp->if_index), KASSERT(ifp == ifnet_byindex_locked(ifp->if_index),
("%s: freeing unallocated ifnet", ifp->if_xname)); ("%s: freeing unallocated ifnet", ifp->if_xname));
ifnet_setbyindex(ifp->if_index, NULL); ifnet_setbyindex(ifp->if_index, NULL);
while (V_if_index > 0 && ifnet_byindex_locked(V_if_index) == NULL) while (V_if_index > 0 && ifnet_byindex_locked(V_if_index) == NULL)
V_if_index--; V_if_index--;
@ -576,6 +554,44 @@ if_free_type(struct ifnet *ifp, u_char type)
free(ifp, M_IFNET); free(ifp, M_IFNET);
} }
/*
* 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 ! */
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);
}
/*
* 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_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 void
if_ref(struct ifnet *ifp) if_ref(struct ifnet *ifp)
{ {
@ -588,7 +604,9 @@ void
if_rele(struct ifnet *ifp) if_rele(struct ifnet *ifp)
{ {
if_free(ifp); if (!refcount_release(&ifp->if_refcount))
return;
if_free_internal(ifp);
} }
void void

View File

@ -149,6 +149,7 @@ struct if_data {
#define IFF_PPROMISC 0x20000 /* (n) user-requested promisc mode */ #define IFF_PPROMISC 0x20000 /* (n) user-requested promisc mode */
#define IFF_MONITOR 0x40000 /* (n) user-requested monitor mode */ #define IFF_MONITOR 0x40000 /* (n) user-requested monitor mode */
#define IFF_STATICARP 0x80000 /* (n) static ARP */ #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 * 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: */ /* flags set internally only: */
#define IFF_CANTCHANGE \ #define IFF_CANTCHANGE \
(IFF_BROADCAST|IFF_POINTOPOINT|IFF_DRV_RUNNING|IFF_DRV_OACTIVE|\ (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. * Values for if_link_state.