#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/vm.h>
#include <sys/proc.h>
#include <sys/tuneable.h>
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/sdt.h>
#include <sys/mutex.h>
#include <sys/bitmap.h>
#include <sys/atomic.h>
#include <sys/sunddi.h>
#include <sys/kobj.h>
#include <sys/disp.h>
#include <vm/seg_kmem.h>
#include <sys/zone.h>
#include <sys/netstack.h>
static zone_key_t netstack_zone_key;
static int netstack_initialized = 0;
static kmutex_t netstack_g_lock;
static struct netstack_registry ns_reg[NS_MAX];
static netstack_t *netstack_head;
struct shared_zone_list {
struct shared_zone_list *sz_next;
zoneid_t sz_zoneid;
};
struct shared_kstat_list {
struct shared_kstat_list *sk_next;
kstat_t *sk_kstat;
};
static kmutex_t netstack_shared_lock;
static struct shared_zone_list *netstack_shared_zones;
static struct shared_kstat_list *netstack_shared_kstats;
static void *netstack_zone_create(zoneid_t zoneid);
static void netstack_zone_shutdown(zoneid_t zoneid, void *arg);
static void netstack_zone_destroy(zoneid_t zoneid, void *arg);
static void netstack_shared_zone_add(zoneid_t zoneid);
static void netstack_shared_zone_remove(zoneid_t zoneid);
static void netstack_shared_kstat_add(kstat_t *ks);
static void netstack_shared_kstat_remove(kstat_t *ks);
typedef boolean_t applyfn_t(kmutex_t *, netstack_t *, int);
static void apply_all_netstacks(int, applyfn_t *);
static void apply_all_modules(netstack_t *, applyfn_t *);
static void apply_all_modules_reverse(netstack_t *, applyfn_t *);
static boolean_t netstack_apply_create(kmutex_t *, netstack_t *, int);
static boolean_t netstack_apply_shutdown(kmutex_t *, netstack_t *, int);
static boolean_t netstack_apply_destroy(kmutex_t *, netstack_t *, int);
static boolean_t wait_for_zone_creator(netstack_t *, kmutex_t *);
static boolean_t wait_for_nms_inprogress(netstack_t *, nm_state_t *,
kmutex_t *);
static void netstack_hold_locked(netstack_t *);
static ksema_t netstack_reap_limiter;
uint_t netstack_outstanding_reaps = 1024;
void
netstack_init(void)
{
mutex_init(&netstack_g_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&netstack_shared_lock, NULL, MUTEX_DEFAULT, NULL);
sema_init(&netstack_reap_limiter, netstack_outstanding_reaps, NULL,
SEMA_DRIVER, NULL);
netstack_initialized = 1;
zone_key_create(&netstack_zone_key, netstack_zone_create,
netstack_zone_shutdown, netstack_zone_destroy);
}
void
netstack_register(int moduleid,
void *(*module_create)(netstackid_t, netstack_t *),
void (*module_shutdown)(netstackid_t, void *),
void (*module_destroy)(netstackid_t, void *))
{
netstack_t *ns;
ASSERT(netstack_initialized);
ASSERT(moduleid >= 0 && moduleid < NS_MAX);
ASSERT(module_create != NULL);
mutex_enter(&netstack_g_lock);
ASSERT(ns_reg[moduleid].nr_create == NULL);
ASSERT(ns_reg[moduleid].nr_flags == 0);
ns_reg[moduleid].nr_create = module_create;
ns_reg[moduleid].nr_shutdown = module_shutdown;
ns_reg[moduleid].nr_destroy = module_destroy;
ns_reg[moduleid].nr_flags = NRF_REGISTERED;
for (ns = netstack_head; ns != NULL; ns = ns->netstack_next) {
nm_state_t *nms = &ns->netstack_m_state[moduleid];
mutex_enter(&ns->netstack_lock);
if (!(ns->netstack_flags & NSF_CLOSING) &&
(nms->nms_flags & NSS_CREATE_ALL) == 0) {
nms->nms_flags |= NSS_CREATE_NEEDED;
DTRACE_PROBE2(netstack__create__needed,
netstack_t *, ns, int, moduleid);
}
mutex_exit(&ns->netstack_lock);
}
mutex_exit(&netstack_g_lock);
apply_all_netstacks(moduleid, netstack_apply_create);
}
void
netstack_unregister(int moduleid)
{
netstack_t *ns;
ASSERT(moduleid >= 0 && moduleid < NS_MAX);
ASSERT(ns_reg[moduleid].nr_create != NULL);
ASSERT(ns_reg[moduleid].nr_flags & NRF_REGISTERED);
mutex_enter(&netstack_g_lock);
for (ns = netstack_head; ns != NULL; ns = ns->netstack_next) {
boolean_t created = B_FALSE;
nm_state_t *nms = &ns->netstack_m_state[moduleid];
mutex_enter(&ns->netstack_lock);
if (nms->nms_flags & NSS_CREATE_NEEDED)
nms->nms_flags &= ~NSS_CREATE_NEEDED;
if (nms->nms_flags & NSS_CREATE_INPROGRESS ||
nms->nms_flags & NSS_CREATE_COMPLETED)
created = B_TRUE;
if (ns_reg[moduleid].nr_shutdown != NULL && created &&
(nms->nms_flags & NSS_CREATE_COMPLETED) &&
(nms->nms_flags & NSS_SHUTDOWN_ALL) == 0) {
nms->nms_flags |= NSS_SHUTDOWN_NEEDED;
DTRACE_PROBE2(netstack__shutdown__needed,
netstack_t *, ns, int, moduleid);
}
if ((ns_reg[moduleid].nr_flags & NRF_REGISTERED) &&
ns_reg[moduleid].nr_destroy != NULL && created &&
(nms->nms_flags & NSS_DESTROY_ALL) == 0) {
nms->nms_flags |= NSS_DESTROY_NEEDED;
DTRACE_PROBE2(netstack__destroy__needed,
netstack_t *, ns, int, moduleid);
}
mutex_exit(&ns->netstack_lock);
}
ns_reg[moduleid].nr_flags |= NRF_DYING;
mutex_exit(&netstack_g_lock);
apply_all_netstacks(moduleid, netstack_apply_shutdown);
apply_all_netstacks(moduleid, netstack_apply_destroy);
mutex_enter(&netstack_g_lock);
ASSERT(ns_reg[moduleid].nr_flags & NRF_REGISTERED);
ASSERT(ns_reg[moduleid].nr_flags & NRF_DYING);
for (ns = netstack_head; ns != NULL; ns = ns->netstack_next) {
nm_state_t *nms = &ns->netstack_m_state[moduleid];
mutex_enter(&ns->netstack_lock);
if (nms->nms_flags & NSS_DESTROY_COMPLETED) {
nms->nms_flags = 0;
DTRACE_PROBE2(netstack__destroy__done,
netstack_t *, ns, int, moduleid);
}
mutex_exit(&ns->netstack_lock);
}
ns_reg[moduleid].nr_create = NULL;
ns_reg[moduleid].nr_shutdown = NULL;
ns_reg[moduleid].nr_destroy = NULL;
ns_reg[moduleid].nr_flags = 0;
mutex_exit(&netstack_g_lock);
}
static void *
netstack_zone_create(zoneid_t zoneid)
{
netstackid_t stackid;
netstack_t *ns;
netstack_t **nsp;
zone_t *zone;
int i;
ASSERT(netstack_initialized);
zone = zone_find_by_id_nolock(zoneid);
ASSERT(zone != NULL);
if (zone->zone_flags & ZF_NET_EXCL) {
stackid = zoneid;
} else {
stackid = GLOBAL_NETSTACKID;
}
ns = (netstack_t *)kmem_zalloc(sizeof (netstack_t), KM_SLEEP);
mutex_enter(&netstack_g_lock);
for (nsp = &netstack_head; *nsp != NULL;
nsp = &((*nsp)->netstack_next)) {
if ((*nsp)->netstack_stackid == stackid) {
VERIFY(stackid == GLOBAL_NETSTACKID);
kmem_free(ns, sizeof (netstack_t));
ns = *nsp;
mutex_enter(&ns->netstack_lock);
ns->netstack_numzones++;
mutex_exit(&ns->netstack_lock);
mutex_exit(&netstack_g_lock);
DTRACE_PROBE1(netstack__inc__numzones,
netstack_t *, ns);
netstack_shared_zone_add(zoneid);
zone->zone_netstack = ns;
return (ns);
}
}
mutex_init(&ns->netstack_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&ns->netstack_cv, NULL, CV_DEFAULT, NULL);
ns->netstack_stackid = zoneid;
ns->netstack_numzones = 1;
ns->netstack_refcnt = 1;
ns->netstack_flags = NSF_UNINIT;
*nsp = ns;
zone->zone_netstack = ns;
mutex_enter(&ns->netstack_lock);
ns->netstack_flags |= NSF_ZONE_CREATE;
for (i = 0; i < NS_MAX; i++) {
nm_state_t *nms = &ns->netstack_m_state[i];
cv_init(&nms->nms_cv, NULL, CV_DEFAULT, NULL);
if ((ns_reg[i].nr_flags == NRF_REGISTERED) &&
(nms->nms_flags & NSS_CREATE_ALL) == 0) {
nms->nms_flags |= NSS_CREATE_NEEDED;
DTRACE_PROBE2(netstack__create__needed,
netstack_t *, ns, int, i);
}
}
mutex_exit(&ns->netstack_lock);
mutex_exit(&netstack_g_lock);
apply_all_modules(ns, netstack_apply_create);
mutex_enter(&ns->netstack_lock);
ns->netstack_flags &= ~NSF_UNINIT;
ASSERT(ns->netstack_flags & NSF_ZONE_CREATE);
ns->netstack_flags &= ~NSF_ZONE_CREATE;
cv_broadcast(&ns->netstack_cv);
mutex_exit(&ns->netstack_lock);
return (ns);
}
static void
netstack_zone_shutdown(zoneid_t zoneid, void *arg)
{
netstack_t *ns = (netstack_t *)arg;
int i;
ASSERT(arg != NULL);
mutex_enter(&ns->netstack_lock);
ASSERT(ns->netstack_numzones > 0);
if (ns->netstack_numzones != 1) {
mutex_exit(&ns->netstack_lock);
ASSERT(ns->netstack_stackid == GLOBAL_NETSTACKID);
return;
}
mutex_exit(&ns->netstack_lock);
mutex_enter(&netstack_g_lock);
mutex_enter(&ns->netstack_lock);
ASSERT(!(ns->netstack_flags & NSF_ZONE_INPROGRESS));
ns->netstack_flags |= NSF_ZONE_SHUTDOWN;
for (i = 0; i < NS_MAX; i++) {
nm_state_t *nms = &ns->netstack_m_state[i];
if ((ns_reg[i].nr_flags & NRF_REGISTERED) &&
ns_reg[i].nr_shutdown != NULL &&
(nms->nms_flags & NSS_CREATE_COMPLETED) &&
(nms->nms_flags & NSS_SHUTDOWN_ALL) == 0) {
nms->nms_flags |= NSS_SHUTDOWN_NEEDED;
DTRACE_PROBE2(netstack__shutdown__needed,
netstack_t *, ns, int, i);
}
}
mutex_exit(&ns->netstack_lock);
mutex_exit(&netstack_g_lock);
apply_all_modules_reverse(ns, netstack_apply_shutdown);
mutex_enter(&ns->netstack_lock);
ASSERT(ns->netstack_flags & NSF_ZONE_SHUTDOWN);
ns->netstack_flags &= ~NSF_ZONE_SHUTDOWN;
cv_broadcast(&ns->netstack_cv);
mutex_exit(&ns->netstack_lock);
}
static void
netstack_zone_destroy(zoneid_t zoneid, void *arg)
{
netstack_t *ns = (netstack_t *)arg;
ASSERT(arg != NULL);
mutex_enter(&ns->netstack_lock);
ASSERT(ns->netstack_numzones > 0);
ns->netstack_numzones--;
if (ns->netstack_numzones != 0) {
mutex_exit(&ns->netstack_lock);
ASSERT(ns->netstack_stackid == GLOBAL_NETSTACKID);
netstack_shared_zone_remove(zoneid);
return;
}
ns->netstack_flags |= NSF_CLOSING;
mutex_exit(&ns->netstack_lock);
DTRACE_PROBE1(netstack__dec__numzones, netstack_t *, ns);
netstack_rele(ns);
}
static void
netstack_stack_inactive(netstack_t *ns)
{
int i;
mutex_enter(&netstack_g_lock);
mutex_enter(&ns->netstack_lock);
ASSERT(!(ns->netstack_flags & NSF_ZONE_INPROGRESS));
ns->netstack_flags |= NSF_ZONE_DESTROY;
for (i = 0; i < NS_MAX; i++) {
nm_state_t *nms = &ns->netstack_m_state[i];
if ((ns_reg[i].nr_flags & NRF_REGISTERED) &&
ns_reg[i].nr_shutdown != NULL &&
(nms->nms_flags & NSS_CREATE_COMPLETED) &&
(nms->nms_flags & NSS_SHUTDOWN_ALL) == 0) {
nms->nms_flags |= NSS_SHUTDOWN_NEEDED;
DTRACE_PROBE2(netstack__shutdown__needed,
netstack_t *, ns, int, i);
}
if ((ns_reg[i].nr_flags & NRF_REGISTERED) &&
ns_reg[i].nr_destroy != NULL &&
(nms->nms_flags & NSS_CREATE_COMPLETED) &&
(nms->nms_flags & NSS_DESTROY_ALL) == 0) {
nms->nms_flags |= NSS_DESTROY_NEEDED;
DTRACE_PROBE2(netstack__destroy__needed,
netstack_t *, ns, int, i);
}
}
mutex_exit(&ns->netstack_lock);
mutex_exit(&netstack_g_lock);
apply_all_modules_reverse(ns, netstack_apply_shutdown);
apply_all_modules_reverse(ns, netstack_apply_destroy);
mutex_enter(&ns->netstack_lock);
ASSERT(ns->netstack_flags & NSF_ZONE_DESTROY);
ns->netstack_flags &= ~NSF_ZONE_DESTROY;
cv_broadcast(&ns->netstack_cv);
mutex_exit(&ns->netstack_lock);
}
static void
apply_all_netstacks(int moduleid, applyfn_t *applyfn)
{
netstack_t *ns;
mutex_enter(&netstack_g_lock);
ns = netstack_head;
while (ns != NULL) {
if (wait_for_zone_creator(ns, &netstack_g_lock)) {
ns = netstack_head;
} else if ((applyfn)(&netstack_g_lock, ns, moduleid)) {
ns = netstack_head;
} else {
ns = ns->netstack_next;
}
}
mutex_exit(&netstack_g_lock);
}
static void
apply_all_modules(netstack_t *ns, applyfn_t *applyfn)
{
int i;
mutex_enter(&netstack_g_lock);
for (i = 0; i < NS_MAX; i++) {
(void) (applyfn)(&netstack_g_lock, ns, i);
}
mutex_exit(&netstack_g_lock);
}
static void
apply_all_modules_reverse(netstack_t *ns, applyfn_t *applyfn)
{
int i;
mutex_enter(&netstack_g_lock);
for (i = NS_MAX-1; i >= 0; i--) {
(void) (applyfn)(&netstack_g_lock, ns, i);
}
mutex_exit(&netstack_g_lock);
}
static boolean_t
netstack_apply_create(kmutex_t *lockp, netstack_t *ns, int moduleid)
{
void *result;
netstackid_t stackid;
nm_state_t *nms = &ns->netstack_m_state[moduleid];
boolean_t dropped = B_FALSE;
ASSERT(MUTEX_HELD(lockp));
mutex_enter(&ns->netstack_lock);
if (wait_for_nms_inprogress(ns, nms, lockp))
dropped = B_TRUE;
if (nms->nms_flags & NSS_CREATE_NEEDED) {
nms->nms_flags &= ~NSS_CREATE_NEEDED;
nms->nms_flags |= NSS_CREATE_INPROGRESS;
DTRACE_PROBE2(netstack__create__inprogress,
netstack_t *, ns, int, moduleid);
mutex_exit(&ns->netstack_lock);
mutex_exit(lockp);
dropped = B_TRUE;
ASSERT(ns_reg[moduleid].nr_create != NULL);
stackid = ns->netstack_stackid;
DTRACE_PROBE2(netstack__create__start,
netstackid_t, stackid,
netstack_t *, ns);
result = (ns_reg[moduleid].nr_create)(stackid, ns);
DTRACE_PROBE2(netstack__create__end,
void *, result, netstack_t *, ns);
ASSERT(result != NULL);
mutex_enter(lockp);
mutex_enter(&ns->netstack_lock);
ns->netstack_modules[moduleid] = result;
nms->nms_flags &= ~NSS_CREATE_INPROGRESS;
nms->nms_flags |= NSS_CREATE_COMPLETED;
cv_broadcast(&nms->nms_cv);
DTRACE_PROBE2(netstack__create__completed,
netstack_t *, ns, int, moduleid);
mutex_exit(&ns->netstack_lock);
return (dropped);
} else {
mutex_exit(&ns->netstack_lock);
return (dropped);
}
}
static boolean_t
netstack_apply_shutdown(kmutex_t *lockp, netstack_t *ns, int moduleid)
{
netstackid_t stackid;
void * netstack_module;
nm_state_t *nms = &ns->netstack_m_state[moduleid];
boolean_t dropped = B_FALSE;
ASSERT(MUTEX_HELD(lockp));
mutex_enter(&ns->netstack_lock);
if (wait_for_nms_inprogress(ns, nms, lockp))
dropped = B_TRUE;
if (nms->nms_flags & NSS_SHUTDOWN_NEEDED) {
nms->nms_flags &= ~NSS_SHUTDOWN_NEEDED;
nms->nms_flags |= NSS_SHUTDOWN_INPROGRESS;
DTRACE_PROBE2(netstack__shutdown__inprogress,
netstack_t *, ns, int, moduleid);
mutex_exit(&ns->netstack_lock);
mutex_exit(lockp);
dropped = B_TRUE;
ASSERT(ns_reg[moduleid].nr_shutdown != NULL);
stackid = ns->netstack_stackid;
netstack_module = ns->netstack_modules[moduleid];
DTRACE_PROBE2(netstack__shutdown__start,
netstackid_t, stackid,
void *, netstack_module);
(ns_reg[moduleid].nr_shutdown)(stackid, netstack_module);
DTRACE_PROBE1(netstack__shutdown__end,
netstack_t *, ns);
mutex_enter(lockp);
mutex_enter(&ns->netstack_lock);
nms->nms_flags &= ~NSS_SHUTDOWN_INPROGRESS;
nms->nms_flags |= NSS_SHUTDOWN_COMPLETED;
cv_broadcast(&nms->nms_cv);
DTRACE_PROBE2(netstack__shutdown__completed,
netstack_t *, ns, int, moduleid);
mutex_exit(&ns->netstack_lock);
return (dropped);
} else {
mutex_exit(&ns->netstack_lock);
return (dropped);
}
}
static boolean_t
netstack_apply_destroy(kmutex_t *lockp, netstack_t *ns, int moduleid)
{
netstackid_t stackid;
void * netstack_module;
nm_state_t *nms = &ns->netstack_m_state[moduleid];
boolean_t dropped = B_FALSE;
ASSERT(MUTEX_HELD(lockp));
mutex_enter(&ns->netstack_lock);
if (wait_for_nms_inprogress(ns, nms, lockp))
dropped = B_TRUE;
if (nms->nms_flags & NSS_DESTROY_NEEDED) {
nms->nms_flags &= ~NSS_DESTROY_NEEDED;
nms->nms_flags |= NSS_DESTROY_INPROGRESS;
DTRACE_PROBE2(netstack__destroy__inprogress,
netstack_t *, ns, int, moduleid);
mutex_exit(&ns->netstack_lock);
mutex_exit(lockp);
dropped = B_TRUE;
ASSERT(ns_reg[moduleid].nr_destroy != NULL);
stackid = ns->netstack_stackid;
netstack_module = ns->netstack_modules[moduleid];
DTRACE_PROBE2(netstack__destroy__start,
netstackid_t, stackid,
void *, netstack_module);
(ns_reg[moduleid].nr_destroy)(stackid, netstack_module);
DTRACE_PROBE1(netstack__destroy__end,
netstack_t *, ns);
mutex_enter(lockp);
mutex_enter(&ns->netstack_lock);
ns->netstack_modules[moduleid] = NULL;
nms->nms_flags &= ~NSS_DESTROY_INPROGRESS;
nms->nms_flags |= NSS_DESTROY_COMPLETED;
cv_broadcast(&nms->nms_cv);
DTRACE_PROBE2(netstack__destroy__completed,
netstack_t *, ns, int, moduleid);
mutex_exit(&ns->netstack_lock);
return (dropped);
} else {
mutex_exit(&ns->netstack_lock);
return (dropped);
}
}
static boolean_t
wait_for_zone_creator(netstack_t *ns, kmutex_t *lockp)
{
boolean_t dropped = B_FALSE;
mutex_enter(&ns->netstack_lock);
while (ns->netstack_flags & NSF_ZONE_CREATE) {
DTRACE_PROBE1(netstack__wait__zone__inprogress,
netstack_t *, ns);
if (lockp != NULL) {
dropped = B_TRUE;
mutex_exit(lockp);
}
cv_wait(&ns->netstack_cv, &ns->netstack_lock);
if (lockp != NULL) {
mutex_exit(&ns->netstack_lock);
mutex_enter(lockp);
mutex_enter(&ns->netstack_lock);
}
}
mutex_exit(&ns->netstack_lock);
return (dropped);
}
static boolean_t
wait_for_nms_inprogress(netstack_t *ns, nm_state_t *nms, kmutex_t *lockp)
{
boolean_t dropped = B_FALSE;
while (nms->nms_flags & NSS_ALL_INPROGRESS) {
DTRACE_PROBE2(netstack__wait__nms__inprogress,
netstack_t *, ns, nm_state_t *, nms);
if (lockp != NULL) {
dropped = B_TRUE;
mutex_exit(lockp);
}
cv_wait(&nms->nms_cv, &ns->netstack_lock);
if (lockp != NULL) {
mutex_exit(&ns->netstack_lock);
mutex_enter(lockp);
mutex_enter(&ns->netstack_lock);
}
}
return (dropped);
}
netstack_t *
netstack_get_current(void)
{
netstack_t *ns;
ns = curproc->p_zone->zone_netstack;
ASSERT(ns != NULL);
return (netstack_hold_if_active(ns));
}
netstack_t *
netstack_find_by_cred(const cred_t *cr)
{
zoneid_t zoneid = crgetzoneid(cr);
if (zoneid == (zoneid_t)-1)
zoneid = GLOBAL_ZONEID;
if (curproc->p_zone->zone_id == zoneid)
return (netstack_get_current());
else
return (netstack_find_by_zoneid(zoneid));
}
netstack_t *
netstack_find_by_zoneid(zoneid_t zoneid)
{
netstack_t *ns;
zone_t *zone;
zone = zone_find_by_id(zoneid);
if (zone == NULL)
return (NULL);
ASSERT(zone->zone_netstack != NULL);
ns = netstack_hold_if_active(zone->zone_netstack);
zone_rele(zone);
return (ns);
}
netstack_t *
netstack_find_by_zoneid_nolock(zoneid_t zoneid)
{
zone_t *zone;
zone = zone_find_by_id_nolock(zoneid);
if (zone == NULL)
return (NULL);
ASSERT(zone->zone_netstack != NULL);
return (netstack_hold_if_active(zone->zone_netstack));
}
netstack_t *
netstack_find_by_stackid(netstackid_t stackid)
{
netstack_t *ns;
mutex_enter(&netstack_g_lock);
for (ns = netstack_head; ns != NULL; ns = ns->netstack_next) {
mutex_enter(&ns->netstack_lock);
if (ns->netstack_stackid == stackid &&
!(ns->netstack_flags & (NSF_UNINIT|NSF_CLOSING))) {
netstack_hold_locked(ns);
mutex_exit(&ns->netstack_lock);
mutex_exit(&netstack_g_lock);
return (ns);
}
mutex_exit(&ns->netstack_lock);
}
mutex_exit(&netstack_g_lock);
return (NULL);
}
boolean_t
netstack_inuse_by_stackid(netstackid_t stackid)
{
netstack_t *ns;
boolean_t rval = B_FALSE;
mutex_enter(&netstack_g_lock);
for (ns = netstack_head; ns != NULL; ns = ns->netstack_next) {
if (ns->netstack_stackid == stackid) {
rval = B_TRUE;
break;
}
}
mutex_exit(&netstack_g_lock);
return (rval);
}
static void
netstack_reap(void *arg)
{
netstack_t **nsp, *ns = (netstack_t *)arg;
boolean_t found;
int i;
netstack_stack_inactive(ns);
ASSERT(ns->netstack_refcnt == 0);
ASSERT(ns->netstack_numzones == 0);
mutex_enter(&netstack_g_lock);
found = B_FALSE;
for (nsp = &netstack_head; *nsp != NULL;
nsp = &(*nsp)->netstack_next) {
if (*nsp == ns) {
*nsp = ns->netstack_next;
ns->netstack_next = NULL;
found = B_TRUE;
break;
}
}
ASSERT(found);
mutex_exit(&netstack_g_lock);
ASSERT(ns->netstack_refcnt == 0);
ASSERT(ns->netstack_numzones == 0);
ASSERT(ns->netstack_flags & NSF_CLOSING);
for (i = 0; i < NS_MAX; i++) {
nm_state_t *nms = &ns->netstack_m_state[i];
cv_destroy(&nms->nms_cv);
}
mutex_destroy(&ns->netstack_lock);
cv_destroy(&ns->netstack_cv);
kmem_free(ns, sizeof (*ns));
sema_v(&netstack_reap_limiter);
}
void
netstack_rele(netstack_t *ns)
{
int refcnt, numzones;
mutex_enter(&ns->netstack_lock);
ASSERT(ns->netstack_refcnt > 0);
ns->netstack_refcnt--;
refcnt = ns->netstack_refcnt;
numzones = ns->netstack_numzones;
DTRACE_PROBE1(netstack__dec__ref, netstack_t *, ns);
mutex_exit(&ns->netstack_lock);
if (refcnt == 0 && numzones == 0) {
if (sema_tryp(&netstack_reap_limiter) == 0) {
hrtime_t measurement = gethrtime();
sema_p(&netstack_reap_limiter);
DTRACE_PROBE1(netstack__reap__rate__limited,
hrtime_t, gethrtime() - measurement);
}
(void) taskq_dispatch(system_taskq, netstack_reap, ns,
TQ_SLEEP);
}
}
static void
netstack_hold_locked(netstack_t *ns)
{
ASSERT(MUTEX_HELD(&ns->netstack_lock));
ns->netstack_refcnt++;
ASSERT(ns->netstack_refcnt > 0);
DTRACE_PROBE1(netstack__inc__ref, netstack_t *, ns);
}
netstack_t *
netstack_hold_if_active(netstack_t *ns)
{
netstack_t *retval;
mutex_enter(&ns->netstack_lock);
if (ns->netstack_flags & (NSF_UNINIT | NSF_CLOSING)) {
retval = NULL;
} else {
netstack_hold_locked(ns);
retval = ns;
}
mutex_exit(&ns->netstack_lock);
return (retval);
}
void
netstack_hold(netstack_t *ns)
{
mutex_enter(&ns->netstack_lock);
netstack_hold_locked(ns);
mutex_exit(&ns->netstack_lock);
}
kstat_t *
kstat_create_netstack(char *ks_module, int ks_instance, char *ks_name,
char *ks_class, uchar_t ks_type, uint_t ks_ndata, uchar_t ks_flags,
netstackid_t ks_netstackid)
{
kstat_t *ks;
if (ks_netstackid == GLOBAL_NETSTACKID) {
ks = kstat_create_zone(ks_module, ks_instance, ks_name,
ks_class, ks_type, ks_ndata, ks_flags, GLOBAL_ZONEID);
if (ks != NULL)
netstack_shared_kstat_add(ks);
return (ks);
} else {
zoneid_t zoneid = ks_netstackid;
return (kstat_create_zone(ks_module, ks_instance, ks_name,
ks_class, ks_type, ks_ndata, ks_flags, zoneid));
}
}
void
kstat_delete_netstack(kstat_t *ks, netstackid_t ks_netstackid)
{
if (ks_netstackid == GLOBAL_NETSTACKID) {
netstack_shared_kstat_remove(ks);
}
kstat_delete(ks);
}
static void
netstack_shared_zone_add(zoneid_t zoneid)
{
struct shared_zone_list *sz;
struct shared_kstat_list *sk;
sz = (struct shared_zone_list *)kmem_zalloc(sizeof (*sz), KM_SLEEP);
sz->sz_zoneid = zoneid;
mutex_enter(&netstack_shared_lock);
sz->sz_next = netstack_shared_zones;
netstack_shared_zones = sz;
for (sk = netstack_shared_kstats; sk != NULL; sk = sk->sk_next) {
kstat_zone_add(sk->sk_kstat, zoneid);
}
mutex_exit(&netstack_shared_lock);
}
static void
netstack_shared_zone_remove(zoneid_t zoneid)
{
struct shared_zone_list **szp, *sz;
struct shared_kstat_list *sk;
mutex_enter(&netstack_shared_lock);
sz = NULL;
for (szp = &netstack_shared_zones; *szp != NULL;
szp = &((*szp)->sz_next)) {
if ((*szp)->sz_zoneid == zoneid) {
sz = *szp;
break;
}
}
ASSERT(sz != NULL);
*szp = sz->sz_next;
sz->sz_next = NULL;
for (sk = netstack_shared_kstats; sk != NULL; sk = sk->sk_next) {
kstat_zone_remove(sk->sk_kstat, zoneid);
}
mutex_exit(&netstack_shared_lock);
kmem_free(sz, sizeof (*sz));
}
static void
netstack_shared_kstat_add(kstat_t *ks)
{
struct shared_zone_list *sz;
struct shared_kstat_list *sk;
sk = (struct shared_kstat_list *)kmem_zalloc(sizeof (*sk), KM_SLEEP);
sk->sk_kstat = ks;
mutex_enter(&netstack_shared_lock);
sk->sk_next = netstack_shared_kstats;
netstack_shared_kstats = sk;
for (sz = netstack_shared_zones; sz != NULL; sz = sz->sz_next) {
kstat_zone_add(ks, sz->sz_zoneid);
}
mutex_exit(&netstack_shared_lock);
}
static void
netstack_shared_kstat_remove(kstat_t *ks)
{
struct shared_zone_list *sz;
struct shared_kstat_list **skp, *sk;
mutex_enter(&netstack_shared_lock);
sk = NULL;
for (skp = &netstack_shared_kstats; *skp != NULL;
skp = &((*skp)->sk_next)) {
if ((*skp)->sk_kstat == ks) {
sk = *skp;
break;
}
}
ASSERT(sk != NULL);
*skp = sk->sk_next;
sk->sk_next = NULL;
for (sz = netstack_shared_zones; sz != NULL; sz = sz->sz_next) {
kstat_zone_remove(ks, sz->sz_zoneid);
}
mutex_exit(&netstack_shared_lock);
kmem_free(sk, sizeof (*sk));
}
static boolean_t
netstack_find_shared_zoneid(zoneid_t zoneid)
{
struct shared_zone_list *sz;
mutex_enter(&netstack_shared_lock);
for (sz = netstack_shared_zones; sz != NULL; sz = sz->sz_next) {
if (sz->sz_zoneid == zoneid) {
mutex_exit(&netstack_shared_lock);
return (B_TRUE);
}
}
mutex_exit(&netstack_shared_lock);
return (B_FALSE);
}
zoneid_t
netstackid_to_zoneid(netstackid_t stackid)
{
return (stackid);
}
netstackid_t
zoneid_to_netstackid(zoneid_t zoneid)
{
if (netstack_find_shared_zoneid(zoneid))
return (GLOBAL_ZONEID);
else
return (zoneid);
}
zoneid_t
netstack_get_zoneid(netstack_t *ns)
{
return (netstackid_to_zoneid(ns->netstack_stackid));
}
void
netstack_next_init(netstack_handle_t *handle)
{
*handle = 0;
}
void
netstack_next_fini(netstack_handle_t *handle)
{
}
netstack_t *
netstack_next(netstack_handle_t *handle)
{
netstack_t *ns;
int i, end;
end = *handle;
mutex_enter(&netstack_g_lock);
ns = netstack_head;
for (i = 0; i < end; i++) {
if (ns == NULL)
break;
ns = ns->netstack_next;
}
while (ns != NULL) {
mutex_enter(&ns->netstack_lock);
if ((ns->netstack_flags & (NSF_UNINIT|NSF_CLOSING)) == 0) {
*handle = end + 1;
netstack_hold_locked(ns);
mutex_exit(&ns->netstack_lock);
break;
}
mutex_exit(&ns->netstack_lock);
end++;
ns = ns->netstack_next;
}
mutex_exit(&netstack_g_lock);
return (ns);
}