xref: /illumos-gate/usr/src/uts/common/io/neti_stack.c (revision eb9a1df2aeb866bf1de4494433b6d7e5fa07b3ae)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/param.h>
27 #include <sys/atomic.h>
28 #include <sys/kmem.h>
29 #include <sys/rwlock.h>
30 #include <sys/errno.h>
31 #include <sys/queue.h>
32 #include <sys/sunddi.h>
33 #include <inet/common.h>
34 #include <inet/led.h>
35 #include <inet/ip.h>
36 #include <sys/neti.h>
37 #include <sys/zone.h>
38 #include <sys/sdt.h>
39 
40 
41 typedef boolean_t napplyfn_t(neti_stack_t *, void *);
42 
43 static void *neti_stack_init(netstackid_t stackid, netstack_t *ns);
44 static void neti_stack_fini(netstackid_t stackid, void *arg);
45 static net_instance_int_t *net_instance_int_create(net_instance_t *nin,
46     net_instance_int_t *parent);
47 static void neti_stack_shutdown(netstackid_t stackid, void *arg);
48 static void net_instance_int_free(net_instance_int_t *nini);
49 
50 static boolean_t neti_stack_apply_create(neti_stack_t *, void *);
51 static boolean_t neti_stack_apply_destroy(neti_stack_t *, void *);
52 static boolean_t neti_stack_apply_shutdown(neti_stack_t *, void *);
53 static void neti_apply_all_instances(neti_stack_t *, napplyfn_t *);
54 static void neti_apply_all_stacks(void *, napplyfn_t *);
55 static boolean_t wait_for_nini_inprogress(neti_stack_t *,
56     net_instance_int_t *, uint32_t);
57 
58 static nini_head_t neti_instance_list;
59 static neti_stack_head_t neti_stack_list;
60 static kmutex_t neti_stack_lock;
61 
62 void
63 neti_init()
64 {
65 	mutex_init(&neti_stack_lock, NULL, MUTEX_DRIVER, NULL);
66 
67 	LIST_INIT(&neti_instance_list);
68 	LIST_INIT(&neti_stack_list);
69 	/*
70 	 * We want to be informed each time a netstack is created or
71 	 * destroyed in the kernel.
72 	 */
73 	netstack_register(NS_NETI, neti_stack_init, neti_stack_shutdown,
74 	    neti_stack_fini);
75 }
76 
77 void
78 neti_fini()
79 {
80 	ASSERT(LIST_EMPTY(&neti_instance_list));
81 	ASSERT(LIST_EMPTY(&neti_stack_list));
82 
83 	netstack_unregister(NS_NETI);
84 
85 	mutex_destroy(&neti_stack_lock);
86 }
87 
88 /*
89  * Initialize the neti stack instance.  Because this is called out of the
90  * netstack framework, it is not possible for it to be called twice with
91  * the same values for (stackid,ns).  The same also applies to the other
92  * two functions used with netstack_register: neti_stack_shutdown and
93  * neti_stack_fini.
94  */
95 static void *
96 neti_stack_init(netstackid_t stackid, netstack_t *ns)
97 {
98 	net_instance_int_t *dup;
99 	net_instance_int_t *n;
100 	neti_stack_t *nts;
101 
102 	nts = kmem_zalloc(sizeof (*nts), KM_SLEEP);
103 	LIST_INIT(&nts->nts_instances);
104 	nts->nts_id = (netid_t)stackid;
105 	nts->nts_stackid = stackid;
106 	nts->nts_netstack = ns;
107 	nts->nts_zoneid = netstackid_to_zoneid(stackid);
108 	nts->nts_flags = NSF_ZONE_CREATE;
109 	cv_init(&nts->nts_cv, NULL, CV_DRIVER, NULL);
110 	mutex_init(&nts->nts_lock, NULL, MUTEX_DRIVER, NULL);
111 
112 	mutex_enter(&neti_stack_lock);
113 	LIST_INSERT_HEAD(&neti_stack_list, nts, nts_next);
114 
115 	LIST_FOREACH(n, &neti_instance_list, nini_next) {
116 		/*
117 		 * This function returns with the NSS_CREATE_NEEDED flag
118 		 * set in "dup", so it is adequately prepared for the
119 		 * upcoming apply.
120 		 */
121 		dup = net_instance_int_create(n->nini_instance, n);
122 
123 		mutex_enter(&nts->nts_lock);
124 		LIST_INSERT_HEAD(&nts->nts_instances, dup, nini_next);
125 		mutex_exit(&nts->nts_lock);
126 	}
127 
128 	neti_apply_all_instances(nts, neti_stack_apply_create);
129 
130 	mutex_enter(&nts->nts_lock);
131 	nts->nts_flags &= ~NSF_ZONE_CREATE;
132 	mutex_exit(&nts->nts_lock);
133 
134 	mutex_exit(&neti_stack_lock);
135 
136 	return (nts);
137 }
138 
139 /*
140  * Run the shutdown for all of the hooks.
141  */
142 /*ARGSUSED*/
143 static void
144 neti_stack_shutdown(netstackid_t stackid, void *arg)
145 {
146 	neti_stack_t *nts = arg;
147 	net_instance_int_t *n;
148 	struct net_data *nd;
149 
150 	ASSERT(nts != NULL);
151 
152 	mutex_enter(&neti_stack_lock);
153 	mutex_enter(&nts->nts_lock);
154 	/*
155 	 * Walk through all of the protocol stacks and mark them as shutting
156 	 * down.
157 	 */
158 	LIST_FOREACH(nd, &nts->nts_netd_head, netd_list) {
159 		nd->netd_condemned = 1;
160 	}
161 
162 	/*
163 	 * Now proceed to see which callbacks are waiting to hear about the
164 	 * impending shutdown...
165 	 */
166 	LIST_FOREACH(n, &nts->nts_instances, nini_next) {
167 		if (n->nini_instance->nin_shutdown == NULL) {
168 			/*
169 			 * If there is no shutdown function registered,
170 			 * fake that we have completed it.
171 			 */
172 			n->nini_flags |= NSS_SHUTDOWN_COMPLETED;
173 			continue;
174 		}
175 
176 		/*
177 		 * We need to ensure that we don't try and shutdown something
178 		 * that is already in the process of being shutdown or
179 		 * destroyed. If it is still being created, that's ok, the
180 		 * shtudown flag is added to the mix of things to do.
181 		 */
182 		if ((n->nini_flags & (NSS_DESTROY_ALL|NSS_SHUTDOWN_ALL)) == 0)
183 			n->nini_flags |= NSS_SHUTDOWN_NEEDED;
184 	}
185 	nts->nts_flags |= NSF_ZONE_SHUTDOWN;
186 	mutex_exit(&nts->nts_lock);
187 
188 	neti_apply_all_instances(nts, neti_stack_apply_shutdown);
189 
190 	mutex_enter(&nts->nts_lock);
191 
192 	nts->nts_netstack = NULL;
193 	nts->nts_flags &= ~NSF_ZONE_SHUTDOWN;
194 	mutex_exit(&nts->nts_lock);
195 
196 	mutex_exit(&neti_stack_lock);
197 	ASSERT(nts != NULL);
198 }
199 
200 /*
201  * Free the neti stack instance.
202  * This function relies on the netstack framework only calling the _destroy
203  * callback once for each stackid.  The netstack framework also provides us
204  * with assurance that nobody else will be doing any work (_create, _shutdown)
205  * on it, so there is no need to set and use flags to guard against
206  * simultaneous execution (ie. no need to set NSF_CLOSING.)
207  * What is required, however, is to make sure that we don't corrupt the
208  * list of neti_stack_t's for other code that walks it.
209  */
210 /*ARGSUSED*/
211 static void
212 neti_stack_fini(netstackid_t stackid, void *arg)
213 {
214 	neti_stack_t *nts = arg;
215 	net_instance_int_t *n;
216 	struct net_data *nd;
217 
218 	mutex_enter(&neti_stack_lock);
219 	mutex_enter(&nts->nts_lock);
220 
221 	LIST_REMOVE(nts, nts_next);
222 
223 	/*
224 	 * Walk through all of the protocol stacks and mark them as being
225 	 * destroyed.
226 	 */
227 	LIST_FOREACH(nd, &nts->nts_netd_head, netd_list) {
228 		nd->netd_condemned = 2;
229 	}
230 
231 	LIST_FOREACH(n, &nts->nts_instances, nini_next) {
232 		ASSERT((n->nini_flags & NSS_SHUTDOWN_ALL) != 0);
233 		if ((n->nini_flags & NSS_DESTROY_ALL) == 0)
234 			n->nini_flags |= NSS_DESTROY_NEEDED;
235 	}
236 	mutex_exit(&nts->nts_lock);
237 
238 	neti_apply_all_instances(nts, neti_stack_apply_destroy);
239 
240 	while (!LIST_EMPTY(&nts->nts_instances)) {
241 		n = LIST_FIRST(&nts->nts_instances);
242 		LIST_REMOVE(n, nini_next);
243 
244 		net_instance_int_free(n);
245 	}
246 	mutex_exit(&neti_stack_lock);
247 
248 	ASSERT(LIST_EMPTY(&nts->nts_netd_head));
249 
250 	mutex_destroy(&nts->nts_lock);
251 	cv_destroy(&nts->nts_cv);
252 
253 	kmem_free(nts, sizeof (*nts));
254 }
255 
256 static net_instance_int_t *
257 net_instance_int_create(net_instance_t *nin, net_instance_int_t *parent)
258 {
259 	net_instance_int_t *nini;
260 
261 	nini = kmem_zalloc(sizeof (net_instance_int_t), KM_SLEEP);
262 	nini->nini_instance = nin;
263 	nini->nini_parent = parent;
264 	if (parent != NULL) {
265 		/*
266 		 * If the parent pointer is non-NULL then we take that as
267 		 * an indication that the net_instance_int_t is being
268 		 * created for an active instance and there will expect
269 		 * the create function to be called.  In contrast, if
270 		 * parent is NULL then this code assumes the object is
271 		 * being prepared for insertion onto the master list of
272 		 * callbacks to be called when an instance is created, etc.
273 		 */
274 		parent->nini_ref++;
275 		nini->nini_flags |= NSS_CREATE_NEEDED;
276 	}
277 
278 	cv_init(&nini->nini_cv, NULL, CV_DRIVER, NULL);
279 
280 	return (nini);
281 }
282 
283 /*
284  * Free'ing of a net_instance_int_t is only to be done when we know nobody
285  * else has is using it. For both parents and clones, this is indicated by
286  * nini_ref being greater than 0, however, nini_ref is managed differently
287  * for its two uses. For parents, nini_ref is increased when a new clone is
288  * created and it is decremented here. For clones, nini_ref is adjusted by
289  * code elsewhere (e.g. in neti_stack_apply_*) and is not changed here.
290  */
291 static void
292 net_instance_int_free(net_instance_int_t *nini)
293 {
294 	/*
295 	 * This mutex guards the use of nini_ref.
296 	 */
297 	ASSERT(mutex_owned(&neti_stack_lock));
298 
299 	/*
300 	 * For 'parent' structures, nini_ref will drop to 0 when
301 	 * the last clone has been free'd... but for clones, it
302 	 * is possible for nini_ref to be non-zero if we get in
303 	 * here when all the locks have been given up to execute
304 	 * a callback or wait_for_nini_inprogress. In that case,
305 	 * we do not want to free the structure and just indicate
306 	 * that it is on the "doomed" list, thus we set the
307 	 * condemned flag.
308 	 */
309 	if (nini->nini_parent != NULL) {
310 		if (nini->nini_ref > 0)
311 			nini->nini_condemned = B_TRUE;
312 		nini->nini_parent->nini_ref--;
313 		if (nini->nini_parent->nini_ref == 0)
314 			net_instance_int_free(nini->nini_parent);
315 		nini->nini_parent = NULL;
316 	}
317 
318 	if (nini->nini_ref == 0) {
319 		cv_destroy(&nini->nini_cv);
320 		kmem_free(nini, sizeof (*nini));
321 	}
322 }
323 
324 net_instance_t *
325 net_instance_alloc(const int version)
326 {
327 	net_instance_t *nin;
328 
329 	if (version != NETINFO_VERSION)
330 		return (NULL);
331 
332 	nin = kmem_zalloc(sizeof (net_instance_t), KM_SLEEP);
333 	nin->nin_version = version;
334 
335 	return (nin);
336 }
337 
338 void
339 net_instance_free(net_instance_t *nin)
340 {
341 	kmem_free(nin, sizeof (*nin));
342 }
343 
344 int
345 net_instance_register(net_instance_t *nin)
346 {
347 	net_instance_int_t *parent;
348 	net_instance_int_t *tmp;
349 	neti_stack_t *nts;
350 
351 	ASSERT(nin->nin_name != NULL);
352 
353 	if (nin->nin_create == NULL || nin->nin_destroy == NULL)
354 		return (DDI_FAILURE);
355 
356 	mutex_enter(&neti_stack_lock);
357 	/*
358 	 * Search for duplicate, either on the global list or on any
359 	 * of the known instances.
360 	 */
361 	LIST_FOREACH(tmp, &neti_instance_list, nini_next) {
362 		if (strcmp(nin->nin_name, tmp->nini_instance->nin_name) == 0) {
363 			mutex_exit(&neti_stack_lock);
364 			return (DDI_FAILURE);
365 		}
366 	}
367 
368 	/*
369 	 * Now insert and activate.
370 	 */
371 	parent = net_instance_int_create(nin, NULL);
372 	ASSERT(parent != NULL);
373 	LIST_INSERT_HEAD(&neti_instance_list, parent, nini_next);
374 
375 	LIST_FOREACH(nts, &neti_stack_list, nts_next) {
376 		mutex_enter(&nts->nts_lock);
377 		/*
378 		 * If shutdown of the zone has begun then do not add a new
379 		 * instance of the object being registered.
380 		 */
381 		if ((nts->nts_flags & NSF_ZONE_SHUTDOWN) ||
382 		    (nts->nts_netstack == NULL)) {
383 			mutex_exit(&nts->nts_lock);
384 			continue;
385 		}
386 
387 		/*
388 		 * This function returns with the NSS_CREATE_NEEDED flag
389 		 * set in "dup", so it is adequately prepared for the
390 		 * upcoming apply.
391 		 */
392 		tmp = net_instance_int_create(nin, parent);
393 		ASSERT(tmp != NULL);
394 		LIST_INSERT_HEAD(&nts->nts_instances, tmp, nini_next);
395 		mutex_exit(&nts->nts_lock);
396 
397 	}
398 
399 	neti_apply_all_stacks(parent, neti_stack_apply_create);
400 	mutex_exit(&neti_stack_lock);
401 
402 	return (DDI_SUCCESS);
403 }
404 
405 /*
406  * While net_instance_register() isn't likely to be racing against itself,
407  * net_instance_unregister() can be entered from various directions that
408  * can compete: shutdown of a zone, unloading of a module (and it calling
409  * _unregister() as part of that) and the module doing an _unregister()
410  * anyway.
411  */
412 int
413 net_instance_unregister(net_instance_t *nin)
414 {
415 	net_instance_int_t *parent;
416 	net_instance_int_t *tmp;
417 	neti_stack_t *nts;
418 
419 	mutex_enter(&neti_stack_lock);
420 
421 	LIST_FOREACH(tmp, &neti_instance_list, nini_next) {
422 		if (strcmp(tmp->nini_instance->nin_name, nin->nin_name) == 0) {
423 			LIST_REMOVE(tmp, nini_next);
424 			break;
425 		}
426 	}
427 
428 	if (tmp == NULL) {
429 		mutex_exit(&neti_stack_lock);
430 		return (DDI_FAILURE);
431 	}
432 	parent = tmp;
433 
434 	LIST_FOREACH(nts, &neti_stack_list, nts_next) {
435 		mutex_enter(&nts->nts_lock);
436 		LIST_FOREACH(tmp, &nts->nts_instances, nini_next) {
437 			if (tmp->nini_parent != parent)
438 				continue;
439 			/*
440 			 * Netstack difference:
441 			 * In netstack.c, there is a check for
442 			 * NSS_CREATE_COMPLETED before setting the other
443 			 * _NEEDED flags.  If we consider that a list
444 			 * member must always have at least the _CREATE_NEEDED
445 			 * flag set and that wait_for_nini_inprogress will
446 			 * also wait for that flag to be cleared in both of
447 			 * the shutdown and destroy apply functions.
448 			 *
449 			 * It is possible to optimize out the case where
450 			 * all three _NEEDED flags are set to being able
451 			 * to pretend everything has been done and just
452 			 * set all three _COMPLETE flags.  This makes a
453 			 * special case that we then need to consider in
454 			 * other locations, so for the sake of simplicity,
455 			 * we leave it as it is.
456 			 */
457 			if ((tmp->nini_flags & NSS_SHUTDOWN_ALL) == 0)
458 				tmp->nini_flags |= NSS_SHUTDOWN_NEEDED;
459 			if ((tmp->nini_flags & NSS_DESTROY_ALL) == 0)
460 				tmp->nini_flags |= NSS_DESTROY_NEEDED;
461 			break;
462 		}
463 		mutex_exit(&nts->nts_lock);
464 	}
465 
466 	/*
467 	 * Each of these functions ensures that the requisite _COMPLETED
468 	 * flag is present before calling the apply function. So we are
469 	 * guaranteed to have NSS_CREATE_COMPLETED|NSS_SHUTDOWN_COMPLETED
470 	 * both set after the first call here and when the second completes,
471 	 * NSS_DESTROY_COMPLETED is also set.
472 	 */
473 	neti_apply_all_stacks(parent, neti_stack_apply_shutdown);
474 	neti_apply_all_stacks(parent, neti_stack_apply_destroy);
475 
476 	/*
477 	 * Remove the instance callback information from each stack.
478 	 */
479 	LIST_FOREACH(nts, &neti_stack_list, nts_next) {
480 		mutex_enter(&nts->nts_lock);
481 		LIST_FOREACH(tmp, &nts->nts_instances, nini_next) {
482 			if ((tmp->nini_parent == parent) &&
483 			    (tmp->nini_flags & NSS_SHUTDOWN_COMPLETED) &&
484 			    (tmp->nini_flags & NSS_DESTROY_COMPLETED)) {
485 				/*
486 				 * There should only be one entry that has a
487 				 * matching nini_parent so there is no need to
488 				 * worry about continuing a loop where we are
489 				 * free'ing the structure holding the 'next'
490 				 * pointer.
491 				 */
492 				LIST_REMOVE(tmp, nini_next);
493 				net_instance_int_free(tmp);
494 				break;
495 			}
496 		}
497 		mutex_exit(&nts->nts_lock);
498 	}
499 
500 	mutex_exit(&neti_stack_lock);
501 
502 	return (DDI_SUCCESS);
503 }
504 
505 static void
506 neti_apply_all_instances(neti_stack_t *nts, napplyfn_t *applyfn)
507 {
508 	net_instance_int_t *n;
509 
510 	ASSERT(mutex_owned(&neti_stack_lock));
511 
512 	n = LIST_FIRST(&nts->nts_instances);
513 	while (n != NULL) {
514 		if ((applyfn)(nts, n->nini_parent)) {
515 			/* Lock dropped - restart at head */
516 			n = LIST_FIRST(&nts->nts_instances);
517 		} else {
518 			n = LIST_NEXT(n, nini_next);
519 		}
520 	}
521 }
522 
523 static void
524 neti_apply_all_stacks(void *parent, napplyfn_t *applyfn)
525 {
526 	neti_stack_t *nts;
527 
528 	ASSERT(mutex_owned(&neti_stack_lock));
529 
530 	nts = LIST_FIRST(&neti_stack_list);
531 	while (nts != NULL) {
532 		/*
533 		 * This function differs, in that it doesn't have a call to
534 		 * a "wait_creator" call, from the zsd/netstack code.  The
535 		 * waiting is pushed into the apply functions which cause
536 		 * the waiting to be done in wait_for_nini_progress with
537 		 * the passing in of cmask.
538 		 */
539 		if ((applyfn)(nts, parent)) {
540 			/* Lock dropped - restart at head */
541 			nts = LIST_FIRST(&neti_stack_list);
542 		} else {
543 			nts = LIST_NEXT(nts, nts_next);
544 		}
545 	}
546 }
547 
548 static boolean_t
549 neti_stack_apply_create(neti_stack_t *nts, void *parent)
550 {
551 	void *result;
552 	boolean_t dropped = B_FALSE;
553 	net_instance_int_t *tmp;
554 	net_instance_t *nin;
555 
556 	ASSERT(parent != NULL);
557 	ASSERT(mutex_owned(&neti_stack_lock));
558 
559 	mutex_enter(&nts->nts_lock);
560 
561 	LIST_FOREACH(tmp, &nts->nts_instances, nini_next) {
562 		if (tmp->nini_parent == parent)
563 			break;
564 	}
565 	if (tmp == NULL) {
566 		mutex_exit(&nts->nts_lock);
567 		return (dropped);
568 	}
569 
570 	tmp->nini_ref++;
571 
572 	if (wait_for_nini_inprogress(nts, tmp, 0))
573 		dropped = B_TRUE;
574 
575 	if ((tmp->nini_flags & NSS_CREATE_NEEDED) && !tmp->nini_condemned) {
576 		nin = tmp->nini_instance;
577 		tmp->nini_flags &= ~NSS_CREATE_NEEDED;
578 		tmp->nini_flags |= NSS_CREATE_INPROGRESS;
579 		DTRACE_PROBE2(neti__stack__create__inprogress,
580 		    neti_stack_t *, nts, net_instance_int_t *, tmp);
581 		mutex_exit(&nts->nts_lock);
582 		mutex_exit(&neti_stack_lock);
583 		dropped = B_TRUE;
584 
585 		ASSERT(tmp->nini_created == NULL);
586 		ASSERT(nin->nin_create != NULL);
587 		DTRACE_PROBE2(neti__stack__create__start,
588 		    netstackid_t, nts->nts_id,
589 		    neti_stack_t *, nts);
590 		result = (nin->nin_create)(nts->nts_id);
591 		DTRACE_PROBE2(neti__stack__create__end,
592 		    void *, result, neti_stack_t *, nts);
593 
594 		ASSERT(result != NULL);
595 		mutex_enter(&neti_stack_lock);
596 		mutex_enter(&nts->nts_lock);
597 		tmp->nini_created = result;
598 		tmp->nini_flags &= ~NSS_CREATE_INPROGRESS;
599 		tmp->nini_flags |= NSS_CREATE_COMPLETED;
600 		cv_broadcast(&tmp->nini_cv);
601 		DTRACE_PROBE2(neti__stack__create__completed,
602 		    neti_stack_t *, nts, net_instance_int_t *, tmp);
603 	}
604 	tmp->nini_ref--;
605 
606 	if (tmp->nini_condemned) {
607 		net_instance_int_free(tmp);
608 		dropped = B_TRUE;
609 	}
610 	mutex_exit(&nts->nts_lock);
611 	return (dropped);
612 }
613 
614 
615 static boolean_t
616 neti_stack_apply_shutdown(neti_stack_t *nts, void *parent)
617 {
618 	boolean_t dropped = B_FALSE;
619 	net_instance_int_t *tmp;
620 	net_instance_t *nin;
621 
622 	ASSERT(parent != NULL);
623 	ASSERT(mutex_owned(&neti_stack_lock));
624 
625 	mutex_enter(&nts->nts_lock);
626 
627 	LIST_FOREACH(tmp, &nts->nts_instances, nini_next) {
628 		if (tmp->nini_parent == parent)
629 			break;
630 	}
631 	if (tmp == NULL) {
632 		mutex_exit(&nts->nts_lock);
633 		return (dropped);
634 	}
635 	ASSERT((tmp->nini_flags & NSS_SHUTDOWN_ALL) != 0);
636 
637 	tmp->nini_ref++;
638 
639 	if (wait_for_nini_inprogress(nts, tmp, NSS_CREATE_NEEDED))
640 		dropped = B_TRUE;
641 
642 	nin = tmp->nini_instance;
643 	if (nin->nin_shutdown == NULL) {
644 		/*
645 		 * If there is no shutdown function, fake having completed it.
646 		 */
647 		if (tmp->nini_flags & NSS_SHUTDOWN_NEEDED) {
648 			tmp->nini_flags &= ~NSS_SHUTDOWN_NEEDED;
649 			tmp->nini_flags |= NSS_SHUTDOWN_COMPLETED;
650 		}
651 		tmp->nini_ref--;
652 
653 		if (tmp->nini_condemned) {
654 			net_instance_int_free(tmp);
655 			dropped = B_TRUE;
656 		}
657 
658 		mutex_exit(&nts->nts_lock);
659 		return (dropped);
660 	}
661 
662 	if ((tmp->nini_flags & NSS_SHUTDOWN_NEEDED) && !tmp->nini_condemned) {
663 		ASSERT((tmp->nini_flags & NSS_CREATE_COMPLETED) != 0);
664 		tmp->nini_flags &= ~NSS_SHUTDOWN_NEEDED;
665 		tmp->nini_flags |= NSS_SHUTDOWN_INPROGRESS;
666 		DTRACE_PROBE2(neti__stack__shutdown__inprogress,
667 		    neti_stack_t *, nts, net_instance_int_t *, tmp);
668 		mutex_exit(&nts->nts_lock);
669 		mutex_exit(&neti_stack_lock);
670 		dropped = B_TRUE;
671 
672 		ASSERT(nin->nin_shutdown != NULL);
673 		DTRACE_PROBE2(neti__stack__shutdown__start,
674 		    netstackid_t, nts->nts_id,
675 		    neti_stack_t *, nts);
676 		(nin->nin_shutdown)(nts->nts_id, tmp->nini_created);
677 		DTRACE_PROBE1(neti__stack__shutdown__end,
678 		    neti_stack_t *, nts);
679 
680 		mutex_enter(&neti_stack_lock);
681 		mutex_enter(&nts->nts_lock);
682 		tmp->nini_flags &= ~NSS_SHUTDOWN_INPROGRESS;
683 		tmp->nini_flags |= NSS_SHUTDOWN_COMPLETED;
684 		cv_broadcast(&tmp->nini_cv);
685 		DTRACE_PROBE2(neti__stack__shutdown__completed,
686 		    neti_stack_t *, nts, net_instance_int_t *, tmp);
687 	}
688 	ASSERT((tmp->nini_flags & NSS_SHUTDOWN_COMPLETED) != 0);
689 	tmp->nini_ref--;
690 
691 	if (tmp->nini_condemned) {
692 		net_instance_int_free(tmp);
693 		dropped = B_TRUE;
694 	}
695 	mutex_exit(&nts->nts_lock);
696 	return (dropped);
697 }
698 
699 static boolean_t
700 neti_stack_apply_destroy(neti_stack_t *nts, void *parent)
701 {
702 	boolean_t dropped = B_FALSE;
703 	net_instance_int_t *tmp;
704 	net_instance_t *nin;
705 
706 	ASSERT(parent != NULL);
707 	ASSERT(mutex_owned(&neti_stack_lock));
708 
709 	mutex_enter(&nts->nts_lock);
710 
711 	LIST_FOREACH(tmp, &nts->nts_instances, nini_next) {
712 		if (tmp->nini_parent == parent)
713 			break;
714 	}
715 	if (tmp == NULL) {
716 		mutex_exit(&nts->nts_lock);
717 		return (dropped);
718 	}
719 
720 	tmp->nini_ref++;
721 
722 	/*
723 	 * We pause here so that when we continue we know that we're the
724 	 * only one doing anything active with this node.
725 	 */
726 	if (wait_for_nini_inprogress(nts, tmp,
727 	    NSS_CREATE_NEEDED|NSS_SHUTDOWN_NEEDED))
728 		dropped = B_TRUE;
729 
730 	if ((tmp->nini_flags & NSS_DESTROY_NEEDED) && !tmp->nini_condemned) {
731 		ASSERT((tmp->nini_flags & NSS_SHUTDOWN_COMPLETED) != 0);
732 		nin = tmp->nini_instance;
733 		tmp->nini_flags &= ~NSS_DESTROY_NEEDED;
734 		tmp->nini_flags |= NSS_DESTROY_INPROGRESS;
735 		DTRACE_PROBE2(neti__stack__destroy__inprogress,
736 		    neti_stack_t *, nts, net_instance_int_t *, tmp);
737 		mutex_exit(&nts->nts_lock);
738 		mutex_exit(&neti_stack_lock);
739 		dropped = B_TRUE;
740 
741 		ASSERT(nin->nin_destroy != NULL);
742 		DTRACE_PROBE2(neti__stack__destroy__start,
743 		    netstackid_t, nts->nts_id,
744 		    neti_stack_t *, nts);
745 		(nin->nin_destroy)(nts->nts_id, tmp->nini_created);
746 		DTRACE_PROBE1(neti__stack__destroy__end,
747 		    neti_stack_t *, nts);
748 
749 		mutex_enter(&neti_stack_lock);
750 		mutex_enter(&nts->nts_lock);
751 		tmp->nini_flags &= ~NSS_DESTROY_INPROGRESS;
752 		tmp->nini_flags |= NSS_DESTROY_COMPLETED;
753 		cv_broadcast(&tmp->nini_cv);
754 		DTRACE_PROBE2(neti__stack__destroy__completed,
755 		    neti_stack_t *, nts, net_instance_int_t *, tmp);
756 	}
757 	tmp->nini_ref--;
758 
759 	if (tmp->nini_condemned) {
760 		net_instance_int_free(tmp);
761 		dropped = B_TRUE;
762 	}
763 	mutex_exit(&nts->nts_lock);
764 	return (dropped);
765 }
766 
767 static boolean_t
768 wait_for_nini_inprogress(neti_stack_t *nts, net_instance_int_t *nini,
769     uint32_t cmask)
770 {
771 	boolean_t dropped = B_FALSE;
772 
773 	ASSERT(mutex_owned(&neti_stack_lock));
774 
775 	while (nini->nini_flags & (NSS_ALL_INPROGRESS|cmask)) {
776 		DTRACE_PROBE2(neti__wait__nini__inprogress,
777 		    neti_stack_t *, nts, net_instance_int_t *, nini);
778 		dropped = B_TRUE;
779 		mutex_exit(&neti_stack_lock);
780 
781 		cv_wait(&nini->nini_cv, &nts->nts_lock);
782 
783 		/* First drop netstack_lock to preserve order */
784 		mutex_exit(&nts->nts_lock);
785 		DTRACE_PROBE2(wait__nini__inprogress__pause,
786 		    neti_stack_t *, nts, net_instance_int_t *, nini);
787 		mutex_enter(&neti_stack_lock);
788 		mutex_enter(&nts->nts_lock);
789 	}
790 	DTRACE_PROBE2(neti__wait__nini__inprogress__complete,
791 	    neti_stack_t *, nts, net_instance_int_t *, nini);
792 	return (dropped);
793 }
794 
795 /* ======================================================================= */
796 
797 netid_t
798 net_zoneidtonetid(zoneid_t zoneid)
799 {
800 
801 	neti_stack_t *nts;
802 
803 	mutex_enter(&neti_stack_lock);
804 	LIST_FOREACH(nts, &neti_stack_list, nts_next) {
805 		if (nts->nts_zoneid == zoneid) {
806 			mutex_exit(&neti_stack_lock);
807 			return (nts->nts_id);
808 		}
809 	}
810 	mutex_exit(&neti_stack_lock);
811 
812 	return (-1);
813 }
814 
815 zoneid_t
816 net_getzoneidbynetid(netid_t netid)
817 {
818 	neti_stack_t *nts;
819 
820 	mutex_enter(&neti_stack_lock);
821 	LIST_FOREACH(nts, &neti_stack_list, nts_next) {
822 		if (nts->nts_id == netid) {
823 			mutex_exit(&neti_stack_lock);
824 			return (nts->nts_zoneid);
825 		}
826 	}
827 	mutex_exit(&neti_stack_lock);
828 
829 	return (-1);
830 }
831 
832 netstackid_t
833 net_getnetstackidbynetid(netid_t netid)
834 {
835 	neti_stack_t *nts;
836 
837 	mutex_enter(&neti_stack_lock);
838 	LIST_FOREACH(nts, &neti_stack_list, nts_next) {
839 		if (nts->nts_id == netid) {
840 			mutex_exit(&neti_stack_lock);
841 			return (nts->nts_stackid);
842 		}
843 	}
844 	mutex_exit(&neti_stack_lock);
845 
846 	return (-1);
847 }
848 
849 netid_t
850 net_getnetidbynetstackid(netstackid_t netstackid)
851 {
852 	neti_stack_t *nts;
853 
854 	mutex_enter(&neti_stack_lock);
855 	LIST_FOREACH(nts, &neti_stack_list, nts_next) {
856 		if (nts->nts_stackid == netstackid) {
857 			mutex_exit(&neti_stack_lock);
858 			return (nts->nts_id);
859 		}
860 	}
861 	mutex_exit(&neti_stack_lock);
862 
863 	return (-1);
864 }
865 
866 neti_stack_t *
867 net_getnetistackbyid(netid_t netid)
868 {
869 	neti_stack_t *nts;
870 
871 	mutex_enter(&neti_stack_lock);
872 	LIST_FOREACH(nts, &neti_stack_list, nts_next) {
873 		if (nts->nts_id == netid) {
874 			mutex_exit(&neti_stack_lock);
875 			return (nts);
876 		}
877 	}
878 	mutex_exit(&neti_stack_lock);
879 
880 	return (NULL);
881 }
882 
883 int
884 net_instance_notify_register(netid_t netid, hook_notify_fn_t callback,
885     void *arg)
886 {
887 
888 	return (hook_stack_notify_register(net_getnetstackidbynetid(netid),
889 	    callback, arg));
890 }
891 
892 int
893 net_instance_notify_unregister(netid_t netid, hook_notify_fn_t callback)
894 {
895 
896 	return (hook_stack_notify_unregister(net_getnetstackidbynetid(netid),
897 	    callback));
898 }
899