Home | History | Annotate | Download | only in dlmgmtd
      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 2009 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 <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 }
    281 
    282 int
    283 linkattr_get(dlmgmt_linkattr_t **headp, const char *attr, void **attrvalp,
    284     size_t *attrszp, dladm_datatype_t *typep)
    285 {
    286 	dlmgmt_linkattr_t *attrp;
    287 
    288 	if ((attrp = linkattr_find(*headp, attr)) == NULL)
    289 		return (ENOENT);
    290 
    291 	*attrvalp = attrp->lp_val;
    292 	*attrszp = attrp->lp_sz;
    293 	if (typep != NULL)
    294 		*typep = attrp->lp_type;
    295 	return (0);
    296 }
    297 
    298 int
    299 linkprop_getnext(dlmgmt_linkattr_t **headp, const char *lastattr,
    300     char **attrnamep, void **attrvalp, size_t *attrszp, dladm_datatype_t *typep)
    301 {
    302 	dlmgmt_linkattr_t	*attrp;
    303 
    304 	/* skip to entry following lastattr or pick first if none specified */
    305 	for (attrp = *headp; attrp != NULL; attrp = attrp->lp_next) {
    306 		if (!attrp->lp_linkprop)
    307 			continue;
    308 		if (lastattr[0] == '\0')
    309 			break;
    310 		if (strcmp(attrp->lp_name, lastattr) == 0) {
    311 			attrp = attrp->lp_next;
    312 			break;
    313 		}
    314 	}
    315 	if (attrp == NULL)
    316 		return (ENOENT);
    317 
    318 	*attrnamep = attrp->lp_name;
    319 	*attrvalp = attrp->lp_val;
    320 	*attrszp = attrp->lp_sz;
    321 	*typep = attrp->lp_type;
    322 	return (0);
    323 }
    324 
    325 boolean_t
    326 linkattr_equal(dlmgmt_linkattr_t **headp, const char *attr, void *attrval,
    327     size_t attrsz)
    328 {
    329 	void	*saved_attrval;
    330 	size_t	saved_attrsz;
    331 
    332 	if (linkattr_get(headp, attr, &saved_attrval, &saved_attrsz, NULL) != 0)
    333 		return (B_FALSE);
    334 
    335 	return ((saved_attrsz == attrsz) &&
    336 	    (memcmp(saved_attrval, attrval, attrsz) == 0));
    337 }
    338 
    339 static int
    340 dlmgmt_table_readwritelock(boolean_t write)
    341 {
    342 	if (write)
    343 		return (pthread_rwlock_trywrlock(&dlmgmt_avl_lock));
    344 	else
    345 		return (pthread_rwlock_tryrdlock(&dlmgmt_avl_lock));
    346 }
    347 
    348 void
    349 dlmgmt_table_lock(boolean_t write)
    350 {
    351 	(void) pthread_mutex_lock(&dlmgmt_avl_mutex);
    352 	while (dlmgmt_table_readwritelock(write) == EBUSY)
    353 		(void) pthread_cond_wait(&dlmgmt_avl_cv, &dlmgmt_avl_mutex);
    354 
    355 	(void) pthread_mutex_unlock(&dlmgmt_avl_mutex);
    356 }
    357 
    358 void
    359 dlmgmt_table_unlock(void)
    360 {
    361 	(void) pthread_rwlock_unlock(&dlmgmt_avl_lock);
    362 	(void) pthread_mutex_lock(&dlmgmt_avl_mutex);
    363 	(void) pthread_cond_broadcast(&dlmgmt_avl_cv);
    364 	(void) pthread_mutex_unlock(&dlmgmt_avl_mutex);
    365 }
    366 
    367 void
    368 link_destroy(dlmgmt_link_t *linkp)
    369 {
    370 	dlmgmt_linkattr_t *next, *attrp;
    371 
    372 	for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
    373 		next = attrp->lp_next;
    374 		free(attrp->lp_val);
    375 		free(attrp);
    376 	}
    377 	free(linkp);
    378 }
    379 
    380 /*
    381  * Set the DLMGMT_ACTIVE flag on the link to note that it is active.  When a
    382  * link becomes active and it belongs to a non-global zone, it is also added
    383  * to that zone.
    384  */
    385 int
    386 link_activate(dlmgmt_link_t *linkp)
    387 {
    388 	int		err = 0;
    389 	zoneid_t	zoneid;
    390 
    391 	if (zone_check_datalink(&zoneid, linkp->ll_linkid) == 0) {
    392 		/*
    393 		 * This link was already added to a non-global zone.  This can
    394 		 * happen if dlmgmtd is restarted.
    395 		 */
    396 		if (zoneid != linkp->ll_zoneid) {
    397 			if (link_by_name(linkp->ll_link, zoneid) != NULL) {
    398 				err = EEXIST;
    399 				goto done;
    400 			}
    401 			avl_remove(&dlmgmt_name_avl, linkp);
    402 			linkp->ll_zoneid = zoneid;
    403 			avl_add(&dlmgmt_name_avl, linkp);
    404 			avl_add(&dlmgmt_loan_avl, linkp);
    405 			linkp->ll_onloan = B_TRUE;
    406 		}
    407 	} else if (linkp->ll_zoneid != GLOBAL_ZONEID) {
    408 		err = zone_add_datalink(linkp->ll_zoneid, linkp->ll_linkid);
    409 	}
    410 done:
    411 	if (err == 0)
    412 		linkp->ll_flags |= DLMGMT_ACTIVE;
    413 	return (err);
    414 }
    415 
    416 /*
    417  * Is linkp visible from the caller's zoneid?  It is if the link is in the
    418  * same zone as the caller, or if the caller is in the global zone and the
    419  * link is on loan to a non-global zone.
    420  */
    421 boolean_t
    422 link_is_visible(dlmgmt_link_t *linkp, zoneid_t zoneid)
    423 {
    424 	return (linkp->ll_zoneid == zoneid ||
    425 	    (zoneid == GLOBAL_ZONEID && linkp->ll_onloan));
    426 }
    427 
    428 dlmgmt_link_t *
    429 link_by_id(datalink_id_t linkid, zoneid_t zoneid)
    430 {
    431 	dlmgmt_link_t link, *linkp;
    432 
    433 	link.ll_linkid = linkid;
    434 	linkp = avl_find(&dlmgmt_id_avl, &link, NULL);
    435 	if (zoneid != GLOBAL_ZONEID && linkp->ll_zoneid != zoneid)
    436 		linkp = NULL;
    437 	return (linkp);
    438 }
    439 
    440 dlmgmt_link_t *
    441 link_by_name(const char *name, zoneid_t zoneid)
    442 {
    443 	dlmgmt_link_t	link, *linkp;
    444 
    445 	(void) strlcpy(link.ll_link, name, MAXLINKNAMELEN);
    446 	link.ll_zoneid = zoneid;
    447 	linkp = avl_find(&dlmgmt_name_avl, &link, NULL);
    448 	if (linkp == NULL && zoneid == GLOBAL_ZONEID) {
    449 		/* The link could be on loan to a non-global zone? */
    450 		linkp = avl_find(&dlmgmt_loan_avl, &link, NULL);
    451 	}
    452 	return (linkp);
    453 }
    454 
    455 int
    456 dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media,
    457     zoneid_t zoneid, uint32_t flags, dlmgmt_link_t **linkpp)
    458 {
    459 	dlmgmt_link_t	*linkp = NULL;
    460 	avl_index_t	name_where, id_where;
    461 	int		err = 0;
    462 
    463 	if (!dladm_valid_linkname(name))
    464 		return (EINVAL);
    465 	if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID)
    466 		return (ENOSPC);
    467 
    468 	if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) {
    469 		err = ENOMEM;
    470 		goto done;
    471 	}
    472 
    473 	(void) strlcpy(linkp->ll_link, name, MAXLINKNAMELEN);
    474 	linkp->ll_class = class;
    475 	linkp->ll_media = media;
    476 	linkp->ll_linkid = dlmgmt_nextlinkid;
    477 	linkp->ll_zoneid = zoneid;
    478 	linkp->ll_gen = 0;
    479 
    480 	if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL ||
    481 	    avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) {
    482 		err = EEXIST;
    483 		goto done;
    484 	}
    485 
    486 	avl_insert(&dlmgmt_name_avl, linkp, name_where);
    487 	avl_insert(&dlmgmt_id_avl, linkp, id_where);
    488 
    489 	if ((flags & DLMGMT_ACTIVE) && (err = link_activate(linkp)) != 0) {
    490 		avl_remove(&dlmgmt_name_avl, linkp);
    491 		avl_remove(&dlmgmt_id_avl, linkp);
    492 		goto done;
    493 	}
    494 
    495 	linkp->ll_flags = flags;
    496 	dlmgmt_advance(linkp);
    497 	*linkpp = linkp;
    498 
    499 done:
    500 	if (err != 0)
    501 		free(linkp);
    502 	return (err);
    503 }
    504 
    505 int
    506 dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags)
    507 {
    508 	if ((linkp->ll_flags & flags) == 0) {
    509 		/*
    510 		 * The link does not exist in the specified space.
    511 		 */
    512 		return (ENOENT);
    513 	}
    514 
    515 	linkp->ll_flags &= ~flags;
    516 	if (flags & DLMGMT_PERSIST) {
    517 		dlmgmt_linkattr_t *next, *attrp;
    518 
    519 		for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
    520 			next = attrp->lp_next;
    521 			free(attrp->lp_val);
    522 			free(attrp);
    523 		}
    524 		linkp->ll_head = NULL;
    525 	}
    526 
    527 	if ((flags & DLMGMT_ACTIVE) && linkp->ll_zoneid != GLOBAL_ZONEID) {
    528 		(void) zone_remove_datalink(linkp->ll_zoneid, linkp->ll_linkid);
    529 		if (linkp->ll_onloan)
    530 			avl_remove(&dlmgmt_loan_avl, linkp);
    531 	}
    532 
    533 	if (linkp->ll_flags == 0) {
    534 		avl_remove(&dlmgmt_id_avl, linkp);
    535 		avl_remove(&dlmgmt_name_avl, linkp);
    536 		link_destroy(linkp);
    537 	}
    538 
    539 	return (0);
    540 }
    541 
    542 int
    543 dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr,
    544     dlmgmt_getattr_retval_t *retvalp)
    545 {
    546 	int			err;
    547 	void			*attrval;
    548 	size_t			attrsz;
    549 	dladm_datatype_t	attrtype;
    550 
    551 	err = linkattr_get(headp, attr, &attrval, &attrsz, &attrtype);
    552 	if (err != 0)
    553 		return (err);
    554 
    555 	assert(attrsz > 0);
    556 	if (attrsz > MAXLINKATTRVALLEN)
    557 		return (EINVAL);
    558 
    559 	retvalp->lr_type = attrtype;
    560 	retvalp->lr_attrsz = attrsz;
    561 	bcopy(attrval, retvalp->lr_attrval, attrsz);
    562 	return (0);
    563 }
    564 
    565 void
    566 dlmgmt_dlconf_table_lock(boolean_t write)
    567 {
    568 	if (write)
    569 		(void) pthread_rwlock_wrlock(&dlmgmt_dlconf_lock);
    570 	else
    571 		(void) pthread_rwlock_rdlock(&dlmgmt_dlconf_lock);
    572 }
    573 
    574 void
    575 dlmgmt_dlconf_table_unlock(void)
    576 {
    577 	(void) pthread_rwlock_unlock(&dlmgmt_dlconf_lock);
    578 }
    579 
    580 int
    581 dlconf_create(const char *name, datalink_id_t linkid, datalink_class_t class,
    582     uint32_t media, zoneid_t zoneid, dlmgmt_dlconf_t **dlconfpp)
    583 {
    584 	dlmgmt_dlconf_t	*dlconfp = NULL;
    585 	int		err = 0;
    586 
    587 	if (dlmgmt_nextconfid == 0) {
    588 		err = ENOSPC;
    589 		goto done;
    590 	}
    591 
    592 	if ((dlconfp = calloc(1, sizeof (dlmgmt_dlconf_t))) == NULL) {
    593 		err = ENOMEM;
    594 		goto done;
    595 	}
    596 
    597 	(void) strlcpy(dlconfp->ld_link, name, MAXLINKNAMELEN);
    598 	dlconfp->ld_linkid = linkid;
    599 	dlconfp->ld_class = class;
    600 	dlconfp->ld_media = media;
    601 	dlconfp->ld_id = dlmgmt_nextconfid;
    602 	dlconfp->ld_zoneid = zoneid;
    603 
    604 done:
    605 	*dlconfpp = dlconfp;
    606 	return (err);
    607 }
    608 
    609 void
    610 dlconf_destroy(dlmgmt_dlconf_t *dlconfp)
    611 {
    612 	dlmgmt_linkattr_t *next, *attrp;
    613 
    614 	for (attrp = dlconfp->ld_head; attrp != NULL; attrp = next) {
    615 		next = attrp->lp_next;
    616 		free(attrp->lp_val);
    617 		free(attrp);
    618 	}
    619 	free(dlconfp);
    620 }
    621 
    622 int
    623 dlmgmt_generate_name(const char *prefix, char *name, size_t size,
    624     zoneid_t zoneid)
    625 {
    626 	dlmgmt_prefix_t	*lpp, *prev = NULL;
    627 	dlmgmt_link_t	link, *linkp;
    628 
    629 	/*
    630 	 * See whether the requested prefix is already in the list.
    631 	 */
    632 	for (lpp = &dlmgmt_prefixlist; lpp != NULL;
    633 	    prev = lpp, lpp = lpp->lp_next) {
    634 		if (lpp->lp_zoneid == zoneid &&
    635 		    strcmp(prefix, lpp->lp_prefix) == 0)
    636 			break;
    637 	}
    638 
    639 	/*
    640 	 * Not found.
    641 	 */
    642 	if (lpp == NULL) {
    643 		assert(prev != NULL);
    644 
    645 		/*
    646 		 * First add this new prefix into the prefix list.
    647 		 */
    648 		if ((lpp = malloc(sizeof (dlmgmt_prefix_t))) == NULL)
    649 			return (ENOMEM);
    650 
    651 		prev->lp_next = lpp;
    652 		lpp->lp_next = NULL;
    653 		lpp->lp_zoneid = zoneid;
    654 		lpp->lp_nextppa = 0;
    655 		(void) strlcpy(lpp->lp_prefix, prefix, MAXLINKNAMELEN);
    656 
    657 		/*
    658 		 * Now determine this prefix's nextppa.
    659 		 */
    660 		(void) snprintf(link.ll_link, MAXLINKNAMELEN, "%s%d",
    661 		    prefix, 0);
    662 		link.ll_zoneid = zoneid;
    663 		if ((linkp = avl_find(&dlmgmt_name_avl, &link, NULL)) != NULL)
    664 			dlmgmt_advance_ppa(linkp);
    665 	}
    666 
    667 	if (lpp->lp_nextppa == (uint_t)-1)
    668 		return (ENOSPC);
    669 
    670 	(void) snprintf(name, size, "%s%d", prefix, lpp->lp_nextppa);
    671 	return (0);
    672 }
    673 
    674 /*
    675  * Advance the next available ppa value if the name prefix of the current
    676  * link is in the prefix list.
    677  */
    678 static void
    679 dlmgmt_advance_ppa(dlmgmt_link_t *linkp)
    680 {
    681 	dlmgmt_prefix_t	*lpp;
    682 	char		prefix[MAXLINKNAMELEN];
    683 	char		linkname[MAXLINKNAMELEN];
    684 	uint_t		start, ppa;
    685 
    686 	(void) dlpi_parselink(linkp->ll_link, prefix, &ppa);
    687 
    688 	/*
    689 	 * See whether the requested prefix is already in the list.
    690 	 */
    691 	for (lpp = &dlmgmt_prefixlist; lpp != NULL; lpp = lpp->lp_next) {
    692 		if (lpp->lp_zoneid == linkp->ll_zoneid &&
    693 		    strcmp(prefix, lpp->lp_prefix) == 0)
    694 			break;
    695 	}
    696 
    697 	/*
    698 	 * If the link name prefix is in the list, advance the
    699 	 * next available ppa for the <prefix>N name.
    700 	 */
    701 	if (lpp == NULL || lpp->lp_nextppa != ppa)
    702 		return;
    703 
    704 	start = lpp->lp_nextppa++;
    705 	linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
    706 	while (lpp->lp_nextppa != start) {
    707 		if (lpp->lp_nextppa == (uint_t)-1) {
    708 			/*
    709 			 * wrapped around. search from <prefix>1.
    710 			 */
    711 			lpp->lp_nextppa = 0;
    712 			(void) snprintf(linkname, MAXLINKNAMELEN,
    713 			    "%s%d", lpp->lp_prefix, lpp->lp_nextppa);
    714 			linkp = link_by_name(linkname, lpp->lp_zoneid);
    715 			if (linkp == NULL)
    716 				return;
    717 		} else {
    718 			if (linkp == NULL)
    719 				return;
    720 			(void) dlpi_parselink(linkp->ll_link, prefix, &ppa);
    721 			if ((strcmp(prefix, lpp->lp_prefix) != 0) ||
    722 			    (ppa != lpp->lp_nextppa)) {
    723 				return;
    724 			}
    725 		}
    726 		linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
    727 		lpp->lp_nextppa++;
    728 	}
    729 	lpp->lp_nextppa = (uint_t)-1;
    730 }
    731 
    732 /*
    733  * Advance to the next available linkid value.
    734  */
    735 static void
    736 dlmgmt_advance_linkid(dlmgmt_link_t *linkp)
    737 {
    738 	datalink_id_t	start;
    739 
    740 	if (linkp->ll_linkid != dlmgmt_nextlinkid)
    741 		return;
    742 
    743 	start = dlmgmt_nextlinkid;
    744 	linkp = AVL_NEXT(&dlmgmt_id_avl, linkp);
    745 
    746 	do {
    747 		if (dlmgmt_nextlinkid == DATALINK_MAX_LINKID) {
    748 			/*
    749 			 * wrapped around. search from 1.
    750 			 */
    751 			dlmgmt_nextlinkid = 1;
    752 			if ((linkp = link_by_id(1, GLOBAL_ZONEID)) == NULL)
    753 				return;
    754 		} else {
    755 			dlmgmt_nextlinkid++;
    756 			if (linkp == NULL)
    757 				return;
    758 			if (linkp->ll_linkid != dlmgmt_nextlinkid)
    759 				return;
    760 		}
    761 
    762 		linkp = AVL_NEXT(&dlmgmt_id_avl, linkp);
    763 	} while (dlmgmt_nextlinkid != start);
    764 
    765 	dlmgmt_nextlinkid = DATALINK_INVALID_LINKID;
    766 }
    767 
    768 /*
    769  * Advance various global values, for example, next linkid value, next ppa for
    770  * various prefix etc.
    771  */
    772 void
    773 dlmgmt_advance(dlmgmt_link_t *linkp)
    774 {
    775 	dlmgmt_advance_linkid(linkp);
    776 	dlmgmt_advance_ppa(linkp);
    777 }
    778 
    779 /*
    780  * Advance to the next available dlconf id.
    781  */
    782 void
    783 dlmgmt_advance_dlconfid(dlmgmt_dlconf_t *dlconfp)
    784 {
    785 	uint_t	start;
    786 
    787 	start = dlmgmt_nextconfid++;
    788 	dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp);
    789 	while (dlmgmt_nextconfid != start) {
    790 		if (dlmgmt_nextconfid == 0) {
    791 			dlmgmt_dlconf_t	dlconf;
    792 
    793 			/*
    794 			 * wrapped around. search from 1.
    795 			 */
    796 			dlconf.ld_id = dlmgmt_nextconfid = 1;
    797 			dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
    798 			if (dlconfp == NULL)
    799 				return;
    800 		} else {
    801 			if ((dlconfp == NULL) ||
    802 			    (dlconfp->ld_id != dlmgmt_nextconfid)) {
    803 				return;
    804 			}
    805 		}
    806 		dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp);
    807 		dlmgmt_nextconfid++;
    808 	}
    809 	dlmgmt_nextconfid = 0;
    810 }
    811