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
dlmgmt_log(int pri,const char * fmt,...)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
cmp_link_by_name(const void * v1,const void * v2)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
cmp_link_by_zname(const void * v1,const void * v2)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
cmp_link_by_id(const void * v1,const void * v2)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
cmp_dlconf_by_id(const void * v1,const void * v2)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
dlmgmt_linktable_init(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
dlmgmt_linktable_fini(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
linkattr_add(dlmgmt_linkattr_t ** headp,dlmgmt_linkattr_t * attrp)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
linkattr_rm(dlmgmt_linkattr_t ** headp,dlmgmt_linkattr_t * attrp)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 *
linkattr_find(dlmgmt_linkattr_t * headp,const char * attr)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
linkattr_set(dlmgmt_linkattr_t ** headp,const char * attr,void * attrval,size_t attrsz,dladm_datatype_t type)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
linkattr_unset(dlmgmt_linkattr_t ** headp,const char * attr)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
linkattr_get(dlmgmt_linkattr_t ** headp,const char * attr,void ** attrvalp,size_t * attrszp,dladm_datatype_t * typep)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
linkattr_equal(dlmgmt_linkattr_t ** headp,const char * attr,void * attrval,size_t attrsz)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
linkattr_destroy(dlmgmt_link_t * linkp)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
dlmgmt_table_readwritelock(boolean_t write)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
dlmgmt_table_lock(boolean_t write)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
dlmgmt_table_unlock(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
link_destroy(dlmgmt_link_t * linkp)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
link_activate(dlmgmt_link_t * linkp)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
link_is_visible(dlmgmt_link_t * linkp,zoneid_t zoneid)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 *
link_by_id(datalink_id_t linkid,zoneid_t zoneid)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 *
link_by_name(const char * name,zoneid_t zoneid)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
dlmgmt_create_common(const char * name,datalink_class_t class,uint32_t media,zoneid_t zoneid,uint32_t flags,dlmgmt_link_t ** linkpp)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
dlmgmt_destroy_common(dlmgmt_link_t * linkp,uint32_t flags)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
dlmgmt_getattr_common(dlmgmt_linkattr_t ** headp,const char * attr,dlmgmt_getattr_retval_t * retvalp)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
dlmgmt_dlconf_table_lock(boolean_t write)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
dlmgmt_dlconf_table_unlock(void)627 dlmgmt_dlconf_table_unlock(void)
628 {
629 (void) pthread_rwlock_unlock(&dlmgmt_dlconf_lock);
630 }
631
632 int
dlconf_create(const char * name,datalink_id_t linkid,datalink_class_t class,uint32_t media,zoneid_t zoneid,dlmgmt_dlconf_t ** dlconfpp)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
dlconf_destroy(dlmgmt_dlconf_t * dlconfp)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
dlmgmt_generate_name(const char * prefix,char * name,size_t size,zoneid_t zoneid)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
dlmgmt_advance_ppa(dlmgmt_link_t * linkp)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
dlmgmt_advance_linkid(dlmgmt_link_t * linkp)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
dlmgmt_advance(dlmgmt_link_t * linkp)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
dlmgmt_advance_dlconfid(dlmgmt_dlconf_t * dlconfp)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