diff options
Diffstat (limited to 'usr/src/cmd/dlmgmtd/dlmgmt_util.c')
-rw-r--r-- | usr/src/cmd/dlmgmtd/dlmgmt_util.c | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_util.c b/usr/src/cmd/dlmgmtd/dlmgmt_util.c new file mode 100644 index 0000000000..06416db018 --- /dev/null +++ b/usr/src/cmd/dlmgmtd/dlmgmt_util.c @@ -0,0 +1,748 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Utility functions used by the dlmgmtd daemon. + */ + +#include <assert.h> +#include <pthread.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <strings.h> +#include <syslog.h> +#include <stdarg.h> +#include <libdlpi.h> +#include "dlmgmt_impl.h" + +/* + * There are two datalink AVL tables. One table (dlmgmt_name_avl) is keyed by + * the link name, and the other (dlmgmt_id_avl) is keyed by the link id. + * Each link will be present in both tables. + */ +avl_tree_t dlmgmt_name_avl; +avl_tree_t dlmgmt_id_avl; + +avl_tree_t dlmgmt_dlconf_avl; + +static pthread_rwlock_t dlmgmt_avl_lock = PTHREAD_RWLOCK_INITIALIZER; +static pthread_mutex_t dlmgmt_avl_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t dlmgmt_avl_cv = PTHREAD_COND_INITIALIZER; +static pthread_rwlock_t dlmgmt_dlconf_lock = PTHREAD_RWLOCK_INITIALIZER; + +typedef struct dlmgmt_prefix { + struct dlmgmt_prefix *lp_next; + char lp_prefix[MAXLINKNAMELEN]; + uint_t lp_nextppa; +} dlmgmt_prefix_t; +static dlmgmt_prefix_t *dlmgmt_prefixlist; + +static datalink_id_t dlmgmt_nextlinkid; +static datalink_id_t dlmgmt_nextconfid = 1; + +static int linkattr_add(dlmgmt_linkattr_t **, + dlmgmt_linkattr_t *); +static int linkattr_rm(dlmgmt_linkattr_t **, + dlmgmt_linkattr_t *); +static int link_create(const char *, datalink_class_t, uint32_t, + uint32_t, dlmgmt_link_t **); + +static void dlmgmt_advance_linkid(dlmgmt_link_t *); +static void dlmgmt_advance_ppa(dlmgmt_link_t *); + +void +dlmgmt_log(int pri, const char *fmt, ...) +{ + va_list alist; + + va_start(alist, fmt); + if (debug) { + (void) vfprintf(stderr, fmt, alist); + (void) fputc('\n', stderr); + } else { + vsyslog(pri, fmt, alist); + } + va_end(alist); +} + +static int +cmp_link_by_name(const void *v1, const void *v2) +{ + const dlmgmt_link_t *link1 = v1; + const dlmgmt_link_t *link2 = v2; + int cmp; + + cmp = strcmp(link1->ll_link, link2->ll_link); + return ((cmp == 0) ? 0 : ((cmp < 0) ? -1 : 1)); +} + +static int +cmp_link_by_id(const void *v1, const void *v2) +{ + const dlmgmt_link_t *link1 = v1; + const dlmgmt_link_t *link2 = v2; + + if ((uint64_t)(link1->ll_linkid) == (uint64_t)(link2->ll_linkid)) + return (0); + else if ((uint64_t)(link1->ll_linkid) < (uint64_t)(link2->ll_linkid)) + return (-1); + else + return (1); +} + +static int +cmp_dlconf_by_id(const void *v1, const void *v2) +{ + const dlmgmt_dlconf_t *dlconfp1 = v1; + const dlmgmt_dlconf_t *dlconfp2 = v2; + + if (dlconfp1->ld_id == dlconfp2->ld_id) + return (0); + else if (dlconfp1->ld_id < dlconfp2->ld_id) + return (-1); + else + return (1); +} + +int +dlmgmt_linktable_init() +{ + /* + * Initialize the prefix list. First add the "net" prefix to the list. + */ + dlmgmt_prefixlist = malloc(sizeof (dlmgmt_prefix_t)); + if (dlmgmt_prefixlist == NULL) { + dlmgmt_log(LOG_WARNING, "dlmgmt_linktable_init() failed: %s", + strerror(ENOMEM)); + return (ENOMEM); + } + + dlmgmt_prefixlist->lp_next = NULL; + dlmgmt_prefixlist->lp_nextppa = 0; + (void) strlcpy(dlmgmt_prefixlist->lp_prefix, "net", MAXLINKNAMELEN); + + avl_create(&dlmgmt_name_avl, cmp_link_by_name, sizeof (dlmgmt_link_t), + offsetof(dlmgmt_link_t, ll_node_by_name)); + avl_create(&dlmgmt_id_avl, cmp_link_by_id, sizeof (dlmgmt_link_t), + offsetof(dlmgmt_link_t, ll_node_by_id)); + avl_create(&dlmgmt_dlconf_avl, cmp_dlconf_by_id, + sizeof (dlmgmt_dlconf_t), offsetof(dlmgmt_dlconf_t, ld_node)); + dlmgmt_nextlinkid = 1; + return (0); +} + +void +dlmgmt_linktable_fini() +{ + dlmgmt_prefix_t *lpp, *next; + + for (lpp = dlmgmt_prefixlist; lpp != NULL; lpp = next) { + next = lpp->lp_next; + free(lpp); + } + + avl_destroy(&dlmgmt_dlconf_avl); + avl_destroy(&dlmgmt_name_avl); + avl_destroy(&dlmgmt_id_avl); +} + +static int +linkattr_add(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp) +{ + if (*headp == NULL) { + *headp = attrp; + } else { + (*headp)->lp_prev = attrp; + attrp->lp_next = *headp; + *headp = attrp; + } + return (0); +} + +static int +linkattr_rm(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp) +{ + dlmgmt_linkattr_t *next, *prev; + + next = attrp->lp_next; + prev = attrp->lp_prev; + if (next != NULL) + next->lp_prev = prev; + if (prev != NULL) + prev->lp_next = next; + else + *headp = next; + + return (0); +} + +int +linkattr_set(dlmgmt_linkattr_t **headp, const char *attr, void *attrval, + size_t attrsz, dladm_datatype_t type) +{ + dlmgmt_linkattr_t *attrp; + int err; + + /* + * See whether the attr is already set. + */ + for (attrp = *headp; attrp != NULL; attrp = attrp->lp_next) { + if (strcmp(attrp->lp_name, attr) == 0) + break; + } + + if (attrp != NULL) { + /* + * It is already set. If the value changed, update it. + */ + if (linkattr_equal(headp, attr, attrval, attrsz)) + return (0); + + free(attrp->lp_val); + } else { + /* + * It is not set yet, allocate the linkattr and prepend to the + * list. + */ + if ((attrp = calloc(1, sizeof (dlmgmt_linkattr_t))) == NULL) + return (ENOMEM); + + if ((err = linkattr_add(headp, attrp)) != 0) { + free(attrp); + return (err); + } + (void) strlcpy(attrp->lp_name, attr, MAXLINKATTRLEN); + } + if ((attrp->lp_val = calloc(1, attrsz)) == NULL) { + (void) linkattr_rm(headp, attrp); + free(attrp); + return (ENOMEM); + } + + bcopy(attrval, attrp->lp_val, attrsz); + attrp->lp_sz = attrsz; + attrp->lp_type = type; + return (0); +} + +int +linkattr_unset(dlmgmt_linkattr_t **headp, const char *attr) +{ + dlmgmt_linkattr_t *attrp, *prev; + + /* + * See whether the attr exists. + */ + for (prev = NULL, attrp = *headp; attrp != NULL; + prev = attrp, attrp = attrp->lp_next) { + if (strcmp(attrp->lp_name, attr) == 0) + break; + } + + /* + * This attribute is not set in the first place. Return success. + */ + if (attrp == NULL) + return (0); + + /* + * Remove this attr from the list. + */ + if (prev == NULL) + *headp = attrp->lp_next; + else + prev->lp_next = attrp->lp_next; + + free(attrp->lp_val); + free(attrp); + return (0); +} + +int +linkattr_get(dlmgmt_linkattr_t **headp, const char *attr, void **attrvalp, + size_t *attrszp, dladm_datatype_t *typep) +{ + dlmgmt_linkattr_t *attrp = *headp; + + /* + * find the specific attr. + */ + for (attrp = *headp; attrp != NULL; attrp = attrp->lp_next) { + if (strcmp(attrp->lp_name, attr) == 0) + break; + } + + if (attrp == NULL) + return (ENOENT); + + *attrvalp = attrp->lp_val; + *attrszp = attrp->lp_sz; + if (typep != NULL) + *typep = attrp->lp_type; + return (0); +} + +boolean_t +linkattr_equal(dlmgmt_linkattr_t **headp, const char *attr, void *attrval, + size_t attrsz) +{ + void *saved_attrval; + size_t saved_attrsz; + + if (linkattr_get(headp, attr, &saved_attrval, &saved_attrsz, NULL) != 0) + return (B_FALSE); + + return ((saved_attrsz == attrsz) && + (memcmp(saved_attrval, attrval, attrsz) == 0)); +} + +static int +dlmgmt_table_readwritelock(boolean_t write) +{ + if (write) + return (pthread_rwlock_trywrlock(&dlmgmt_avl_lock)); + else + return (pthread_rwlock_tryrdlock(&dlmgmt_avl_lock)); +} + +void +dlmgmt_table_lock(boolean_t write) +{ + (void) pthread_mutex_lock(&dlmgmt_avl_mutex); + while (dlmgmt_table_readwritelock(write) == EBUSY) + (void) pthread_cond_wait(&dlmgmt_avl_cv, &dlmgmt_avl_mutex); + + (void) pthread_mutex_unlock(&dlmgmt_avl_mutex); +} + +void +dlmgmt_table_unlock() +{ + (void) pthread_rwlock_unlock(&dlmgmt_avl_lock); + (void) pthread_mutex_lock(&dlmgmt_avl_mutex); + (void) pthread_cond_broadcast(&dlmgmt_avl_cv); + (void) pthread_mutex_unlock(&dlmgmt_avl_mutex); +} + +static int +link_create(const char *name, datalink_class_t class, uint32_t media, + uint32_t flags, dlmgmt_link_t **linkpp) +{ + dlmgmt_link_t *linkp = NULL; + int err = 0; + + if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID) { + err = ENOSPC; + goto done; + } + + if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) { + err = ENOMEM; + goto done; + } + + (void) strlcpy(linkp->ll_link, name, MAXLINKNAMELEN); + linkp->ll_class = class; + linkp->ll_media = media; + linkp->ll_linkid = dlmgmt_nextlinkid; + linkp->ll_flags = flags; + linkp->ll_gen = 0; +done: + *linkpp = linkp; + return (err); +} + +void +link_destroy(dlmgmt_link_t *linkp) +{ + dlmgmt_linkattr_t *next, *attrp; + + for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { + next = attrp->lp_next; + free(attrp->lp_val); + free(attrp); + } + free(linkp); +} + +dlmgmt_link_t * +link_by_id(datalink_id_t linkid) +{ + dlmgmt_link_t link; + + link.ll_linkid = linkid; + return (avl_find(&dlmgmt_id_avl, &link, NULL)); +} + +dlmgmt_link_t * +link_by_name(const char *name) +{ + dlmgmt_link_t link; + + (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN); + return (avl_find(&dlmgmt_name_avl, &link, NULL)); +} + +int +dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media, + uint32_t flags, dlmgmt_link_t **linkpp) +{ + dlmgmt_link_t link, *linkp, *tmp; + avl_index_t name_where, id_where; + int err; + + /* + * Validate the link. + */ + if (!dladm_valid_linkname(name)) + return (EINVAL); + + /* + * Check to see whether this is an existing link name. + */ + (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN); + if ((linkp = avl_find(&dlmgmt_name_avl, &link, &name_where)) != NULL) + return (EEXIST); + + if ((err = link_create(name, class, media, flags, &linkp)) != 0) + return (err); + + link.ll_linkid = linkp->ll_linkid; + tmp = avl_find(&dlmgmt_id_avl, &link, &id_where); + assert(tmp == NULL); + avl_insert(&dlmgmt_name_avl, linkp, name_where); + avl_insert(&dlmgmt_id_avl, linkp, id_where); + dlmgmt_advance(linkp); + *linkpp = linkp; + return (0); +} + +int +dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags) +{ + if ((linkp->ll_flags & flags) == 0) { + /* + * The link does not exist in the specified space. + */ + return (ENOENT); + } + linkp->ll_flags &= ~flags; + if (!(linkp->ll_flags & DLMGMT_PERSIST)) { + dlmgmt_linkattr_t *next, *attrp; + + for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { + next = attrp->lp_next; + free(attrp->lp_val); + free(attrp); + } + linkp->ll_head = NULL; + } + + if (linkp->ll_flags == 0) { + avl_remove(&dlmgmt_id_avl, linkp); + avl_remove(&dlmgmt_name_avl, linkp); + link_destroy(linkp); + } + + return (0); +} + +int +dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr, + dlmgmt_getattr_retval_t **retvalpp, size_t *retszp) +{ + int err; + void *attrval; + size_t attrsz; + dladm_datatype_t attrtype; + dlmgmt_getattr_retval_t *retvalp; + + err = linkattr_get(headp, attr, &attrval, &attrsz, &attrtype); + if (err != 0) + return (err); + + assert(attrsz > 0); + *retszp = sizeof (dlmgmt_getattr_retval_t) + attrsz - 1; + if ((retvalp = malloc(*retszp)) == NULL) + return (ENOMEM); + + retvalp->lr_err = 0; + retvalp->lr_type = attrtype; + bcopy(attrval, retvalp->lr_attr, attrsz); + *retvalpp = retvalp; + return (0); +} + +void +dlmgmt_dlconf_table_lock(boolean_t write) +{ + if (write) + (void) pthread_rwlock_wrlock(&dlmgmt_dlconf_lock); + else + (void) pthread_rwlock_rdlock(&dlmgmt_dlconf_lock); +} + +void +dlmgmt_dlconf_table_unlock() +{ + (void) pthread_rwlock_unlock(&dlmgmt_dlconf_lock); +} + +int +dlconf_create(const char *name, datalink_id_t linkid, datalink_class_t class, + uint32_t media, dlmgmt_dlconf_t **dlconfpp) +{ + dlmgmt_dlconf_t *dlconfp = NULL; + int err = 0; + + if (dlmgmt_nextconfid == 0) { + err = ENOSPC; + goto done; + } + + if ((dlconfp = calloc(1, sizeof (dlmgmt_dlconf_t))) == NULL) { + err = ENOMEM; + goto done; + } + + (void) strlcpy(dlconfp->ld_link, name, MAXLINKNAMELEN); + dlconfp->ld_linkid = linkid; + dlconfp->ld_class = class; + dlconfp->ld_media = media; + dlconfp->ld_id = dlmgmt_nextconfid; + +done: + *dlconfpp = dlconfp; + return (err); +} + +void +dlconf_destroy(dlmgmt_dlconf_t *dlconfp) +{ + dlmgmt_linkattr_t *next, *attrp; + + for (attrp = dlconfp->ld_head; attrp != NULL; attrp = next) { + next = attrp->lp_next; + free(attrp->lp_val); + free(attrp); + } + free(dlconfp); +} + +int +dlmgmt_generate_name(const char *prefix, char *name, size_t size) +{ + dlmgmt_prefix_t *lpp, *prev = NULL; + + /* + * See whether the requested prefix is already in the list. + */ + for (lpp = dlmgmt_prefixlist; lpp != NULL; prev = lpp, + lpp = lpp->lp_next) { + if (strcmp(prefix, lpp->lp_prefix) == 0) + break; + } + + /* + * Not found. + */ + if (lpp == NULL) { + dlmgmt_link_t *linkp, link; + + assert(prev != NULL); + + /* + * First add this new prefix into the prefix list. + */ + if ((lpp = malloc(sizeof (dlmgmt_prefix_t))) == NULL) + return (ENOMEM); + + prev->lp_next = lpp; + lpp->lp_next = NULL; + lpp->lp_nextppa = 0; + (void) strlcpy(lpp->lp_prefix, prefix, MAXLINKNAMELEN); + + /* + * Now determine this prefix's nextppa. + */ + (void) snprintf(link.ll_link, MAXLINKNAMELEN, "%s%d", + prefix, lpp->lp_nextppa); + linkp = avl_find(&dlmgmt_name_avl, &link, NULL); + if (linkp != NULL) + dlmgmt_advance_ppa(linkp); + } + + if (lpp->lp_nextppa == (uint_t)-1) + return (ENOSPC); + + (void) snprintf(name, size, "%s%d", prefix, lpp->lp_nextppa); + return (0); +} + +/* + * Advance the next available ppa value if the name prefix of the current + * link is in the prefix list. + */ +static void +dlmgmt_advance_ppa(dlmgmt_link_t *linkp) +{ + dlmgmt_prefix_t *lpp; + char prefix[MAXLINKNAMELEN]; + uint_t start, ppa; + + (void) dlpi_parselink(linkp->ll_link, prefix, &ppa); + + /* + * See whether the requested prefix is already in the list. + */ + for (lpp = dlmgmt_prefixlist; lpp != NULL; lpp = lpp->lp_next) { + if (strcmp(prefix, lpp->lp_prefix) == 0) + break; + } + + /* + * If the link name prefix is in the list, advance the + * next available ppa for the <prefix>N name. + */ + if (lpp == NULL || lpp->lp_nextppa != ppa) + return; + + start = lpp->lp_nextppa++; + linkp = AVL_NEXT(&dlmgmt_name_avl, linkp); + while (lpp->lp_nextppa != start) { + if (lpp->lp_nextppa == (uint_t)-1) { + dlmgmt_link_t link; + + /* + * wrapped around. search from <prefix>1. + */ + lpp->lp_nextppa = 0; + (void) snprintf(link.ll_link, MAXLINKNAMELEN, + "%s%d", lpp->lp_prefix, lpp->lp_nextppa); + linkp = avl_find(&dlmgmt_name_avl, &link, NULL); + if (linkp == NULL) + return; + } else { + if (linkp == NULL) + return; + (void) dlpi_parselink(linkp->ll_link, prefix, &ppa); + if ((strcmp(prefix, lpp->lp_prefix) != 0) || + (ppa != lpp->lp_nextppa)) { + return; + } + } + linkp = AVL_NEXT(&dlmgmt_name_avl, linkp); + lpp->lp_nextppa++; + } + lpp->lp_nextppa = (uint_t)-1; +} + +/* + * Advance to the next available linkid value. + */ +static void +dlmgmt_advance_linkid(dlmgmt_link_t *linkp) +{ + datalink_id_t start; + + if (linkp->ll_linkid != dlmgmt_nextlinkid) + return; + + start = dlmgmt_nextlinkid; + linkp = AVL_NEXT(&dlmgmt_id_avl, linkp); + + do { + if (dlmgmt_nextlinkid == DATALINK_MAX_LINKID) { + dlmgmt_link_t link; + + /* + * wrapped around. search from 1. + */ + dlmgmt_nextlinkid = 1; + link.ll_linkid = 1; + linkp = avl_find(&dlmgmt_id_avl, &link, NULL); + if (linkp == NULL) + return; + } else { + dlmgmt_nextlinkid++; + if (linkp == NULL) + return; + if (linkp->ll_linkid != dlmgmt_nextlinkid) + return; + } + + linkp = AVL_NEXT(&dlmgmt_id_avl, linkp); + } while (dlmgmt_nextlinkid != start); + + dlmgmt_nextlinkid = DATALINK_INVALID_LINKID; +} + +/* + * Advance various global values, for example, next linkid value, next ppa for + * various prefix etc. + */ +void +dlmgmt_advance(dlmgmt_link_t *linkp) +{ + dlmgmt_advance_linkid(linkp); + dlmgmt_advance_ppa(linkp); +} + +/* + * Advance to the next available dlconf id. + */ +void +dlmgmt_advance_dlconfid(dlmgmt_dlconf_t *dlconfp) +{ + uint_t start; + + start = dlmgmt_nextconfid++; + dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp); + while (dlmgmt_nextconfid != start) { + if (dlmgmt_nextconfid == 0) { + dlmgmt_dlconf_t dlconf; + + /* + * wrapped around. search from 1. + */ + dlconf.ld_id = dlmgmt_nextconfid = 1; + dlconfp = avl_find(&dlmgmt_name_avl, &dlconf, NULL); + if (dlconfp == NULL) + return; + } else { + if ((dlconfp == NULL) || + (dlconfp->ld_id != dlmgmt_nextconfid)) { + return; + } + } + dlconfp = AVL_NEXT(&dlmgmt_name_avl, dlconfp); + dlmgmt_nextconfid++; + } + dlmgmt_nextconfid = 0; +} |