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