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.
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.
358 * When a link is active and owned by an NGZ then it is added to
359 * that zone's datalink list.
360 */
361 int
362 link_activate(dlmgmt_link_t *linkp)
363 {
364 int err = 0;
365 zoneid_t zoneid = ALL_ZONES;
366
367 /*
368 * If zone_check_datalink() returns 0 it means we found the
369 * link in one of the NGZ's datalink lists. Otherwise the link
370 * is under the GZ.
371 */
372 if (zone_check_datalink(&zoneid, linkp->ll_linkid) == 0) {
373 /*
374 * This is a bit subtle. If the following expression
375 * is true then the link was found in one of the NGZ's
376 * datalink lists but the link structure has it under
377 * the GZ. This means that the link is supposed to be
378 * loaned out to an NGZ but the dlmgmtd state is out
379 * of sync -- possibly due to the process restarting.
380 * In this case we need to sync the dlmgmtd state by
381 * marking it as on-loan to the NGZ it's currently
382 * under.
383 */
384 if (zoneid != linkp->ll_zoneid) {
385 assert(linkp->ll_zoneid == 0);
386 assert(linkp->ll_onloan == B_FALSE);
387 assert(linkp->ll_transient == 0);
388
389 /*
390 * If dlmgmtd already has a link with this
391 * name under the NGZ then we have a problem.
392 */
393 if (link_by_name(linkp->ll_link, zoneid) != NULL) {
394 err = EEXIST;
395 goto done;
396 }
397
398 /*
399 * Remove the current linkp entry from the
400 * list because it's under the wrong zoneid.
401 * We don't have to update the dlmgmt_id_avl
402 * because it compares entries by ll_linkid
403 * only.
404 */
405 if (avl_find(&dlmgmt_name_avl, linkp, NULL) != NULL)
406 avl_remove(&dlmgmt_name_avl, linkp);
407
408 /*
409 * Update the link to reflect the fact that
410 * it's on-loan to an NGZ and re-add it to the
411 * list.
412 */
413 linkp->ll_zoneid = zoneid;
414 avl_add(&dlmgmt_name_avl, linkp);
415 linkp->ll_onloan = B_TRUE;
416
417 /*
418 * When a VNIC is not persistent and loaned to
419 * a zone it is considered transient. This is
420 * the same logic found in do_create_vnic()
421 * and is needed here in the event of a
422 * dlmgmtd restart.
423 */
424 if (linkp->ll_class == DATALINK_CLASS_VNIC &&
425 !(linkp->ll_flags & DLMGMT_PERSIST))
426 linkp->ll_transient = B_TRUE;
427 }
428 } else if (linkp->ll_zoneid != GLOBAL_ZONEID) {
429 /*
430 * In this case the link was not found under any NGZ
431 * but according to its ll_zoneid member it is owned
432 * by an NGZ. Add the datalink to the appropriate zone
433 * datalink list.
434 */
435 err = zone_add_datalink(linkp->ll_zoneid, linkp->ll_linkid);
436 assert(linkp->ll_onloan == B_FALSE);
437 }
438 done:
439 if (err == 0)
440 linkp->ll_flags |= DLMGMT_ACTIVE;
441 return (err);
442 }
443
444 /*
445 * Is linkp visible from the caller's zoneid? It is if the link is in the
446 * same zone as the caller, or if the caller is in the global zone and the
447 * link is on loan to a non-global zone.
448 */
449 boolean_t
450 link_is_visible(dlmgmt_link_t *linkp, zoneid_t zoneid)
451 {
452 return (linkp->ll_zoneid == zoneid ||
453 (zoneid == GLOBAL_ZONEID && linkp->ll_onloan));
454 }
455
456 dlmgmt_link_t *
457 link_by_id(datalink_id_t linkid, zoneid_t zoneid)
458 {
459 dlmgmt_link_t link, *linkp;
460
461 link.ll_linkid = linkid;
462 if ((linkp = avl_find(&dlmgmt_id_avl, &link, NULL)) == NULL)
463 return (NULL);
464 if (zoneid != GLOBAL_ZONEID && linkp->ll_zoneid != zoneid)
465 return (NULL);
466 return (linkp);
467 }
468
469 dlmgmt_link_t *
470 link_by_name(const char *name, zoneid_t zoneid)
471 {
472 dlmgmt_link_t link, *linkp;
473
474 (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN);
475 link.ll_zoneid = zoneid;
476 linkp = avl_find(&dlmgmt_name_avl, &link, NULL);
477 return (linkp);
478 }
479
480 int
481 dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media,
482 zoneid_t zoneid, uint32_t flags, dlmgmt_link_t **linkpp)
483 {
484 dlmgmt_link_t *linkp = NULL;
485 avl_index_t name_where, id_where;
486 int err = 0;
487
488 if (!dladm_valid_linkname(name))
489 return (EINVAL);
490 if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID)
491 return (ENOSPC);
492 if (flags & ~(DLMGMT_ACTIVE | DLMGMT_PERSIST | DLMGMT_TRANSIENT) ||
493 ((flags & DLMGMT_PERSIST) && (flags & DLMGMT_TRANSIENT)) ||
494 flags == 0) {
495 return (EINVAL);
496 }
497
498 if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) {
499 err = ENOMEM;
500 goto done;
501 }
502
503 (void) strlcpy(linkp->ll_link, name, MAXLINKNAMELEN);
504 linkp->ll_class = class;
505 linkp->ll_media = media;
506 linkp->ll_linkid = dlmgmt_nextlinkid;
507 linkp->ll_zoneid = zoneid;
508 linkp->ll_gen = 0;
509
510 /*
511 * While DLMGMT_TRANSIENT starts off as a flag it is converted
512 * into a link field since it is really a substate of
513 * DLMGMT_ACTIVE -- it should not survive as a flag beyond
514 * this point.
515 */
516 linkp->ll_transient = (flags & DLMGMT_TRANSIENT) ? B_TRUE : B_FALSE;
517 flags &= ~DLMGMT_TRANSIENT;
518
519 if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL ||
520 avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) {
521 err = EEXIST;
522 goto done;
523 }
524
525 avl_insert(&dlmgmt_name_avl, linkp, name_where);
526 avl_insert(&dlmgmt_id_avl, linkp, id_where);
527
528 if ((flags & DLMGMT_ACTIVE) && (err = link_activate(linkp)) != 0) {
529 avl_remove(&dlmgmt_name_avl, linkp);
530 avl_remove(&dlmgmt_id_avl, linkp);
531 goto done;
532 }
533
534 linkp->ll_flags = flags;
535 dlmgmt_advance(linkp);
536 *linkpp = linkp;
537
538 done:
539 if (err != 0)
540 free(linkp);
541 return (err);
542 }
543
544 int
545 dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags)
546 {
547 /*
548 * After dlmgmt_create_common() the link flags should only
549 * ever include ACTIVE or PERSIST.
550 */
551 assert((linkp->ll_flags & ~(DLMGMT_ACTIVE | DLMGMT_PERSIST)) == 0);
552
553 if ((linkp->ll_flags & flags) == 0) {
554 /*
555 * The link does not exist in the specified space.
556 */
557 return (ENOENT);
558 }
559
560 linkp->ll_flags &= ~flags;
561 if (flags & DLMGMT_PERSIST) {
562 dlmgmt_linkattr_t *next, *attrp;
563
564 for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
565 next = attrp->lp_next;
566 free(attrp->lp_val);
567 free(attrp);
568 }
569 linkp->ll_head = NULL;
570 }
571
572 if ((flags & DLMGMT_ACTIVE) && linkp->ll_zoneid != GLOBAL_ZONEID) {
573 (void) zone_remove_datalink(linkp->ll_zoneid, linkp->ll_linkid);
574 }
575
576 if (linkp->ll_flags == 0) {
577 avl_remove(&dlmgmt_id_avl, linkp);
578 avl_remove(&dlmgmt_name_avl, linkp);
579 link_destroy(linkp);
580 }
581
582 return (0);
583 }
584
585 int
586 dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr,
587 dlmgmt_getattr_retval_t *retvalp)
588 {
589 int err;
590 void *attrval;
591 size_t attrsz;
592 dladm_datatype_t attrtype;
593
594 err = linkattr_get(headp, attr, &attrval, &attrsz, &attrtype);
595 if (err != 0)
596 return (err);
597
598 assert(attrsz > 0);
599 if (attrsz > MAXLINKATTRVALLEN)
600 return (EINVAL);
601
602 retvalp->lr_type = attrtype;
603 retvalp->lr_attrsz = attrsz;
604 bcopy(attrval, retvalp->lr_attrval, attrsz);
605 return (0);
606 }
607
608 void
609 dlmgmt_dlconf_table_lock(boolean_t write)
610 {
611 if (write)
612 (void) pthread_rwlock_wrlock(&dlmgmt_dlconf_lock);
613 else
614 (void) pthread_rwlock_rdlock(&dlmgmt_dlconf_lock);
615 }
616
617 void
618 dlmgmt_dlconf_table_unlock(void)
619 {
620 (void) pthread_rwlock_unlock(&dlmgmt_dlconf_lock);
621 }
622
623 int
624 dlconf_create(const char *name, datalink_id_t linkid, datalink_class_t class,
625 uint32_t media, zoneid_t zoneid, dlmgmt_dlconf_t **dlconfpp)
626 {
627 dlmgmt_dlconf_t *dlconfp = NULL;
628 int err = 0;
629
630 if (dlmgmt_nextconfid == 0) {
631 err = ENOSPC;
632 goto done;
633 }
634
635 if ((dlconfp = calloc(1, sizeof (dlmgmt_dlconf_t))) == NULL) {
636 err = ENOMEM;
637 goto done;
638 }
639
640 (void) strlcpy(dlconfp->ld_link, name, MAXLINKNAMELEN);
641 dlconfp->ld_linkid = linkid;
642 dlconfp->ld_class = class;
643 dlconfp->ld_media = media;
644 dlconfp->ld_id = dlmgmt_nextconfid;
645 dlconfp->ld_zoneid = zoneid;
646
647 done:
648 *dlconfpp = dlconfp;
649 return (err);
650 }
651
652 void
653 dlconf_destroy(dlmgmt_dlconf_t *dlconfp)
654 {
655 dlmgmt_linkattr_t *next, *attrp;
656
657 for (attrp = dlconfp->ld_head; attrp != NULL; attrp = next) {
658 next = attrp->lp_next;
659 free(attrp->lp_val);
660 free(attrp);
661 }
662 free(dlconfp);
663 }
664
665 int
666 dlmgmt_generate_name(const char *prefix, char *name, size_t size,
667 zoneid_t zoneid)
668 {
669 dlmgmt_prefix_t *lpp, *prev = NULL;
670 dlmgmt_link_t link, *linkp;
671
672 /*
673 * See whether the requested prefix is already in the list.
674 */
675 for (lpp = &dlmgmt_prefixlist; lpp != NULL;
676 prev = lpp, lpp = lpp->lp_next) {
677 if (lpp->lp_zoneid == zoneid &&
678 strcmp(prefix, lpp->lp_prefix) == 0)
679 break;
680 }
681
682 /*
683 * Not found.
684 */
685 if (lpp == NULL) {
686 assert(prev != NULL);
687
688 /*
689 * First add this new prefix into the prefix list.
690 */
691 if ((lpp = malloc(sizeof (dlmgmt_prefix_t))) == NULL)
692 return (ENOMEM);
693
694 prev->lp_next = lpp;
695 lpp->lp_next = NULL;
696 lpp->lp_zoneid = zoneid;
697 lpp->lp_nextppa = 0;
698 (void) strlcpy(lpp->lp_prefix, prefix, MAXLINKNAMELEN);
699
700 /*
701 * Now determine this prefix's nextppa.
702 */
703 (void) snprintf(link.ll_link, MAXLINKNAMELEN, "%s%d",
704 prefix, 0);
705 link.ll_zoneid = zoneid;
706 if ((linkp = avl_find(&dlmgmt_name_avl, &link, NULL)) != NULL)
707 dlmgmt_advance_ppa(linkp);
708 }
709
710 if (lpp->lp_nextppa == (uint_t)-1)
711 return (ENOSPC);
712
713 (void) snprintf(name, size, "%s%d", prefix, lpp->lp_nextppa);
714 return (0);
715 }
716
717 /*
718 * Advance the next available ppa value if the name prefix of the current
719 * link is in the prefix list.
720 */
721 static void
722 dlmgmt_advance_ppa(dlmgmt_link_t *linkp)
723 {
724 dlmgmt_prefix_t *lpp;
725 char prefix[MAXLINKNAMELEN];
726 char linkname[MAXLINKNAMELEN];
727 uint_t start, ppa;
728
729 (void) dlpi_parselink(linkp->ll_link, prefix, &ppa);
730
731 /*
732 * See whether the requested prefix is already in the list.
733 */
734 for (lpp = &dlmgmt_prefixlist; lpp != NULL; lpp = lpp->lp_next) {
735 if (lpp->lp_zoneid == linkp->ll_zoneid &&
736 strcmp(prefix, lpp->lp_prefix) == 0)
737 break;
738 }
739
740 /*
741 * If the link name prefix is in the list, advance the
742 * next available ppa for the <prefix>N name.
743 */
744 if (lpp == NULL || lpp->lp_nextppa != ppa)
745 return;
746
747 start = lpp->lp_nextppa++;
748 linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
749 while (lpp->lp_nextppa != start) {
750 if (lpp->lp_nextppa == (uint_t)-1) {
751 /*
752 * wrapped around. search from <prefix>1.
753 */
754 lpp->lp_nextppa = 0;
755 (void) snprintf(linkname, MAXLINKNAMELEN,
756 "%s%d", lpp->lp_prefix, lpp->lp_nextppa);
757 linkp = link_by_name(linkname, lpp->lp_zoneid);
758 if (linkp == NULL)
759 return;
760 } else {
761 if (linkp == NULL)
762 return;
763 (void) dlpi_parselink(linkp->ll_link, prefix, &ppa);
764 if ((strcmp(prefix, lpp->lp_prefix) != 0) ||
765 (ppa != lpp->lp_nextppa)) {
766 return;
767 }
768 }
769 linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
770 lpp->lp_nextppa++;
771 }
772 lpp->lp_nextppa = (uint_t)-1;
773 }
774
775 /*
776 * Advance to the next available linkid value.
777 */
778 static void
779 dlmgmt_advance_linkid(dlmgmt_link_t *linkp)
780 {
781 datalink_id_t start;
782
783 if (linkp->ll_linkid != dlmgmt_nextlinkid)
784 return;
785
786 start = dlmgmt_nextlinkid;
787 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp);
788
789 do {
790 if (dlmgmt_nextlinkid == DATALINK_MAX_LINKID) {
791 /*
792 * wrapped around. search from 1.
793 */
794 dlmgmt_nextlinkid = 1;
795 if ((linkp = link_by_id(1, GLOBAL_ZONEID)) == NULL)
796 return;
797 } else {
798 dlmgmt_nextlinkid++;
799 if (linkp == NULL)
800 return;
801 if (linkp->ll_linkid != dlmgmt_nextlinkid)
802 return;
803 }
804
805 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp);
806 } while (dlmgmt_nextlinkid != start);
807
808 dlmgmt_nextlinkid = DATALINK_INVALID_LINKID;
809 }
810
811 /*
812 * Advance various global values, for example, next linkid value, next ppa for
813 * various prefix etc.
814 */
815 void
816 dlmgmt_advance(dlmgmt_link_t *linkp)
817 {
818 dlmgmt_advance_linkid(linkp);
819 dlmgmt_advance_ppa(linkp);
820 }
821
822 /*
823 * Advance to the next available dlconf id.
824 */
825 void
826 dlmgmt_advance_dlconfid(dlmgmt_dlconf_t *dlconfp)
827 {
828 uint_t start;
829
830 start = dlmgmt_nextconfid++;
831 dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp);
832 while (dlmgmt_nextconfid != start) {
833 if (dlmgmt_nextconfid == 0) {
834 dlmgmt_dlconf_t dlconf;
835
836 /*
837 * wrapped around. search from 1.
838 */
839 dlconf.ld_id = dlmgmt_nextconfid = 1;
840 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
841 if (dlconfp == NULL)
842 return;
843 } else {
844 if ((dlconfp == NULL) ||
845 (dlconfp->ld_id != dlmgmt_nextconfid)) {
846 return;
847 }
848 }
849 dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp);
850 dlmgmt_nextconfid++;
851 }
852 dlmgmt_nextconfid = 0;
853 }