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