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