diff options
Diffstat (limited to 'usr/src/uts/common/io/neti_stack.c')
-rw-r--r-- | usr/src/uts/common/io/neti_stack.c | 840 |
1 files changed, 840 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/neti_stack.c b/usr/src/uts/common/io/neti_stack.c new file mode 100644 index 0000000000..1428638b7b --- /dev/null +++ b/usr/src/uts/common/io/neti_stack.c @@ -0,0 +1,840 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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/param.h> +#include <sys/atomic.h> +#include <sys/kmem.h> +#include <sys/rwlock.h> +#include <sys/errno.h> +#include <sys/queue.h> +#include <sys/sunddi.h> +#include <inet/common.h> +#include <inet/led.h> +#include <inet/ip.h> +#include <sys/neti.h> +#include <sys/zone.h> +#include <sys/sdt.h> + + +typedef boolean_t napplyfn_t(kmutex_t *, neti_stack_t *, void *); + +static void *neti_stack_init(netstackid_t stackid, netstack_t *ns); +static void neti_stack_fini(netstackid_t stackid, void *arg); +static net_instance_int_t *net_instance_int_create(net_instance_t *nin, + net_instance_int_t *parent); +static void neti_stack_shutdown(netstackid_t stackid, void *arg); +static void net_instance_int_free(net_instance_int_t *nini); + +static boolean_t neti_stack_apply_create(kmutex_t *, neti_stack_t *, void *); +static boolean_t neti_stack_apply_destroy(kmutex_t *, neti_stack_t *, void *); +static boolean_t neti_stack_apply_shutdown(kmutex_t *, neti_stack_t *, void *); +static void neti_apply_all_instances(neti_stack_t *, napplyfn_t *); +static void neti_apply_all_stacks(void *, napplyfn_t *); +static boolean_t wait_for_nini_inprogress(neti_stack_t *, kmutex_t *, + net_instance_int_t *, uint32_t); + +static nini_head_t neti_instance_list; +static neti_stack_head_t neti_stack_list; +static kmutex_t neti_stack_lock; + +void +neti_init() +{ + mutex_init(&neti_stack_lock, NULL, MUTEX_DRIVER, NULL); + + LIST_INIT(&neti_instance_list); + LIST_INIT(&neti_stack_list); + /* + * We want to be informed each time a netstack is created or + * destroyed in the kernel. + */ + netstack_register(NS_NETI, neti_stack_init, neti_stack_shutdown, + neti_stack_fini); +} + +void +neti_fini() +{ + ASSERT(LIST_EMPTY(&neti_instance_list)); + ASSERT(LIST_EMPTY(&neti_stack_list)); + + netstack_unregister(NS_NETI); + + mutex_destroy(&neti_stack_lock); +} + +/* + * Initialize the neti stack instance. Because this is called out of the + * netstack framework, it is not possible for it to be called twice with + * the same values for (stackid,ns). The same also applies to the other + * two functions used with netstack_register: neti_stack_shutdown and + * neti_stack_fini. + */ +static void * +neti_stack_init(netstackid_t stackid, netstack_t *ns) +{ + net_instance_int_t *dup; + net_instance_int_t *n; + neti_stack_t *nts; + + nts = kmem_zalloc(sizeof (*nts), KM_SLEEP); + LIST_INIT(&nts->nts_instances); + nts->nts_id = (netid_t)stackid; + nts->nts_stackid = stackid; + nts->nts_netstack = ns; + nts->nts_zoneid = netstackid_to_zoneid(stackid); + nts->nts_flags = NSF_ZONE_CREATE; + cv_init(&nts->nts_cv, NULL, CV_DRIVER, NULL); + mutex_init(&nts->nts_lock, NULL, MUTEX_DRIVER, NULL); + + mutex_enter(&neti_stack_lock); + LIST_INSERT_HEAD(&neti_stack_list, nts, nts_next); + + LIST_FOREACH(n, &neti_instance_list, nini_next) { + /* + * This function returns with the NSS_CREATE_NEEDED flag + * set in "dup", so it is adequately prepared for the + * upcoming apply. + */ + dup = net_instance_int_create(n->nini_instance, n); + + mutex_enter(&nts->nts_lock); + LIST_INSERT_HEAD(&nts->nts_instances, dup, nini_next); + mutex_exit(&nts->nts_lock); + } + + neti_apply_all_instances(nts, neti_stack_apply_create); + + mutex_enter(&nts->nts_lock); + nts->nts_flags &= ~NSF_ZONE_CREATE; + cv_signal(&nts->nts_cv); + mutex_exit(&nts->nts_lock); + + mutex_exit(&neti_stack_lock); + + return (nts); +} + +/* + * Run the shutdown for all of the hooks. + */ +/*ARGSUSED*/ +static void +neti_stack_shutdown(netstackid_t stackid, void *arg) +{ + neti_stack_t *nts = arg; + net_instance_int_t *n; + struct net_data *nd; + + ASSERT(nts != NULL); + + mutex_enter(&neti_stack_lock); + mutex_enter(&nts->nts_lock); + /* + * Walk through all of the protocol stacks and mark them as shutting + * down. + */ + LIST_FOREACH(nd, &nts->nts_netd_head, netd_list) { + nd->netd_condemned = 1; + } + + /* + * Now proceed to see which callbacks are waiting to hear about the + * impending shutdown... + */ + LIST_FOREACH(n, &nts->nts_instances, nini_next) { + if (n->nini_instance->nin_shutdown == NULL) { + /* + * If there is no shutdown function registered, + * fake that we have completed it. + */ + n->nini_flags |= NSS_SHUTDOWN_COMPLETED; + continue; + } + + /* + * We need to ensure that we don't try and shutdown something + * that is already in the process of being shutdown or + * destroyed. If it is still being created, that's ok, the + * shtudown flag is added to the mix of things to do. + */ + if ((n->nini_flags & (NSS_DESTROY_ALL|NSS_SHUTDOWN_ALL)) == 0) + n->nini_flags |= NSS_SHUTDOWN_NEEDED; + } + nts->nts_flags |= NSF_ZONE_SHUTDOWN; + mutex_exit(&nts->nts_lock); + + neti_apply_all_instances(nts, neti_stack_apply_shutdown); + + mutex_enter(&nts->nts_lock); + + nts->nts_netstack = NULL; + mutex_exit(&nts->nts_lock); + + mutex_exit(&neti_stack_lock); + ASSERT(nts != NULL); +} + +/* + * Free the neti stack instance. + * This function relies on the netstack framework only calling the _destroy + * callback once for each stackid. The netstack framework also provides us + * with assurance that nobody else will be doing any work (_create, _shutdown) + * on it, so there is no need to set and use flags to guard against + * simultaneous execution (ie. no need to set NSF_CLOSING.) + * What is required, however, is to make sure that we don't corrupt the + * list of neti_stack_t's for other code that walks it. + */ +/*ARGSUSED*/ +static void +neti_stack_fini(netstackid_t stackid, void *arg) +{ + neti_stack_t *nts = arg; + net_instance_int_t *n; + struct net_data *nd; + + mutex_enter(&neti_stack_lock); + LIST_REMOVE(nts, nts_next); + + mutex_enter(&nts->nts_lock); + nts->nts_flags |= NSF_ZONE_DESTROY; + /* + * Walk through all of the protocol stacks and mark them as being + * destroyed. + */ + LIST_FOREACH(nd, &nts->nts_netd_head, netd_list) { + nd->netd_condemned = 2; + } + + LIST_FOREACH(n, &nts->nts_instances, nini_next) { + ASSERT((n->nini_flags & NSS_SHUTDOWN_ALL) != 0); + if (n->nini_instance->nin_shutdown == NULL) + continue; + if ((n->nini_flags & NSS_DESTROY_ALL) == 0) + n->nini_flags |= NSS_DESTROY_NEEDED; + } + mutex_exit(&nts->nts_lock); + + neti_apply_all_instances(nts, neti_stack_apply_destroy); + mutex_exit(&neti_stack_lock); + + while (!LIST_EMPTY(&nts->nts_instances)) { + n = LIST_FIRST(&nts->nts_instances); + LIST_REMOVE(n, nini_next); + + net_instance_int_free(n); + } + + ASSERT(LIST_EMPTY(&nts->nts_netd_head)); + + mutex_destroy(&nts->nts_lock); + cv_destroy(&nts->nts_cv); + + kmem_free(nts, sizeof (*nts)); +} + +static net_instance_int_t * +net_instance_int_create(net_instance_t *nin, net_instance_int_t *parent) +{ + net_instance_int_t *nini; + + nini = kmem_zalloc(sizeof (net_instance_int_t), KM_SLEEP); + nini->nini_instance = nin; + nini->nini_parent = parent; + if (parent != NULL) { + /* + * If the parent pointer is non-NULL then we take that as + * an indication that the net_instance_int_t is being + * created for an active instance and there will expect + * the create function to be called. In contrast, if + * parent is NULL then this code assumes the object is + * being prepared for insertion onto the master list of + * callbacks to be called when an instance is created, etc. + */ + parent->nini_ref++; + nini->nini_flags |= NSS_CREATE_NEEDED; + } + + cv_init(&nini->nini_cv, NULL, CV_DRIVER, NULL); + + return (nini); +} + +static void +net_instance_int_free(net_instance_int_t *nini) +{ + + cv_destroy(&nini->nini_cv); + + if (nini->nini_parent != NULL) + nini->nini_parent->nini_ref--; + + ASSERT(nini->nini_ref == 0); + kmem_free(nini, sizeof (*nini)); +} + +net_instance_t * +net_instance_alloc(const int version) +{ + net_instance_t *nin; + + if (version != NETINFO_VERSION) + return (NULL); + + nin = kmem_zalloc(sizeof (net_instance_t), KM_SLEEP); + nin->nin_version = version; + + return (nin); +} + +void +net_instance_free(net_instance_t *nin) +{ + kmem_free(nin, sizeof (*nin)); +} + +int +net_instance_register(net_instance_t *nin) +{ + net_instance_int_t *parent; + net_instance_int_t *tmp; + neti_stack_t *nts; + + ASSERT(nin->nin_name != NULL); + + if (nin->nin_create == NULL || nin->nin_destroy == NULL) + return (DDI_FAILURE); + + mutex_enter(&neti_stack_lock); + /* + * Search for duplicate, either on the global list or on any + * of the known instances. + */ + LIST_FOREACH(tmp, &neti_instance_list, nini_next) { + if (strcmp(nin->nin_name, tmp->nini_instance->nin_name) == 0) { + mutex_exit(&neti_stack_lock); + return (DDI_FAILURE); + } + } + + /* + * Now insert and activate. + */ + parent = net_instance_int_create(nin, NULL); + ASSERT(parent != NULL); + LIST_INSERT_HEAD(&neti_instance_list, parent, nini_next); + + LIST_FOREACH(nts, &neti_stack_list, nts_next) { + mutex_enter(&nts->nts_lock); + /* + * If shutdown of the zone has begun then do not add a new + * instance of the object being registered. + */ + if ((nts->nts_flags & NSF_ZONE_SHUTDOWN) || + (nts->nts_netstack == NULL)) { + mutex_exit(&nts->nts_lock); + continue; + } + /* + * This function returns with the NSS_CREATE_NEEDED flag + * set in "dup", so it is adequately prepared for the + * upcoming apply. + */ + tmp = net_instance_int_create(nin, parent); + ASSERT(tmp != NULL); + LIST_INSERT_HEAD(&nts->nts_instances, tmp, nini_next); + mutex_exit(&nts->nts_lock); + + } + + neti_apply_all_stacks(parent, neti_stack_apply_create); + mutex_exit(&neti_stack_lock); + + return (DDI_SUCCESS); +} + +/* + * While net_instance_register() isn't likely to be racing against itself, + * net_instance_unregister() can be entered from various directions that + * can compete: shutdown of a zone, unloading of a module (and it calling + * _unregister() as part of that) and the module doing an _unregister() + * anyway. + */ +int +net_instance_unregister(net_instance_t *nin) +{ + net_instance_int_t *parent; + net_instance_int_t *tmp; + neti_stack_t *nts; + + mutex_enter(&neti_stack_lock); + + LIST_FOREACH(tmp, &neti_instance_list, nini_next) { + if (strcmp(tmp->nini_instance->nin_name, nin->nin_name) == 0) { + LIST_REMOVE(tmp, nini_next); + break; + } + } + + if (tmp == NULL) { + mutex_exit(&neti_stack_lock); + return (DDI_FAILURE); + } + parent = tmp; + + LIST_FOREACH(nts, &neti_stack_list, nts_next) { + mutex_enter(&nts->nts_lock); + LIST_FOREACH(tmp, &nts->nts_instances, nini_next) { + if (tmp->nini_parent != parent) + continue; + /* + * Netstack difference: + * In netstack.c, there is a check for + * NSS_CREATE_COMPLETED before setting the other + * _NEEDED flags. If we consider that a list + * member must always have at least the _CREATE_NEEDED + * flag set and that wait_for_nini_inprogress will + * also wait for that flag to be cleared in both of + * the shutdown and destroy apply functions. + * + * It is possible to optimize out the case where + * all three _NEEDED flags are set to being able + * to pretend everything has been done and just + * set all three _COMPLETE flags. This makes a + * special case that we then need to consider in + * other locations, so for the sake of simplicity, + * we leave it as it is. + */ + if ((tmp->nini_flags & NSS_SHUTDOWN_ALL) == 0) + tmp->nini_flags |= NSS_SHUTDOWN_NEEDED; + if ((tmp->nini_flags & NSS_DESTROY_ALL) == 0) + tmp->nini_flags |= NSS_DESTROY_NEEDED; + } + mutex_exit(&nts->nts_lock); + } + + /* + * Each of these functions ensures that the requisite _COMPLETED + * flag is present before calling the apply function. So we are + * guaranteed to have NSS_CREATE_COMPLETED|NSS_SHUTDOWN_COMPLETED + * both set after the first call here and when the second completes, + * NSS_DESTROY_COMPLETED is also set. + */ + neti_apply_all_stacks(parent, neti_stack_apply_shutdown); + neti_apply_all_stacks(parent, neti_stack_apply_destroy); + + /* + * Remove the instance callback information from each stack. + */ + LIST_FOREACH(nts, &neti_stack_list, nts_next) { + mutex_enter(&nts->nts_lock); + LIST_FOREACH(tmp, &nts->nts_instances, nini_next) { + if ((tmp->nini_parent == parent) && + (tmp->nini_flags & NSS_SHUTDOWN_COMPLETED) && + (tmp->nini_flags & NSS_DESTROY_COMPLETED)) { + /* + * There should only be one entry that has a + * matching nini_parent so there is no need to + * worry about continuing a loop where we are + * free'ing the structure holding the 'next' + * pointer. + */ + LIST_REMOVE(tmp, nini_next); + net_instance_int_free(tmp); + break; + } + } + mutex_exit(&nts->nts_lock); + } + mutex_exit(&neti_stack_lock); + + net_instance_int_free(parent); + + return (DDI_SUCCESS); +} + +static void +neti_apply_all_instances(neti_stack_t *nts, napplyfn_t *applyfn) +{ + net_instance_int_t *n; + + ASSERT(mutex_owned(&neti_stack_lock)); + + n = LIST_FIRST(&nts->nts_instances); + while (n != NULL) { + if ((applyfn)(&neti_stack_lock, nts, n->nini_parent)) { + /* Lock dropped - restart at head */ + n = LIST_FIRST(&nts->nts_instances); + } else { + n = LIST_NEXT(n, nini_next); + } + } +} + +static void +neti_apply_all_stacks(void *parent, napplyfn_t *applyfn) +{ + neti_stack_t *nts; + + ASSERT(mutex_owned(&neti_stack_lock)); + + nts = LIST_FIRST(&neti_stack_list); + while (nts != NULL) { + /* + * This function differs, in that it doesn't have a call to + * a "wait_creator" call, from the zsd/netstack code. The + * waiting is pushed into the apply functions which cause + * the waiting to be done in wait_for_nini_progress with + * the passing in of cmask. + */ + if ((applyfn)(&neti_stack_lock, nts, parent)) { + /* Lock dropped - restart at head */ + nts = LIST_FIRST(&neti_stack_list); + } else { + nts = LIST_NEXT(nts, nts_next); + } + } +} + +static boolean_t +neti_stack_apply_create(kmutex_t *lockp, neti_stack_t *nts, void *parent) +{ + void *result; + boolean_t dropped = B_FALSE; + net_instance_int_t *tmp; + net_instance_t *nin; + + ASSERT(parent != NULL); + ASSERT(lockp != NULL); + ASSERT(mutex_owned(lockp)); + + mutex_enter(&nts->nts_lock); + + LIST_FOREACH(tmp, &nts->nts_instances, nini_next) { + if (tmp->nini_parent == parent) + break; + } + if (tmp == NULL) { + mutex_exit(&nts->nts_lock); + return (dropped); + } + + if (wait_for_nini_inprogress(nts, lockp, tmp, 0)) + dropped = B_TRUE; + + if (tmp->nini_flags & NSS_CREATE_NEEDED) { + nin = tmp->nini_instance; + tmp->nini_flags &= ~NSS_CREATE_NEEDED; + tmp->nini_flags |= NSS_CREATE_INPROGRESS; + DTRACE_PROBE2(neti__stack__create__inprogress, + neti_stack_t *, nts, net_instance_int_t *, tmp); + mutex_exit(&nts->nts_lock); + mutex_exit(lockp); + dropped = B_TRUE; + + ASSERT(tmp->nini_created == NULL); + ASSERT(nin->nin_create != NULL); + DTRACE_PROBE2(neti__stack__create__start, + netstackid_t, nts->nts_id, + neti_stack_t *, nts); + result = (nin->nin_create)(nts->nts_id); + DTRACE_PROBE2(neti__stack__create__end, + void *, result, neti_stack_t *, nts); + + ASSERT(result != NULL); + mutex_enter(lockp); + mutex_enter(&nts->nts_lock); + tmp->nini_created = result; + tmp->nini_flags &= ~NSS_CREATE_INPROGRESS; + tmp->nini_flags |= NSS_CREATE_COMPLETED; + cv_broadcast(&tmp->nini_cv); + DTRACE_PROBE2(neti__stack__create__completed, + neti_stack_t *, nts, net_instance_int_t *, tmp); + } + mutex_exit(&nts->nts_lock); + return (dropped); +} + + +static boolean_t +neti_stack_apply_shutdown(kmutex_t *lockp, neti_stack_t *nts, void *parent) +{ + boolean_t dropped = B_FALSE; + net_instance_int_t *tmp; + net_instance_t *nin; + + ASSERT(parent != NULL); + ASSERT(lockp != NULL); + ASSERT(mutex_owned(lockp)); + + mutex_enter(&nts->nts_lock); + + LIST_FOREACH(tmp, &nts->nts_instances, nini_next) { + if (tmp->nini_parent == parent) + break; + } + if (tmp == NULL) { + mutex_exit(&nts->nts_lock); + return (dropped); + } + + if (wait_for_nini_inprogress(nts, lockp, tmp, NSS_CREATE_NEEDED)) + dropped = B_TRUE; + + nin = tmp->nini_instance; + if (nin->nin_shutdown == NULL) { + /* + * If there is no shutdown function, fake having completed it. + */ + if (tmp->nini_flags & NSS_SHUTDOWN_NEEDED) { + tmp->nini_flags &= ~NSS_SHUTDOWN_NEEDED; + tmp->nini_flags |= NSS_SHUTDOWN_COMPLETED; + } + + mutex_exit(&nts->nts_lock); + return (dropped); + } + + if (tmp->nini_flags & NSS_SHUTDOWN_NEEDED) { + ASSERT((tmp->nini_flags & NSS_CREATE_COMPLETED) != 0); + tmp->nini_flags &= ~NSS_SHUTDOWN_NEEDED; + tmp->nini_flags |= NSS_SHUTDOWN_INPROGRESS; + DTRACE_PROBE2(neti__stack__shutdown__inprogress, + neti_stack_t *, nts, net_instance_int_t *, tmp); + mutex_exit(&nts->nts_lock); + mutex_exit(lockp); + dropped = B_TRUE; + + ASSERT(nin->nin_shutdown != NULL); + DTRACE_PROBE2(neti__stack__shutdown__start, + netstackid_t, nts->nts_id, + neti_stack_t *, nts); + (nin->nin_shutdown)(nts->nts_id, tmp->nini_created); + DTRACE_PROBE1(neti__stack__shutdown__end, + neti_stack_t *, nts); + + mutex_enter(lockp); + mutex_enter(&nts->nts_lock); + tmp->nini_flags &= ~NSS_SHUTDOWN_INPROGRESS; + tmp->nini_flags |= NSS_SHUTDOWN_COMPLETED; + cv_broadcast(&tmp->nini_cv); + DTRACE_PROBE2(neti__stack__shutdown__completed, + neti_stack_t *, nts, net_instance_int_t *, tmp); + } + ASSERT((tmp->nini_flags & NSS_SHUTDOWN_COMPLETED) != 0); + mutex_exit(&nts->nts_lock); + return (dropped); +} + +static boolean_t +neti_stack_apply_destroy(kmutex_t *lockp, neti_stack_t *nts, void *parent) +{ + boolean_t dropped = B_FALSE; + net_instance_int_t *tmp; + net_instance_t *nin; + + ASSERT(parent != NULL); + ASSERT(lockp != NULL); + ASSERT(mutex_owned(lockp)); + + mutex_enter(&nts->nts_lock); + + LIST_FOREACH(tmp, &nts->nts_instances, nini_next) { + if (tmp->nini_parent == parent) + break; + } + if (tmp == NULL) { + mutex_exit(&nts->nts_lock); + return (dropped); + } + + /* + * We pause here so that when we continue we know that we're the + * only one doing anything active with this node. + */ + if (wait_for_nini_inprogress(nts, lockp, tmp, + NSS_CREATE_NEEDED|NSS_SHUTDOWN_NEEDED)) + dropped = B_TRUE; + + if (tmp->nini_flags & NSS_DESTROY_NEEDED) { + ASSERT((tmp->nini_flags & NSS_SHUTDOWN_COMPLETED) != 0); + nin = tmp->nini_instance; + tmp->nini_flags &= ~NSS_DESTROY_NEEDED; + tmp->nini_flags |= NSS_DESTROY_INPROGRESS; + DTRACE_PROBE2(neti__stack__destroy__inprogress, + neti_stack_t *, nts, net_instance_int_t *, tmp); + mutex_exit(&nts->nts_lock); + mutex_exit(lockp); + dropped = B_TRUE; + + ASSERT(nin->nin_destroy != NULL); + DTRACE_PROBE2(neti__stack__destroy__start, + netstackid_t, nts->nts_id, + neti_stack_t *, nts); + (nin->nin_destroy)(nts->nts_id, tmp->nini_created); + DTRACE_PROBE1(neti__stack__destroy__end, + neti_stack_t *, nts); + + mutex_enter(lockp); + mutex_enter(&nts->nts_lock); + tmp->nini_flags &= ~NSS_DESTROY_INPROGRESS; + tmp->nini_flags |= NSS_DESTROY_COMPLETED; + cv_broadcast(&tmp->nini_cv); + DTRACE_PROBE2(neti__stack__destroy__completed, + neti_stack_t *, nts, net_instance_int_t *, tmp); + } + mutex_exit(&nts->nts_lock); + return (dropped); +} + +static boolean_t +wait_for_nini_inprogress(neti_stack_t *nts, kmutex_t *lockp, + net_instance_int_t *nini, uint32_t cmask) +{ + boolean_t dropped = B_FALSE; + + ASSERT(lockp != NULL); + ASSERT(mutex_owned(lockp)); + + while (nini->nini_flags & (NSS_ALL_INPROGRESS|cmask)) { + DTRACE_PROBE2(netstack__wait__nms__inprogress, + neti_stack_t *, nts, net_instance_int_t *, nini); + dropped = B_TRUE; + mutex_exit(lockp); + + cv_wait(&nini->nini_cv, &nts->nts_lock); + + /* First drop netstack_lock to preserve order */ + mutex_exit(&nts->nts_lock); + mutex_enter(lockp); + mutex_enter(&nts->nts_lock); + } + return (dropped); +} + +/* ======================================================================= */ + +netid_t +net_zoneidtonetid(zoneid_t zoneid) +{ + + neti_stack_t *nts; + + mutex_enter(&neti_stack_lock); + LIST_FOREACH(nts, &neti_stack_list, nts_next) { + if (nts->nts_zoneid == zoneid) { + mutex_exit(&neti_stack_lock); + return (nts->nts_id); + } + } + mutex_exit(&neti_stack_lock); + + return (-1); +} + +zoneid_t +net_getzoneidbynetid(netid_t netid) +{ + neti_stack_t *nts; + + mutex_enter(&neti_stack_lock); + LIST_FOREACH(nts, &neti_stack_list, nts_next) { + if (nts->nts_id == netid) { + mutex_exit(&neti_stack_lock); + return (nts->nts_zoneid); + } + } + mutex_exit(&neti_stack_lock); + + return (-1); +} + +netstackid_t +net_getnetstackidbynetid(netid_t netid) +{ + neti_stack_t *nts; + + mutex_enter(&neti_stack_lock); + LIST_FOREACH(nts, &neti_stack_list, nts_next) { + if (nts->nts_id == netid) { + mutex_exit(&neti_stack_lock); + return (nts->nts_stackid); + } + } + mutex_exit(&neti_stack_lock); + + return (-1); +} + +netid_t +net_getnetidbynetstackid(netstackid_t netstackid) +{ + neti_stack_t *nts; + + mutex_enter(&neti_stack_lock); + LIST_FOREACH(nts, &neti_stack_list, nts_next) { + if (nts->nts_stackid == netstackid) { + mutex_exit(&neti_stack_lock); + return (nts->nts_id); + } + } + mutex_exit(&neti_stack_lock); + + return (-1); +} + +neti_stack_t * +net_getnetistackbyid(netid_t netid) +{ + neti_stack_t *nts; + + mutex_enter(&neti_stack_lock); + LIST_FOREACH(nts, &neti_stack_list, nts_next) { + if (nts->nts_id == netid) { + mutex_exit(&neti_stack_lock); + return (nts); + } + } + mutex_exit(&neti_stack_lock); + + return (NULL); +} + +int +net_instance_notify_register(netid_t netid, hook_notify_fn_t callback, + void *arg) +{ + + return (hook_stack_notify_register(net_getnetstackidbynetid(netid), + callback, arg)); +} + +int +net_instance_notify_unregister(netid_t netid, hook_notify_fn_t callback) +{ + + return (hook_stack_notify_unregister(net_getnetstackidbynetid(netid), + callback)); +} |