netstack.c revision 82374bf25f65d3bc0da9a54a283adc3f28f8a665
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/sysmacros.h>
#include <sys/tuneable.h>
#include <vm/seg_kmem.h>
#include <sys/netstack.h>
/*
* What we use so that the zones framework can tell us about new zones,
* which we use to create new stacks.
*/
static zone_key_t netstack_zone_key;
static int netstack_initialized = 0;
/*
* Track the registered netstacks.
* The global lock protects
* - ns_reg
* - the list starting at netstack_head and following the netstack_next
* pointers.
*/
static kmutex_t netstack_g_lock;
/*
*/
/*
* Global list of existing stacks. We use this when a new zone with
* an exclusive IP instance is created.
*
* Note that in some cases a netstack_t needs to stay around after the zone
* has gone away. This is because there might be outstanding references
* (from TCP TIME_WAIT connections, IPsec state, etc). The netstack_t data
* structure and all the foo_stack_t's hanging off of it will be cleaned up
* when the last reference to it is dropped.
* However, the same zone might be rebooted. That is handled using the
* assumption that the zones framework picks a new zoneid each time a zone
* is (re)booted. We assert for that condition in netstack_zone_create().
* Thus the old netstack_t can take its time for things to time out.
*/
static netstack_t *netstack_head;
/*
* To support kstat_create_netstack() using kstat_zone_add we need
* to track both
* - all kstats that have been added for the shared stack
*/
struct shared_zone_list {
struct shared_zone_list *sz_next;
};
struct shared_kstat_list {
struct shared_kstat_list *sk_next;
};
static struct shared_zone_list *netstack_shared_zones;
static struct shared_kstat_list *netstack_shared_kstats;
static void apply_all_netstacks(int, applyfn_t *);
kmutex_t *);
void
netstack_init(void)
{
netstack_initialized = 1;
/*
* We want to be informed each time a zone is created or
* destroyed in the kernel, so we can maintain the
* stack instance information.
*/
}
/*
* Register a new module with the framework.
* This registers interest in changes to the set of netstacks.
* The createfn and destroyfn are required, but the shutdownfn can be
* NULL.
* Note that due to the current zsd implementation, when the create
* function is called the zone isn't fully present, thus functions
* like zone_find_by_* will fail, hence the create function can not
* use many zones kernel functions including zcmn_err().
*/
void
void (*module_shutdown)(netstackid_t, void *),
void (*module_destroy)(netstackid_t, void *))
{
netstack_t *ns;
/*
* Make instances created after this point in time run the create
* callback.
*/
/*
* Determine the set of stacks that exist before we drop the lock.
* Set NSS_CREATE_NEEDED for each of those.
* netstacks which have been deleted will have NSS_CREATE_COMPLETED
* set, but check NSF_CLOSING to be sure.
*/
}
}
/*
* At this point in time a new instance can be created or an instance
* can be destroyed, or some other module can register or unregister.
* Make sure we either run all the create functions for this moduleid
* or we wait for any other creators for this moduleid.
*/
}
void
{
netstack_t *ns;
/*
* Determine the set of stacks that exist before we drop the lock.
* Set NSS_SHUTDOWN_NEEDED and NSS_DESTROY_NEEDED for each of those.
* That ensures that when we return all the callbacks for existing
* instances have completed. And since we set NRF_DYING no new
* instances can use this module.
*/
}
}
}
/*
* Prevent any new netstack from calling the registered create
* function, while keeping the function pointers in place until the
* shutdown and destroy callbacks are complete.
*/
/*
* Clear the nms_flags so that we can handle this module
* being loaded again.
* Also remove the registered functions.
*/
}
}
}
/*
*/
static void *
{
netstack_t *ns;
netstack_t **nsp;
int i;
} else {
/* Look for the stack instance for the global */
}
/* Allocate even if it isn't needed; simplifies locking */
/* Look if there is a matching stack instance */
/*
* Should never find a pre-existing exclusive stack
*/
ns->netstack_numzones++;
netstack_t *, ns);
/* Record that we have a new shared stack zone */
return (ns);
}
}
/* Not found */
/*
* Mark this netstack as having a CREATE running so
* any netstack_register/netstack_unregister waits for
* the existing create callbacks to complete in moduleid order
*/
/*
* Determine the set of module create functions that need to be
* called before we drop the lock.
* Set NSS_CREATE_NEEDED for each of those.
* Skip any with NRF_DYING set, since those are in the process of
* going away, by checking for flags being exactly NRF_REGISTERED.
*/
for (i = 0; i < NS_MAX; i++) {
netstack_t *, ns, int, i);
}
}
/* Tell any waiting netstack_register/netstack_unregister to proceed */
return (ns);
}
/* ARGSUSED */
static void
{
int i;
/* Stack instance being used by other zone */
return;
}
/*
* Mark this netstack as having a SHUTDOWN running so
* any netstack_register/netstack_unregister waits for
* the existing create callbacks to complete in moduleid order
*/
/*
* Determine the set of stacks that exist before we drop the lock.
* Set NSS_SHUTDOWN_NEEDED for each of those.
*/
for (i = 0; i < NS_MAX; i++) {
netstack_t *, ns, int, i);
}
}
/*
* Call the shutdown function for all registered modules for this
* netstack.
*/
/* Tell any waiting netstack_register/netstack_unregister to proceed */
}
/*
* Common routine to release a zone.
* If this was the last zone using the stack instance then prepare to
* have the refcnt dropping to zero free the zone.
*/
/* ARGSUSED */
static void
{
ns->netstack_numzones--;
if (ns->netstack_numzones != 0) {
/* Stack instance being used by other zone */
/* Record that we a shared stack zone has gone away */
return;
}
/*
* Set CLOSING so that netstack_find_by will not find it.
*/
/* No other thread can call zone_destroy for this stack */
/*
* Decrease refcnt to account for the one in netstack_zone_init()
*/
}
/*
* Called when the reference count drops to zero.
* Call the destroy functions for each registered module.
*/
static void
{
int i;
/*
* Mark this netstack as having a DESTROY running so
* any netstack_register/netstack_unregister waits for
* the existing destroy callbacks to complete in reverse moduleid order
*/
/*
* If the shutdown callback wasn't called earlier (e.g., if this is
* a netstack shared between multiple zones), then we schedule it now.
*
* Determine the set of stacks that exist before we drop the lock.
* Set NSS_DESTROY_NEEDED for each of those. That
* ensures that when we return all the callbacks for existing
* instances have completed.
*/
for (i = 0; i < NS_MAX; i++) {
netstack_t *, ns, int, i);
}
netstack_t *, ns, int, i);
}
}
/*
* Call the shutdown and destroy functions for all registered modules
* for this netstack.
*
* Since there are some ordering dependencies between the modules we
* tear them down in the reverse order of what was used to create them.
*
* Since a netstack_t is never reused (when a zone is rebooted it gets
* a new zoneid == netstackid i.e. a new netstack_t is allocated) we
* leave nms_flags the way it is i.e. with NSS_DESTROY_COMPLETED set.
* That is different than in the netstack_unregister() case.
*/
/* Tell any waiting netstack_register/netstack_unregister to proceed */
}
/*
* Apply a function to all netstacks for a particular moduleid.
*
* If there is any zone activity (due to a zone being created, shutdown,
* or destroyed) we wait for that to complete before we proceed. This ensures
* that the moduleids are processed in order when a zone is created or
* destroyed.
*
* The applyfn has to drop netstack_g_lock if it does some work.
* In that case we don't follow netstack_next,
* even if it is possible to do so without any hazards. This is
* because we want the design to allow for the list of netstacks threaded
* by netstack_next to change in any arbitrary way during the time the
* lock was dropped.
*
* It is safe to restart the loop at netstack_head since the applyfn
* changes netstack_m_state as it processes things, so a subsequent
* pass through will have no effect in applyfn, hence the loop will terminate
* in at worst O(N^2).
*/
static void
{
netstack_t *ns;
ns = netstack_head;
/* Lock dropped - restart at head */
ns = netstack_head;
/* Lock dropped - restart at head */
ns = netstack_head;
} else {
}
}
}
/*
* Apply a function to all moduleids for a particular netstack.
*
* Since the netstack linkage doesn't matter in this case we can
* ignore whether the function drops the lock.
*/
static void
{
int i;
for (i = 0; i < NS_MAX; i++) {
/*
* We don't care whether the lock was dropped
* since we are not iterating over netstack_head.
*/
}
}
/* Like the above but in reverse moduleid order */
static void
{
int i;
for (i = NS_MAX-1; i >= 0; i--) {
/*
* We don't care whether the lock was dropped
* since we are not iterating over netstack_head.
*/
}
}
/*
* Call the create function for the ns and moduleid if CREATE_NEEDED
* is set.
* If some other thread gets here first and sets *_INPROGRESS, then
* we wait for that thread to complete so that we can ensure that
*
* When we call the create function, we temporarily drop the netstack_lock
* held by the caller, and return true to tell the caller it needs to
* re-evalute the state.
*/
static boolean_t
{
void *result;
netstack_t *, ns);
return (dropped);
} else {
return (dropped);
}
}
/*
* Call the shutdown function for the ns and moduleid if SHUTDOWN_NEEDED
* is set.
* If some other thread gets here first and sets *_INPROGRESS, then
* we wait for that thread to complete so that we can ensure that
*
* When we call the shutdown function, we temporarily drop the netstack_lock
* held by the caller, and return true to tell the caller it needs to
* re-evalute the state.
*/
static boolean_t
{
void * netstack_module;
void *, netstack_module);
netstack_t *, ns);
return (dropped);
} else {
return (dropped);
}
}
/*
* Call the destroy function for the ns and moduleid if DESTROY_NEEDED
* is set.
* If some other thread gets here first and sets *_INPROGRESS, then
* we wait for that thread to complete so that we can ensure that
*
* When we call the destroy function, we temporarily drop the netstack_lock
* held by the caller, and return true to tell the caller it needs to
* re-evalute the state.
*/
static boolean_t
{
void * netstack_module;
void *, netstack_module);
netstack_t *, ns);
return (dropped);
} else {
return (dropped);
}
}
/*
* If somebody is creating the netstack (due to a new zone being created)
* then we wait for them to complete. This ensures that any additional
* netstack_register() doesn't cause the create functions to run out of
* order.
* Note that we do not need such a global wait in the case of the shutdown
* and destroy callbacks, since in that case it is sufficient for both
* threads to set NEEDED and wait for INPROGRESS to ensure ordering.
* Returns true if lockp was temporarily dropped while waiting.
*/
static boolean_t
{
netstack_t *, ns);
}
/* First drop netstack_lock to preserve order */
}
}
return (dropped);
}
/*
* combination.
* Returns true if lockp was temporarily dropped while waiting.
*/
static boolean_t
{
}
/* First drop netstack_lock to preserve order */
}
}
return (dropped);
}
/*
* Get the stack instance used in caller's zone.
* Increases the reference count, caller must do a netstack_rele.
* It can't be called after zone_destroy() has started.
*/
netstack_get_current(void)
{
netstack_t *ns;
return (NULL);
return (ns);
}
/*
* Find a stack instance given the cred.
* This is used by the modules to potentially allow for a future when
* something other than the zoneid is used to determine the stack.
*/
{
/* Handle the case when cr_zone is NULL */
/* For performance ... */
return (netstack_get_current());
else
return (netstack_find_by_zoneid(zoneid));
}
/*
* Find a stack instance given the zoneid.
* Increases the reference count if found; caller must do a
* netstack_rele().
*
* If there is no exact match then assume the shared stack instance
* matches.
*
* Skip the unitialized ones.
*/
{
netstack_t *ns;
return (NULL);
else
return (ns);
}
/*
* Find a stack instance given the zoneid. Can only be called from
* the create callback. See the comments in zone_find_by_id_nolock why
* that limitation exists.
*
* Increases the reference count if found; caller must do a
* netstack_rele().
*
* If there is no exact match then assume the shared stack instance
* matches.
*
* Skip the unitialized ones.
*/
{
netstack_t *ns;
return (NULL);
else
/* zone_find_by_id_nolock does not have a hold on the zone */
return (ns);
}
/*
* Find a stack instance given the stackid with exact match?
* Increases the reference count if found; caller must do a
* netstack_rele().
*
* Skip the unitialized ones.
*/
{
netstack_t *ns;
return (ns);
}
}
return (NULL);
}
void
{
netstack_t **nsp;
int i;
ns->netstack_refcnt--;
/*
* As we drop the lock additional netstack_rele()s can come in
* and decrement the refcnt to zero and free the netstack_t.
* Store pointers in local variables and if we were not the last
* then don't reference the netstack_t after that.
*/
/*
* Time to call the destroy functions and free up
* the structure
*/
/* Make sure nothing increased the references */
/* Finally remove from list of netstacks */
break;
}
}
/* Make sure nothing increased the references */
for (i = 0; i < NS_MAX; i++) {
}
}
}
void
{
ns->netstack_refcnt++;
}
/*
* To support kstat_create_netstack() using kstat_zone_add we need
* to track both
* - all kstats that have been added for the shared stack
*/
kstat_t *
{
if (ks_netstackid == GLOBAL_NETSTACKID) {
return (ks);
} else {
}
}
void
{
if (ks_netstackid == GLOBAL_NETSTACKID) {
}
}
static void
{
struct shared_zone_list *sz;
struct shared_kstat_list *sk;
/* Insert in list */
/*
* Perform kstat_zone_add for each existing shared stack kstat.
* Note: Holds netstack_shared_lock lock across kstat_zone_add.
*/
}
}
static void
{
struct shared_kstat_list *sk;
/* Find in list */
break;
}
}
/* We must find it */
/*
* Perform kstat_zone_remove for each existing shared stack kstat.
* Note: Holds netstack_shared_lock lock across kstat_zone_remove.
*/
}
}
static void
{
struct shared_zone_list *sz;
struct shared_kstat_list *sk;
/* Insert in list */
/*
* Perform kstat_zone_add for each existing shared stack zone.
* Note: Holds netstack_shared_lock lock across kstat_zone_add.
*/
}
}
static void
{
struct shared_zone_list *sz;
/* Find in list */
break;
}
}
/* Must find it */
/*
* Perform kstat_zone_remove for each existing shared stack kstat.
* Note: Holds netstack_shared_lock lock across kstat_zone_remove.
*/
}
}
/*
* If a zoneid is part of the shared zone, return true
*/
static boolean_t
{
struct shared_zone_list *sz;
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Hide the fact that zoneids and netstackids are allocated from
* the same space in the current implementation.
* is no need for that. But this should only be done for ids that are
* valid.
*/
{
return (stackid);
}
{
return (GLOBAL_ZONEID);
else
return (zoneid);
}
/*
* Simplistic support for walking all the handles.
* Example usage:
* netstack_handle_t nh;
* netstack_t *ns;
*
* netstack_next_init(&nh);
* while ((ns = netstack_next(&nh)) != NULL) {
* do something;
* netstack_rele(ns);
* }
* netstack_next_fini(&nh);
*/
void
{
*handle = 0;
}
/* ARGSUSED */
void
{
}
{
netstack_t *ns;
int i, end;
/* Walk skipping *handle number of instances */
/* Look if there is a matching stack instance */
ns = netstack_head;
for (i = 0; i < end; i++) {
break;
}
/* skip those with that aren't really here */
break;
}
end++;
}
}
return (ns);
}