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.
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.
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]
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Utility functions used by the dlmgmtd daemon.
43 #include "dlmgmt_impl.h"
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.
52 avl_tree_t dlmgmt_name_avl
;
53 avl_tree_t dlmgmt_id_avl
;
54 avl_tree_t dlmgmt_loan_avl
;
56 avl_tree_t dlmgmt_dlconf_avl
;
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
;
63 typedef struct dlmgmt_prefix
{
64 struct dlmgmt_prefix
*lp_next
;
65 char lp_prefix
[MAXLINKNAMELEN
];
69 static dlmgmt_prefix_t dlmgmt_prefixlist
;
71 datalink_id_t dlmgmt_nextlinkid
;
72 static datalink_id_t dlmgmt_nextconfid
= 1;
74 static void dlmgmt_advance_linkid(dlmgmt_link_t
*);
75 static void dlmgmt_advance_ppa(dlmgmt_link_t
*);
78 dlmgmt_log(int pri
, const char *fmt
, ...)
84 (void) vfprintf(stderr
, fmt
, alist
);
85 (void) fputc('\n', stderr
);
87 vsyslog(pri
, fmt
, alist
);
93 cmp_link_by_name(const void *v1
, const void *v2
)
95 const dlmgmt_link_t
*link1
= v1
;
96 const dlmgmt_link_t
*link2
= v2
;
99 cmp
= strcmp(link1
->ll_link
, link2
->ll_link
);
100 return ((cmp
== 0) ? 0 : ((cmp
< 0) ? -1 : 1));
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.
109 cmp_link_by_zname(const void *v1
, const void *v2
)
111 const dlmgmt_link_t
*link1
= v1
;
112 const dlmgmt_link_t
*link2
= v2
;
114 if (link1
->ll_zoneid
< link2
->ll_zoneid
)
116 if (link1
->ll_zoneid
> link2
->ll_zoneid
)
118 return (cmp_link_by_name(link1
, link2
));
122 cmp_link_by_id(const void *v1
, const void *v2
)
124 const dlmgmt_link_t
*link1
= v1
;
125 const dlmgmt_link_t
*link2
= v2
;
127 if ((uint64_t)(link1
->ll_linkid
) == (uint64_t)(link2
->ll_linkid
))
129 else if ((uint64_t)(link1
->ll_linkid
) < (uint64_t)(link2
->ll_linkid
))
136 cmp_dlconf_by_id(const void *v1
, const void *v2
)
138 const dlmgmt_dlconf_t
*dlconfp1
= v1
;
139 const dlmgmt_dlconf_t
*dlconfp2
= v2
;
141 if (dlconfp1
->ld_id
== dlconfp2
->ld_id
)
143 else if (dlconfp1
->ld_id
< dlconfp2
->ld_id
)
150 dlmgmt_linktable_init(void)
153 * Initialize the prefix list. First add the "net" prefix for the
154 * global zone to the list.
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
);
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;
173 dlmgmt_linktable_fini(void)
175 dlmgmt_prefix_t
*lpp
, *next
;
177 for (lpp
= dlmgmt_prefixlist
.lp_next
; lpp
!= NULL
; lpp
= next
) {
182 avl_destroy(&dlmgmt_dlconf_avl
);
183 avl_destroy(&dlmgmt_name_avl
);
184 avl_destroy(&dlmgmt_loan_avl
);
185 avl_destroy(&dlmgmt_id_avl
);
189 linkattr_add(dlmgmt_linkattr_t
**headp
, dlmgmt_linkattr_t
*attrp
)
191 if (*headp
== NULL
) {
194 (*headp
)->lp_prev
= attrp
;
195 attrp
->lp_next
= *headp
;
201 linkattr_rm(dlmgmt_linkattr_t
**headp
, dlmgmt_linkattr_t
*attrp
)
203 dlmgmt_linkattr_t
*next
, *prev
;
205 next
= attrp
->lp_next
;
206 prev
= attrp
->lp_prev
;
208 next
->lp_prev
= prev
;
210 prev
->lp_next
= next
;
216 linkattr_find(dlmgmt_linkattr_t
*headp
, const char *attr
)
218 dlmgmt_linkattr_t
*attrp
;
220 for (attrp
= headp
; attrp
!= NULL
; attrp
= attrp
->lp_next
) {
221 if (strcmp(attrp
->lp_name
, attr
) == 0)
228 linkattr_set(dlmgmt_linkattr_t
**headp
, const char *attr
, void *attrval
,
229 size_t attrsz
, dladm_datatype_t type
)
231 dlmgmt_linkattr_t
*attrp
;
235 attrp
= linkattr_find(*headp
, attr
);
238 * It is already set. If the value changed, update it.
240 if (linkattr_equal(headp
, attr
, attrval
, attrsz
))
245 * It is not set yet, allocate the linkattr and prepend to the
248 if ((attrp
= calloc(1, sizeof (dlmgmt_linkattr_t
))) == NULL
)
251 (void) strlcpy(attrp
->lp_name
, attr
, MAXLINKATTRLEN
);
254 if ((newval
= calloc(1, attrsz
)) == NULL
) {
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
);
268 linkattr_add(headp
, attrp
);
273 linkattr_unset(dlmgmt_linkattr_t
**headp
, const char *attr
)
275 dlmgmt_linkattr_t
*attrp
;
277 if ((attrp
= linkattr_find(*headp
, attr
)) != NULL
) {
278 linkattr_rm(headp
, attrp
);
285 linkattr_get(dlmgmt_linkattr_t
**headp
, const char *attr
, void **attrvalp
,
286 size_t *attrszp
, dladm_datatype_t
*typep
)
288 dlmgmt_linkattr_t
*attrp
;
290 if ((attrp
= linkattr_find(*headp
, attr
)) == NULL
)
293 *attrvalp
= attrp
->lp_val
;
294 *attrszp
= attrp
->lp_sz
;
296 *typep
= attrp
->lp_type
;
301 linkattr_equal(dlmgmt_linkattr_t
**headp
, const char *attr
, void *attrval
,
307 if (linkattr_get(headp
, attr
, &saved_attrval
, &saved_attrsz
, NULL
) != 0)
310 return ((saved_attrsz
== attrsz
) &&
311 (memcmp(saved_attrval
, attrval
, attrsz
) == 0));
315 linkattr_destroy(dlmgmt_link_t
*linkp
)
317 dlmgmt_linkattr_t
*next
, *attrp
;
319 for (attrp
= linkp
->ll_head
; attrp
!= NULL
; attrp
= next
) {
320 next
= attrp
->lp_next
;
327 dlmgmt_table_readwritelock(boolean_t write
)
330 return (pthread_rwlock_trywrlock(&dlmgmt_avl_lock
));
332 return (pthread_rwlock_tryrdlock(&dlmgmt_avl_lock
));
336 dlmgmt_table_lock(boolean_t write
)
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
);
342 (void) pthread_mutex_unlock(&dlmgmt_avl_mutex
);
346 dlmgmt_table_unlock(void)
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
);
355 link_destroy(dlmgmt_link_t
*linkp
)
357 linkattr_destroy(linkp
);
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
367 link_activate(dlmgmt_link_t
*linkp
)
370 zoneid_t zoneid
= ALL_ZONES
;
372 if (zone_check_datalink(&zoneid
, linkp
->ll_linkid
) == 0) {
374 * This link was already added to a non-global zone. This can
375 * happen if dlmgmtd is restarted.
377 if (zoneid
!= linkp
->ll_zoneid
) {
378 if (link_by_name(linkp
->ll_link
, zoneid
) != NULL
) {
383 if (avl_find(&dlmgmt_name_avl
, linkp
, NULL
) != NULL
)
384 avl_remove(&dlmgmt_name_avl
, linkp
);
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
;
391 } else if (linkp
->ll_zoneid
!= GLOBAL_ZONEID
) {
392 err
= zone_add_datalink(linkp
->ll_zoneid
, linkp
->ll_linkid
);
396 linkp
->ll_flags
|= DLMGMT_ACTIVE
;
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.
406 link_is_visible(dlmgmt_link_t
*linkp
, zoneid_t zoneid
)
408 return (linkp
->ll_zoneid
== zoneid
||
409 (zoneid
== GLOBAL_ZONEID
&& linkp
->ll_onloan
));
413 link_by_id(datalink_id_t linkid
, zoneid_t zoneid
)
415 dlmgmt_link_t link
, *linkp
;
417 link
.ll_linkid
= linkid
;
418 if ((linkp
= avl_find(&dlmgmt_id_avl
, &link
, NULL
)) == NULL
)
420 if (zoneid
!= GLOBAL_ZONEID
&& linkp
->ll_zoneid
!= zoneid
)
426 link_by_name(const char *name
, zoneid_t zoneid
)
428 dlmgmt_link_t link
, *linkp
;
430 (void) strlcpy(link
.ll_link
, name
, MAXLINKNAMELEN
);
431 link
.ll_zoneid
= zoneid
;
432 linkp
= avl_find(&dlmgmt_name_avl
, &link
, NULL
);
433 if (linkp
== NULL
&& zoneid
== GLOBAL_ZONEID
) {
434 /* The link could be on loan to a non-global zone? */
435 linkp
= avl_find(&dlmgmt_loan_avl
, &link
, NULL
);
441 dlmgmt_create_common(const char *name
, datalink_class_t
class, uint32_t media
,
442 zoneid_t zoneid
, uint32_t flags
, dlmgmt_link_t
**linkpp
)
444 dlmgmt_link_t
*linkp
= NULL
;
445 avl_index_t name_where
, id_where
;
448 if (!dladm_valid_linkname(name
))
450 if (dlmgmt_nextlinkid
== DATALINK_INVALID_LINKID
)
453 if ((linkp
= calloc(1, sizeof (dlmgmt_link_t
))) == NULL
) {
458 (void) strlcpy(linkp
->ll_link
, name
, MAXLINKNAMELEN
);
459 linkp
->ll_class
= class;
460 linkp
->ll_media
= media
;
461 linkp
->ll_linkid
= dlmgmt_nextlinkid
;
462 linkp
->ll_zoneid
= zoneid
;
465 if (avl_find(&dlmgmt_name_avl
, linkp
, &name_where
) != NULL
||
466 avl_find(&dlmgmt_id_avl
, linkp
, &id_where
) != NULL
) {
471 avl_insert(&dlmgmt_name_avl
, linkp
, name_where
);
472 avl_insert(&dlmgmt_id_avl
, linkp
, id_where
);
474 if ((flags
& DLMGMT_ACTIVE
) && (err
= link_activate(linkp
)) != 0) {
475 avl_remove(&dlmgmt_name_avl
, linkp
);
476 avl_remove(&dlmgmt_id_avl
, linkp
);
480 linkp
->ll_flags
= flags
;
481 dlmgmt_advance(linkp
);
491 dlmgmt_destroy_common(dlmgmt_link_t
*linkp
, uint32_t flags
)
493 if ((linkp
->ll_flags
& flags
) == 0) {
495 * The link does not exist in the specified space.
500 linkp
->ll_flags
&= ~flags
;
501 if (flags
& DLMGMT_PERSIST
) {
502 dlmgmt_linkattr_t
*next
, *attrp
;
504 for (attrp
= linkp
->ll_head
; attrp
!= NULL
; attrp
= next
) {
505 next
= attrp
->lp_next
;
509 linkp
->ll_head
= NULL
;
512 if ((flags
& DLMGMT_ACTIVE
) && linkp
->ll_zoneid
!= GLOBAL_ZONEID
) {
513 (void) zone_remove_datalink(linkp
->ll_zoneid
, linkp
->ll_linkid
);
514 if (linkp
->ll_onloan
)
515 avl_remove(&dlmgmt_loan_avl
, linkp
);
518 if (linkp
->ll_flags
== 0) {
519 avl_remove(&dlmgmt_id_avl
, linkp
);
520 avl_remove(&dlmgmt_name_avl
, linkp
);
528 dlmgmt_getattr_common(dlmgmt_linkattr_t
**headp
, const char *attr
,
529 dlmgmt_getattr_retval_t
*retvalp
)
534 dladm_datatype_t attrtype
;
536 err
= linkattr_get(headp
, attr
, &attrval
, &attrsz
, &attrtype
);
541 if (attrsz
> MAXLINKATTRVALLEN
)
544 retvalp
->lr_type
= attrtype
;
545 retvalp
->lr_attrsz
= attrsz
;
546 bcopy(attrval
, retvalp
->lr_attrval
, attrsz
);
551 dlmgmt_dlconf_table_lock(boolean_t write
)
554 (void) pthread_rwlock_wrlock(&dlmgmt_dlconf_lock
);
556 (void) pthread_rwlock_rdlock(&dlmgmt_dlconf_lock
);
560 dlmgmt_dlconf_table_unlock(void)
562 (void) pthread_rwlock_unlock(&dlmgmt_dlconf_lock
);
566 dlconf_create(const char *name
, datalink_id_t linkid
, datalink_class_t
class,
567 uint32_t media
, zoneid_t zoneid
, dlmgmt_dlconf_t
**dlconfpp
)
569 dlmgmt_dlconf_t
*dlconfp
= NULL
;
572 if (dlmgmt_nextconfid
== 0) {
577 if ((dlconfp
= calloc(1, sizeof (dlmgmt_dlconf_t
))) == NULL
) {
582 (void) strlcpy(dlconfp
->ld_link
, name
, MAXLINKNAMELEN
);
583 dlconfp
->ld_linkid
= linkid
;
584 dlconfp
->ld_class
= class;
585 dlconfp
->ld_media
= media
;
586 dlconfp
->ld_id
= dlmgmt_nextconfid
;
587 dlconfp
->ld_zoneid
= zoneid
;
595 dlconf_destroy(dlmgmt_dlconf_t
*dlconfp
)
597 dlmgmt_linkattr_t
*next
, *attrp
;
599 for (attrp
= dlconfp
->ld_head
; attrp
!= NULL
; attrp
= next
) {
600 next
= attrp
->lp_next
;
608 dlmgmt_generate_name(const char *prefix
, char *name
, size_t size
,
611 dlmgmt_prefix_t
*lpp
, *prev
= NULL
;
612 dlmgmt_link_t link
, *linkp
;
615 * See whether the requested prefix is already in the list.
617 for (lpp
= &dlmgmt_prefixlist
; lpp
!= NULL
;
618 prev
= lpp
, lpp
= lpp
->lp_next
) {
619 if (lpp
->lp_zoneid
== zoneid
&&
620 strcmp(prefix
, lpp
->lp_prefix
) == 0)
628 assert(prev
!= NULL
);
631 * First add this new prefix into the prefix list.
633 if ((lpp
= malloc(sizeof (dlmgmt_prefix_t
))) == NULL
)
638 lpp
->lp_zoneid
= zoneid
;
640 (void) strlcpy(lpp
->lp_prefix
, prefix
, MAXLINKNAMELEN
);
643 * Now determine this prefix's nextppa.
645 (void) snprintf(link
.ll_link
, MAXLINKNAMELEN
, "%s%d",
647 link
.ll_zoneid
= zoneid
;
648 if ((linkp
= avl_find(&dlmgmt_name_avl
, &link
, NULL
)) != NULL
)
649 dlmgmt_advance_ppa(linkp
);
652 if (lpp
->lp_nextppa
== (uint_t
)-1)
655 (void) snprintf(name
, size
, "%s%d", prefix
, lpp
->lp_nextppa
);
660 * Advance the next available ppa value if the name prefix of the current
661 * link is in the prefix list.
664 dlmgmt_advance_ppa(dlmgmt_link_t
*linkp
)
666 dlmgmt_prefix_t
*lpp
;
667 char prefix
[MAXLINKNAMELEN
];
668 char linkname
[MAXLINKNAMELEN
];
671 (void) dlpi_parselink(linkp
->ll_link
, prefix
, &ppa
);
674 * See whether the requested prefix is already in the list.
676 for (lpp
= &dlmgmt_prefixlist
; lpp
!= NULL
; lpp
= lpp
->lp_next
) {
677 if (lpp
->lp_zoneid
== linkp
->ll_zoneid
&&
678 strcmp(prefix
, lpp
->lp_prefix
) == 0)
683 * If the link name prefix is in the list, advance the
684 * next available ppa for the <prefix>N name.
686 if (lpp
== NULL
|| lpp
->lp_nextppa
!= ppa
)
689 start
= lpp
->lp_nextppa
++;
690 linkp
= AVL_NEXT(&dlmgmt_name_avl
, linkp
);
691 while (lpp
->lp_nextppa
!= start
) {
692 if (lpp
->lp_nextppa
== (uint_t
)-1) {
694 * wrapped around. search from <prefix>1.
697 (void) snprintf(linkname
, MAXLINKNAMELEN
,
698 "%s%d", lpp
->lp_prefix
, lpp
->lp_nextppa
);
699 linkp
= link_by_name(linkname
, lpp
->lp_zoneid
);
705 (void) dlpi_parselink(linkp
->ll_link
, prefix
, &ppa
);
706 if ((strcmp(prefix
, lpp
->lp_prefix
) != 0) ||
707 (ppa
!= lpp
->lp_nextppa
)) {
711 linkp
= AVL_NEXT(&dlmgmt_name_avl
, linkp
);
714 lpp
->lp_nextppa
= (uint_t
)-1;
718 * Advance to the next available linkid value.
721 dlmgmt_advance_linkid(dlmgmt_link_t
*linkp
)
725 if (linkp
->ll_linkid
!= dlmgmt_nextlinkid
)
728 start
= dlmgmt_nextlinkid
;
729 linkp
= AVL_NEXT(&dlmgmt_id_avl
, linkp
);
732 if (dlmgmt_nextlinkid
== DATALINK_MAX_LINKID
) {
734 * wrapped around. search from 1.
736 dlmgmt_nextlinkid
= 1;
737 if ((linkp
= link_by_id(1, GLOBAL_ZONEID
)) == NULL
)
743 if (linkp
->ll_linkid
!= dlmgmt_nextlinkid
)
747 linkp
= AVL_NEXT(&dlmgmt_id_avl
, linkp
);
748 } while (dlmgmt_nextlinkid
!= start
);
750 dlmgmt_nextlinkid
= DATALINK_INVALID_LINKID
;
754 * Advance various global values, for example, next linkid value, next ppa for
755 * various prefix etc.
758 dlmgmt_advance(dlmgmt_link_t
*linkp
)
760 dlmgmt_advance_linkid(linkp
);
761 dlmgmt_advance_ppa(linkp
);
765 * Advance to the next available dlconf id.
768 dlmgmt_advance_dlconfid(dlmgmt_dlconf_t
*dlconfp
)
772 start
= dlmgmt_nextconfid
++;
773 dlconfp
= AVL_NEXT(&dlmgmt_dlconf_avl
, dlconfp
);
774 while (dlmgmt_nextconfid
!= start
) {
775 if (dlmgmt_nextconfid
== 0) {
776 dlmgmt_dlconf_t dlconf
;
779 * wrapped around. search from 1.
781 dlconf
.ld_id
= dlmgmt_nextconfid
= 1;
782 dlconfp
= avl_find(&dlmgmt_dlconf_avl
, &dlconf
, NULL
);
786 if ((dlconfp
== NULL
) ||
787 (dlconfp
->ld_id
!= dlmgmt_nextconfid
)) {
791 dlconfp
= AVL_NEXT(&dlmgmt_dlconf_avl
, dlconfp
);
794 dlmgmt_nextconfid
= 0;