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