summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/neti_stack.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/neti_stack.c')
-rw-r--r--usr/src/uts/common/io/neti_stack.c840
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));
+}