xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_util.c (revision 694c35faa87b858ecdadfe4fc592615f4eefbb07)
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 /*
23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Utility functions used by the dlmgmtd daemon.
28  */
29 
30 #include <assert.h>
31 #include <pthread.h>
32 #include <stddef.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <strings.h>
37 #include <string.h>
38 #include <syslog.h>
39 #include <stdarg.h>
40 #include <zone.h>
41 #include <errno.h>
42 #include <libdlpi.h>
43 #include "dlmgmt_impl.h"
44 
45 /*
46  * There are three datalink AVL tables.  The dlmgmt_name_avl tree contains all
47  * datalinks and is keyed by zoneid and link name.  The dlmgmt_id_avl also
48  * contains all datalinks, and it is keyed by link ID.  The dlmgmt_loan_avl is
49  * keyed by link name, and contains the set of global-zone links that are
50  * currently on loan to non-global zones.
51  */
52 avl_tree_t	dlmgmt_name_avl;
53 avl_tree_t	dlmgmt_id_avl;
54 avl_tree_t	dlmgmt_loan_avl;
55 
56 avl_tree_t	dlmgmt_dlconf_avl;
57 
58 static pthread_rwlock_t	dlmgmt_avl_lock = PTHREAD_RWLOCK_INITIALIZER;
59 static pthread_mutex_t  dlmgmt_avl_mutex = PTHREAD_MUTEX_INITIALIZER;
60 static pthread_cond_t	dlmgmt_avl_cv = PTHREAD_COND_INITIALIZER;
61 static pthread_rwlock_t	dlmgmt_dlconf_lock = PTHREAD_RWLOCK_INITIALIZER;
62 
63 typedef struct dlmgmt_prefix {
64 	struct dlmgmt_prefix	*lp_next;
65 	char			lp_prefix[MAXLINKNAMELEN];
66 	zoneid_t		lp_zoneid;
67 	uint_t			lp_nextppa;
68 } dlmgmt_prefix_t;
69 static dlmgmt_prefix_t	dlmgmt_prefixlist;
70 
71 datalink_id_t		dlmgmt_nextlinkid;
72 static datalink_id_t	dlmgmt_nextconfid = 1;
73 
74 static void		dlmgmt_advance_linkid(dlmgmt_link_t *);
75 static void		dlmgmt_advance_ppa(dlmgmt_link_t *);
76 
77 void
78 dlmgmt_log(int pri, const char *fmt, ...)
79 {
80 	va_list alist;
81 
82 	va_start(alist, fmt);
83 	if (debug) {
84 		(void) vfprintf(stderr, fmt, alist);
85 		(void) fputc('\n', stderr);
86 	} else {
87 		vsyslog(pri, fmt, alist);
88 	}
89 	va_end(alist);
90 }
91 
92 static int
93 cmp_link_by_name(const void *v1, const void *v2)
94 {
95 	const dlmgmt_link_t *link1 = v1;
96 	const dlmgmt_link_t *link2 = v2;
97 	int cmp;
98 
99 	cmp = strcmp(link1->ll_link, link2->ll_link);
100 	return ((cmp == 0) ? 0 : ((cmp < 0) ? -1 : 1));
101 }
102 
103 /*
104  * Note that the zoneid associated with a link is effectively part of its
105  * name.  This is essentially what results in having each zone have disjoint
106  * datalink namespaces.
107  */
108 static int
109 cmp_link_by_zname(const void *v1, const void *v2)
110 {
111 	const dlmgmt_link_t *link1 = v1;
112 	const dlmgmt_link_t *link2 = v2;
113 
114 	if (link1->ll_zoneid < link2->ll_zoneid)
115 		return (-1);
116 	if (link1->ll_zoneid > link2->ll_zoneid)
117 		return (1);
118 	return (cmp_link_by_name(link1, link2));
119 }
120 
121 static int
122 cmp_link_by_id(const void *v1, const void *v2)
123 {
124 	const dlmgmt_link_t *link1 = v1;
125 	const dlmgmt_link_t *link2 = v2;
126 
127 	if ((uint64_t)(link1->ll_linkid) == (uint64_t)(link2->ll_linkid))
128 		return (0);
129 	else if ((uint64_t)(link1->ll_linkid) < (uint64_t)(link2->ll_linkid))
130 		return (-1);
131 	else
132 		return (1);
133 }
134 
135 static int
136 cmp_dlconf_by_id(const void *v1, const void *v2)
137 {
138 	const dlmgmt_dlconf_t *dlconfp1 = v1;
139 	const dlmgmt_dlconf_t *dlconfp2 = v2;
140 
141 	if (dlconfp1->ld_id == dlconfp2->ld_id)
142 		return (0);
143 	else if (dlconfp1->ld_id < dlconfp2->ld_id)
144 		return (-1);
145 	else
146 		return (1);
147 }
148 
149 void
150 dlmgmt_linktable_init(void)
151 {
152 	/*
153 	 * Initialize the prefix list. First add the "net" prefix for the
154 	 * global zone to the list.
155 	 */
156 	dlmgmt_prefixlist.lp_next = NULL;
157 	dlmgmt_prefixlist.lp_zoneid = GLOBAL_ZONEID;
158 	dlmgmt_prefixlist.lp_nextppa = 0;
159 	(void) strlcpy(dlmgmt_prefixlist.lp_prefix, "net", MAXLINKNAMELEN);
160 
161 	avl_create(&dlmgmt_name_avl, cmp_link_by_zname, sizeof (dlmgmt_link_t),
162 	    offsetof(dlmgmt_link_t, ll_name_node));
163 	avl_create(&dlmgmt_id_avl, cmp_link_by_id, sizeof (dlmgmt_link_t),
164 	    offsetof(dlmgmt_link_t, ll_id_node));
165 	avl_create(&dlmgmt_loan_avl, cmp_link_by_name, sizeof (dlmgmt_link_t),
166 	    offsetof(dlmgmt_link_t, ll_loan_node));
167 	avl_create(&dlmgmt_dlconf_avl, cmp_dlconf_by_id,
168 	    sizeof (dlmgmt_dlconf_t), offsetof(dlmgmt_dlconf_t, ld_node));
169 	dlmgmt_nextlinkid = 1;
170 }
171 
172 void
173 dlmgmt_linktable_fini(void)
174 {
175 	dlmgmt_prefix_t *lpp, *next;
176 
177 	for (lpp = dlmgmt_prefixlist.lp_next; lpp != NULL; lpp = next) {
178 		next = lpp->lp_next;
179 		free(lpp);
180 	}
181 
182 	avl_destroy(&dlmgmt_dlconf_avl);
183 	avl_destroy(&dlmgmt_name_avl);
184 	avl_destroy(&dlmgmt_loan_avl);
185 	avl_destroy(&dlmgmt_id_avl);
186 }
187 
188 static void
189 linkattr_add(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp)
190 {
191 	if (*headp == NULL) {
192 		*headp = attrp;
193 	} else {
194 		(*headp)->lp_prev = attrp;
195 		attrp->lp_next = *headp;
196 		*headp = attrp;
197 	}
198 }
199 
200 static void
201 linkattr_rm(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp)
202 {
203 	dlmgmt_linkattr_t *next, *prev;
204 
205 	next = attrp->lp_next;
206 	prev = attrp->lp_prev;
207 	if (next != NULL)
208 		next->lp_prev = prev;
209 	if (prev != NULL)
210 		prev->lp_next = next;
211 	else
212 		*headp = next;
213 }
214 
215 dlmgmt_linkattr_t *
216 linkattr_find(dlmgmt_linkattr_t *headp, const char *attr)
217 {
218 	dlmgmt_linkattr_t *attrp;
219 
220 	for (attrp = headp; attrp != NULL; attrp = attrp->lp_next) {
221 		if (strcmp(attrp->lp_name, attr) == 0)
222 			break;
223 	}
224 	return (attrp);
225 }
226 
227 int
228 linkattr_set(dlmgmt_linkattr_t **headp, const char *attr, void *attrval,
229     size_t attrsz, dladm_datatype_t type)
230 {
231 	dlmgmt_linkattr_t	*attrp;
232 	void			*newval;
233 	boolean_t		new;
234 
235 	attrp = linkattr_find(*headp, attr);
236 	if (attrp != NULL) {
237 		/*
238 		 * It is already set.  If the value changed, update it.
239 		 */
240 		if (linkattr_equal(headp, attr, attrval, attrsz))
241 			return (0);
242 		new = B_FALSE;
243 	} else {
244 		/*
245 		 * It is not set yet, allocate the linkattr and prepend to the
246 		 * list.
247 		 */
248 		if ((attrp = calloc(1, sizeof (dlmgmt_linkattr_t))) == NULL)
249 			return (ENOMEM);
250 
251 		(void) strlcpy(attrp->lp_name, attr, MAXLINKATTRLEN);
252 		new = B_TRUE;
253 	}
254 	if ((newval = calloc(1, attrsz)) == NULL) {
255 		if (new)
256 			free(attrp);
257 		return (ENOMEM);
258 	}
259 
260 	if (!new)
261 		free(attrp->lp_val);
262 	attrp->lp_val = newval;
263 	bcopy(attrval, attrp->lp_val, attrsz);
264 	attrp->lp_sz = attrsz;
265 	attrp->lp_type = type;
266 	attrp->lp_linkprop = dladm_attr_is_linkprop(attr);
267 	if (new)
268 		linkattr_add(headp, attrp);
269 	return (0);
270 }
271 
272 void
273 linkattr_unset(dlmgmt_linkattr_t **headp, const char *attr)
274 {
275 	dlmgmt_linkattr_t *attrp;
276 
277 	if ((attrp = linkattr_find(*headp, attr)) != NULL) {
278 		linkattr_rm(headp, attrp);
279 		free(attrp->lp_val);
280 		free(attrp);
281 	}
282 }
283 
284 int
285 linkattr_get(dlmgmt_linkattr_t **headp, const char *attr, void **attrvalp,
286     size_t *attrszp, dladm_datatype_t *typep)
287 {
288 	dlmgmt_linkattr_t *attrp;
289 
290 	if ((attrp = linkattr_find(*headp, attr)) == NULL)
291 		return (ENOENT);
292 
293 	*attrvalp = attrp->lp_val;
294 	*attrszp = attrp->lp_sz;
295 	if (typep != NULL)
296 		*typep = attrp->lp_type;
297 	return (0);
298 }
299 
300 boolean_t
301 linkattr_equal(dlmgmt_linkattr_t **headp, const char *attr, void *attrval,
302     size_t attrsz)
303 {
304 	void	*saved_attrval;
305 	size_t	saved_attrsz;
306 
307 	if (linkattr_get(headp, attr, &saved_attrval, &saved_attrsz, NULL) != 0)
308 		return (B_FALSE);
309 
310 	return ((saved_attrsz == attrsz) &&
311 	    (memcmp(saved_attrval, attrval, attrsz) == 0));
312 }
313 
314 void
315 linkattr_destroy(dlmgmt_link_t *linkp)
316 {
317 	dlmgmt_linkattr_t *next, *attrp;
318 
319 	for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
320 		next = attrp->lp_next;
321 		free(attrp->lp_val);
322 		free(attrp);
323 	}
324 }
325 
326 static int
327 dlmgmt_table_readwritelock(boolean_t write)
328 {
329 	if (write)
330 		return (pthread_rwlock_trywrlock(&dlmgmt_avl_lock));
331 	else
332 		return (pthread_rwlock_tryrdlock(&dlmgmt_avl_lock));
333 }
334 
335 void
336 dlmgmt_table_lock(boolean_t write)
337 {
338 	(void) pthread_mutex_lock(&dlmgmt_avl_mutex);
339 	while (dlmgmt_table_readwritelock(write) == EBUSY)
340 		(void) pthread_cond_wait(&dlmgmt_avl_cv, &dlmgmt_avl_mutex);
341 
342 	(void) pthread_mutex_unlock(&dlmgmt_avl_mutex);
343 }
344 
345 void
346 dlmgmt_table_unlock(void)
347 {
348 	(void) pthread_rwlock_unlock(&dlmgmt_avl_lock);
349 	(void) pthread_mutex_lock(&dlmgmt_avl_mutex);
350 	(void) pthread_cond_broadcast(&dlmgmt_avl_cv);
351 	(void) pthread_mutex_unlock(&dlmgmt_avl_mutex);
352 }
353 
354 void
355 link_destroy(dlmgmt_link_t *linkp)
356 {
357 	linkattr_destroy(linkp);
358 	free(linkp);
359 }
360 
361 /*
362  * Set the DLMGMT_ACTIVE flag on the link to note that it is active.  When a
363  * link becomes active and it belongs to a non-global zone, it is also added
364  * to that zone.
365  */
366 int
367 link_activate(dlmgmt_link_t *linkp)
368 {
369 	int		err = 0;
370 	zoneid_t	zoneid = ALL_ZONES;
371 
372 	if (zone_check_datalink(&zoneid, linkp->ll_linkid) == 0) {
373 		/*
374 		 * This link was already added to a non-global zone.  This can
375 		 * happen if dlmgmtd is restarted.
376 		 */
377 		if (zoneid != linkp->ll_zoneid) {
378 			if (link_by_name(linkp->ll_link, zoneid) != NULL) {
379 				err = EEXIST;
380 				goto done;
381 			}
382 
383 			if (avl_find(&dlmgmt_name_avl, linkp, NULL) != NULL)
384 				avl_remove(&dlmgmt_name_avl, linkp);
385 
386 			linkp->ll_zoneid = zoneid;
387 			avl_add(&dlmgmt_name_avl, linkp);
388 			avl_add(&dlmgmt_loan_avl, linkp);
389 			linkp->ll_onloan = B_TRUE;
390 		}
391 	} else if (linkp->ll_zoneid != GLOBAL_ZONEID) {
392 		err = zone_add_datalink(linkp->ll_zoneid, linkp->ll_linkid);
393 	}
394 done:
395 	if (err == 0)
396 		linkp->ll_flags |= DLMGMT_ACTIVE;
397 	return (err);
398 }
399 
400 /*
401  * Is linkp visible from the caller's zoneid?  It is if the link is in the
402  * same zone as the caller, or if the caller is in the global zone and the
403  * link is on loan to a non-global zone.
404  */
405 boolean_t
406 link_is_visible(dlmgmt_link_t *linkp, zoneid_t zoneid)
407 {
408 	return (linkp->ll_zoneid == zoneid ||
409 	    (zoneid == GLOBAL_ZONEID && linkp->ll_onloan));
410 }
411 
412 dlmgmt_link_t *
413 link_by_id(datalink_id_t linkid, zoneid_t zoneid)
414 {
415 	dlmgmt_link_t link, *linkp;
416 
417 	link.ll_linkid = linkid;
418 	linkp = avl_find(&dlmgmt_id_avl, &link, NULL);
419 	if (zoneid != GLOBAL_ZONEID && linkp->ll_zoneid != zoneid)
420 		linkp = NULL;
421 	return (linkp);
422 }
423 
424 dlmgmt_link_t *
425 link_by_name(const char *name, zoneid_t zoneid)
426 {
427 	dlmgmt_link_t	link, *linkp;
428 
429 	(void) strlcpy(link.ll_link, name, MAXLINKNAMELEN);
430 	link.ll_zoneid = zoneid;
431 	linkp = avl_find(&dlmgmt_name_avl, &link, NULL);
432 	if (linkp == NULL && zoneid == GLOBAL_ZONEID) {
433 		/* The link could be on loan to a non-global zone? */
434 		linkp = avl_find(&dlmgmt_loan_avl, &link, NULL);
435 	}
436 	return (linkp);
437 }
438 
439 int
440 dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media,
441     zoneid_t zoneid, uint32_t flags, dlmgmt_link_t **linkpp)
442 {
443 	dlmgmt_link_t	*linkp = NULL;
444 	avl_index_t	name_where, id_where;
445 	int		err = 0;
446 
447 	if (!dladm_valid_linkname(name))
448 		return (EINVAL);
449 	if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID)
450 		return (ENOSPC);
451 
452 	if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) {
453 		err = ENOMEM;
454 		goto done;
455 	}
456 
457 	(void) strlcpy(linkp->ll_link, name, MAXLINKNAMELEN);
458 	linkp->ll_class = class;
459 	linkp->ll_media = media;
460 	linkp->ll_linkid = dlmgmt_nextlinkid;
461 	linkp->ll_zoneid = zoneid;
462 	linkp->ll_gen = 0;
463 
464 	if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL ||
465 	    avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) {
466 		err = EEXIST;
467 		goto done;
468 	}
469 
470 	avl_insert(&dlmgmt_name_avl, linkp, name_where);
471 	avl_insert(&dlmgmt_id_avl, linkp, id_where);
472 
473 	if ((flags & DLMGMT_ACTIVE) && (err = link_activate(linkp)) != 0) {
474 		avl_remove(&dlmgmt_name_avl, linkp);
475 		avl_remove(&dlmgmt_id_avl, linkp);
476 		goto done;
477 	}
478 
479 	linkp->ll_flags = flags;
480 	dlmgmt_advance(linkp);
481 	*linkpp = linkp;
482 
483 done:
484 	if (err != 0)
485 		free(linkp);
486 	return (err);
487 }
488 
489 int
490 dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags)
491 {
492 	if ((linkp->ll_flags & flags) == 0) {
493 		/*
494 		 * The link does not exist in the specified space.
495 		 */
496 		return (ENOENT);
497 	}
498 
499 	linkp->ll_flags &= ~flags;
500 	if (flags & DLMGMT_PERSIST) {
501 		dlmgmt_linkattr_t *next, *attrp;
502 
503 		for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
504 			next = attrp->lp_next;
505 			free(attrp->lp_val);
506 			free(attrp);
507 		}
508 		linkp->ll_head = NULL;
509 	}
510 
511 	if ((flags & DLMGMT_ACTIVE) && linkp->ll_zoneid != GLOBAL_ZONEID) {
512 		(void) zone_remove_datalink(linkp->ll_zoneid, linkp->ll_linkid);
513 		if (linkp->ll_onloan)
514 			avl_remove(&dlmgmt_loan_avl, linkp);
515 	}
516 
517 	if (linkp->ll_flags == 0) {
518 		avl_remove(&dlmgmt_id_avl, linkp);
519 		avl_remove(&dlmgmt_name_avl, linkp);
520 		link_destroy(linkp);
521 	}
522 
523 	return (0);
524 }
525 
526 int
527 dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr,
528     dlmgmt_getattr_retval_t *retvalp)
529 {
530 	int			err;
531 	void			*attrval;
532 	size_t			attrsz;
533 	dladm_datatype_t	attrtype;
534 
535 	err = linkattr_get(headp, attr, &attrval, &attrsz, &attrtype);
536 	if (err != 0)
537 		return (err);
538 
539 	assert(attrsz > 0);
540 	if (attrsz > MAXLINKATTRVALLEN)
541 		return (EINVAL);
542 
543 	retvalp->lr_type = attrtype;
544 	retvalp->lr_attrsz = attrsz;
545 	bcopy(attrval, retvalp->lr_attrval, attrsz);
546 	return (0);
547 }
548 
549 void
550 dlmgmt_dlconf_table_lock(boolean_t write)
551 {
552 	if (write)
553 		(void) pthread_rwlock_wrlock(&dlmgmt_dlconf_lock);
554 	else
555 		(void) pthread_rwlock_rdlock(&dlmgmt_dlconf_lock);
556 }
557 
558 void
559 dlmgmt_dlconf_table_unlock(void)
560 {
561 	(void) pthread_rwlock_unlock(&dlmgmt_dlconf_lock);
562 }
563 
564 int
565 dlconf_create(const char *name, datalink_id_t linkid, datalink_class_t class,
566     uint32_t media, zoneid_t zoneid, dlmgmt_dlconf_t **dlconfpp)
567 {
568 	dlmgmt_dlconf_t	*dlconfp = NULL;
569 	int		err = 0;
570 
571 	if (dlmgmt_nextconfid == 0) {
572 		err = ENOSPC;
573 		goto done;
574 	}
575 
576 	if ((dlconfp = calloc(1, sizeof (dlmgmt_dlconf_t))) == NULL) {
577 		err = ENOMEM;
578 		goto done;
579 	}
580 
581 	(void) strlcpy(dlconfp->ld_link, name, MAXLINKNAMELEN);
582 	dlconfp->ld_linkid = linkid;
583 	dlconfp->ld_class = class;
584 	dlconfp->ld_media = media;
585 	dlconfp->ld_id = dlmgmt_nextconfid;
586 	dlconfp->ld_zoneid = zoneid;
587 
588 done:
589 	*dlconfpp = dlconfp;
590 	return (err);
591 }
592 
593 void
594 dlconf_destroy(dlmgmt_dlconf_t *dlconfp)
595 {
596 	dlmgmt_linkattr_t *next, *attrp;
597 
598 	for (attrp = dlconfp->ld_head; attrp != NULL; attrp = next) {
599 		next = attrp->lp_next;
600 		free(attrp->lp_val);
601 		free(attrp);
602 	}
603 	free(dlconfp);
604 }
605 
606 int
607 dlmgmt_generate_name(const char *prefix, char *name, size_t size,
608     zoneid_t zoneid)
609 {
610 	dlmgmt_prefix_t	*lpp, *prev = NULL;
611 	dlmgmt_link_t	link, *linkp;
612 
613 	/*
614 	 * See whether the requested prefix is already in the list.
615 	 */
616 	for (lpp = &dlmgmt_prefixlist; lpp != NULL;
617 	    prev = lpp, lpp = lpp->lp_next) {
618 		if (lpp->lp_zoneid == zoneid &&
619 		    strcmp(prefix, lpp->lp_prefix) == 0)
620 			break;
621 	}
622 
623 	/*
624 	 * Not found.
625 	 */
626 	if (lpp == NULL) {
627 		assert(prev != NULL);
628 
629 		/*
630 		 * First add this new prefix into the prefix list.
631 		 */
632 		if ((lpp = malloc(sizeof (dlmgmt_prefix_t))) == NULL)
633 			return (ENOMEM);
634 
635 		prev->lp_next = lpp;
636 		lpp->lp_next = NULL;
637 		lpp->lp_zoneid = zoneid;
638 		lpp->lp_nextppa = 0;
639 		(void) strlcpy(lpp->lp_prefix, prefix, MAXLINKNAMELEN);
640 
641 		/*
642 		 * Now determine this prefix's nextppa.
643 		 */
644 		(void) snprintf(link.ll_link, MAXLINKNAMELEN, "%s%d",
645 		    prefix, 0);
646 		link.ll_zoneid = zoneid;
647 		if ((linkp = avl_find(&dlmgmt_name_avl, &link, NULL)) != NULL)
648 			dlmgmt_advance_ppa(linkp);
649 	}
650 
651 	if (lpp->lp_nextppa == (uint_t)-1)
652 		return (ENOSPC);
653 
654 	(void) snprintf(name, size, "%s%d", prefix, lpp->lp_nextppa);
655 	return (0);
656 }
657 
658 /*
659  * Advance the next available ppa value if the name prefix of the current
660  * link is in the prefix list.
661  */
662 static void
663 dlmgmt_advance_ppa(dlmgmt_link_t *linkp)
664 {
665 	dlmgmt_prefix_t	*lpp;
666 	char		prefix[MAXLINKNAMELEN];
667 	char		linkname[MAXLINKNAMELEN];
668 	uint_t		start, ppa;
669 
670 	(void) dlpi_parselink(linkp->ll_link, prefix, &ppa);
671 
672 	/*
673 	 * See whether the requested prefix is already in the list.
674 	 */
675 	for (lpp = &dlmgmt_prefixlist; lpp != NULL; lpp = lpp->lp_next) {
676 		if (lpp->lp_zoneid == linkp->ll_zoneid &&
677 		    strcmp(prefix, lpp->lp_prefix) == 0)
678 			break;
679 	}
680 
681 	/*
682 	 * If the link name prefix is in the list, advance the
683 	 * next available ppa for the <prefix>N name.
684 	 */
685 	if (lpp == NULL || lpp->lp_nextppa != ppa)
686 		return;
687 
688 	start = lpp->lp_nextppa++;
689 	linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
690 	while (lpp->lp_nextppa != start) {
691 		if (lpp->lp_nextppa == (uint_t)-1) {
692 			/*
693 			 * wrapped around. search from <prefix>1.
694 			 */
695 			lpp->lp_nextppa = 0;
696 			(void) snprintf(linkname, MAXLINKNAMELEN,
697 			    "%s%d", lpp->lp_prefix, lpp->lp_nextppa);
698 			linkp = link_by_name(linkname, lpp->lp_zoneid);
699 			if (linkp == NULL)
700 				return;
701 		} else {
702 			if (linkp == NULL)
703 				return;
704 			(void) dlpi_parselink(linkp->ll_link, prefix, &ppa);
705 			if ((strcmp(prefix, lpp->lp_prefix) != 0) ||
706 			    (ppa != lpp->lp_nextppa)) {
707 				return;
708 			}
709 		}
710 		linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
711 		lpp->lp_nextppa++;
712 	}
713 	lpp->lp_nextppa = (uint_t)-1;
714 }
715 
716 /*
717  * Advance to the next available linkid value.
718  */
719 static void
720 dlmgmt_advance_linkid(dlmgmt_link_t *linkp)
721 {
722 	datalink_id_t	start;
723 
724 	if (linkp->ll_linkid != dlmgmt_nextlinkid)
725 		return;
726 
727 	start = dlmgmt_nextlinkid;
728 	linkp = AVL_NEXT(&dlmgmt_id_avl, linkp);
729 
730 	do {
731 		if (dlmgmt_nextlinkid == DATALINK_MAX_LINKID) {
732 			/*
733 			 * wrapped around. search from 1.
734 			 */
735 			dlmgmt_nextlinkid = 1;
736 			if ((linkp = link_by_id(1, GLOBAL_ZONEID)) == NULL)
737 				return;
738 		} else {
739 			dlmgmt_nextlinkid++;
740 			if (linkp == NULL)
741 				return;
742 			if (linkp->ll_linkid != dlmgmt_nextlinkid)
743 				return;
744 		}
745 
746 		linkp = AVL_NEXT(&dlmgmt_id_avl, linkp);
747 	} while (dlmgmt_nextlinkid != start);
748 
749 	dlmgmt_nextlinkid = DATALINK_INVALID_LINKID;
750 }
751 
752 /*
753  * Advance various global values, for example, next linkid value, next ppa for
754  * various prefix etc.
755  */
756 void
757 dlmgmt_advance(dlmgmt_link_t *linkp)
758 {
759 	dlmgmt_advance_linkid(linkp);
760 	dlmgmt_advance_ppa(linkp);
761 }
762 
763 /*
764  * Advance to the next available dlconf id.
765  */
766 void
767 dlmgmt_advance_dlconfid(dlmgmt_dlconf_t *dlconfp)
768 {
769 	uint_t	start;
770 
771 	start = dlmgmt_nextconfid++;
772 	dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp);
773 	while (dlmgmt_nextconfid != start) {
774 		if (dlmgmt_nextconfid == 0) {
775 			dlmgmt_dlconf_t	dlconf;
776 
777 			/*
778 			 * wrapped around. search from 1.
779 			 */
780 			dlconf.ld_id = dlmgmt_nextconfid = 1;
781 			dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
782 			if (dlconfp == NULL)
783 				return;
784 		} else {
785 			if ((dlconfp == NULL) ||
786 			    (dlconfp->ld_id != dlmgmt_nextconfid)) {
787 				return;
788 			}
789 		}
790 		dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp);
791 		dlmgmt_nextconfid++;
792 	}
793 	dlmgmt_nextconfid = 0;
794 }
795