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