summaryrefslogtreecommitdiff
path: root/usr/src/cmd/rcm_daemon
diff options
context:
space:
mode:
authorRishi Srivatsavai <Rishi.Srivatsavai@Sun.COM>2009-09-10 15:11:49 -0400
committerRishi Srivatsavai <Rishi.Srivatsavai@Sun.COM>2009-09-10 15:11:49 -0400
commit4eaa471005973e11a6110b69fe990530b3b95a38 (patch)
tree3aca4c2ad771c935bfa146d4a6abbe51ee464a57 /usr/src/cmd/rcm_daemon
parentd5688513c1985c22c27675da44b5b691f123818e (diff)
downloadillumos-joyent-4eaa471005973e11a6110b69fe990530b3b95a38.tar.gz
PSARC 2007/596 RBridges: Routing Bridges
PSARC 2008/055 Solaris Bridging PSARC 2009/344 Bridging Updates 6223953 Solaris should provide layer 2 bridging 6770623 bogus error messages generated by dladm should be cleaned up
Diffstat (limited to 'usr/src/cmd/rcm_daemon')
-rw-r--r--usr/src/cmd/rcm_daemon/Makefile.com10
-rw-r--r--usr/src/cmd/rcm_daemon/common/bridge_rcm.c898
2 files changed, 905 insertions, 3 deletions
diff --git a/usr/src/cmd/rcm_daemon/Makefile.com b/usr/src/cmd/rcm_daemon/Makefile.com
index c72efbeed2..d5a7c72ff7 100644
--- a/usr/src/cmd/rcm_daemon/Makefile.com
+++ b/usr/src/cmd/rcm_daemon/Makefile.com
@@ -56,7 +56,8 @@ COMMON_MOD_SRC = \
$(COMMON)/pool_rcm.c \
$(COMMON)/mpxio_rcm.c \
$(COMMON)/ip_anon_rcm.c \
- $(COMMON)/svm_rcm.c
+ $(COMMON)/svm_rcm.c \
+ $(COMMON)/bridge_rcm.c
sparc_MOD_SRC = $(COMMON)/ttymux_rcm.c
@@ -79,7 +80,8 @@ COMMON_MOD_OBJ = \
pool_rcm.o \
mpxio_rcm.o \
ip_anon_rcm.o \
- svm_rcm.o
+ svm_rcm.o \
+ bridge_rcm.o
sparc_MOD_OBJ = ttymux_rcm.o
@@ -98,7 +100,8 @@ COMMON_RCM_MODS = \
SUNW_pool_rcm.so \
SUNW_mpxio_rcm.so \
SUNW_ip_anon_rcm.so \
- SUNW_svm_rcm.so
+ SUNW_svm_rcm.so \
+ SUNW_bridge_rcm.so
sparc_RCM_MODS = SUNW_ttymux_rcm.so
@@ -128,6 +131,7 @@ SUNW_vnic_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm
SUNW_aggr_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm
SUNW_ip_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil -ldladm -lipmp
SUNW_ip_anon_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil
+SUNW_bridge_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm
LDLIBS += -lgen -lelf -lrcm -lnvpair -ldevinfo -lnsl -lsocket
diff --git a/usr/src/cmd/rcm_daemon/common/bridge_rcm.c b/usr/src/cmd/rcm_daemon/common/bridge_rcm.c
new file mode 100644
index 0000000000..60188c2fda
--- /dev/null
+++ b/usr/src/cmd/rcm_daemon/common/bridge_rcm.c
@@ -0,0 +1,898 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This RCM module adds support to the RCM framework for Bridge links
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <synch.h>
+#include <assert.h>
+#include <strings.h>
+#include "rcm_module.h"
+#include <libintl.h>
+#include <libdllink.h>
+#include <libdlbridge.h>
+#include <libdlpi.h>
+
+/*
+ * Definitions
+ */
+#ifndef lint
+#define _(x) gettext(x)
+#else
+#define _(x) x
+#endif
+
+/* Some generic well-knowns and defaults used in this module */
+#define RCM_LINK_PREFIX "SUNW_datalink" /* RCM datalink name prefix */
+#define RCM_LINK_RESOURCE_MAX (13 + LINKID_STR_WIDTH)
+
+/* Bridge Cache state flags */
+typedef enum {
+ CACHE_NODE_STALE = 0x1, /* stale cached data */
+ CACHE_NODE_NEW = 0x2, /* new cached nodes */
+ CACHE_NODE_OFFLINED = 0x4 /* nodes offlined */
+} cache_node_state_t;
+
+/* Network Cache lookup options */
+#define CACHE_NO_REFRESH 0x1 /* cache refresh not needed */
+#define CACHE_REFRESH 0x2 /* refresh cache */
+
+/* Cache element */
+typedef struct link_cache {
+ struct link_cache *vc_next; /* next cached resource */
+ struct link_cache *vc_prev; /* prev cached resource */
+ char *vc_resource; /* resource name */
+ datalink_id_t vc_linkid; /* linkid */
+ cache_node_state_t vc_state; /* cache state flags */
+ char vc_bridge[MAXLINKNAMELEN];
+} link_cache_t;
+
+/*
+ * Global cache for network Bridges
+ */
+static link_cache_t cache_head;
+static link_cache_t cache_tail;
+static mutex_t cache_lock;
+static boolean_t events_registered = B_FALSE;
+
+static dladm_handle_t dld_handle = NULL;
+
+/*
+ * RCM module interface prototypes
+ */
+static int bridge_register(rcm_handle_t *);
+static int bridge_unregister(rcm_handle_t *);
+static int bridge_get_info(rcm_handle_t *, char *, id_t, uint_t,
+ char **, char **, nvlist_t *, rcm_info_t **);
+static int bridge_suspend(rcm_handle_t *, char *, id_t,
+ timespec_t *, uint_t, char **, rcm_info_t **);
+static int bridge_resume(rcm_handle_t *, char *, id_t, uint_t,
+ char **, rcm_info_t **);
+static int bridge_offline(rcm_handle_t *, char *, id_t, uint_t,
+ char **, rcm_info_t **);
+static int bridge_undo_offline(rcm_handle_t *, char *, id_t,
+ uint_t, char **, rcm_info_t **);
+static int bridge_remove(rcm_handle_t *, char *, id_t, uint_t,
+ char **, rcm_info_t **);
+static int bridge_notify_event(rcm_handle_t *, char *, id_t,
+ uint_t, char **, nvlist_t *, rcm_info_t **);
+static int bridge_configure(rcm_handle_t *, datalink_id_t);
+
+/* Module private routines */
+static void cache_free(void);
+static int cache_update(rcm_handle_t *);
+static void cache_remove(link_cache_t *);
+static void node_free(link_cache_t *);
+static void cache_insert(link_cache_t *);
+static link_cache_t *cache_lookup(rcm_handle_t *, char *, uint_t);
+static char *bridge_usage(link_cache_t *);
+static void bridge_log_err(datalink_id_t, char **, char *);
+
+/* Module-Private data */
+static struct rcm_mod_ops bridge_ops =
+{
+ RCM_MOD_OPS_VERSION,
+ bridge_register,
+ bridge_unregister,
+ bridge_get_info,
+ bridge_suspend,
+ bridge_resume,
+ bridge_offline,
+ bridge_undo_offline,
+ bridge_remove,
+ NULL,
+ NULL,
+ bridge_notify_event
+};
+
+/*
+ * rcm_mod_init() - Update registrations, and return the ops structure.
+ */
+struct rcm_mod_ops *
+rcm_mod_init(void)
+{
+ dladm_status_t status;
+ char errmsg[DLADM_STRSIZE];
+
+ rcm_log_message(RCM_TRACE1, "Bridge: mod_init\n");
+
+ cache_head.vc_next = &cache_tail;
+ cache_head.vc_prev = NULL;
+ cache_tail.vc_prev = &cache_head;
+ cache_tail.vc_next = NULL;
+ (void) mutex_init(&cache_lock, 0, NULL);
+
+ if ((status = dladm_open(&dld_handle)) != DLADM_STATUS_OK) {
+ rcm_log_message(RCM_WARNING,
+ "Bridge: cannot open datalink handle: %s\n",
+ dladm_status2str(status, errmsg));
+ return (NULL);
+ }
+
+ /* Return the ops vectors */
+ return (&bridge_ops);
+}
+
+/*
+ * rcm_mod_info() - Return a string describing this module.
+ */
+const char *
+rcm_mod_info(void)
+{
+ rcm_log_message(RCM_TRACE1, "Bridge: mod_info\n");
+
+ return ("Bridge module version 1.0");
+}
+
+/*
+ * rcm_mod_fini() - Destroy the network Bridge cache.
+ */
+int
+rcm_mod_fini(void)
+{
+ rcm_log_message(RCM_TRACE1, "Bridge: mod_fini\n");
+
+ /*
+ * Note that bridge_unregister() does not seem to be called anywhere,
+ * therefore we free the cache nodes here. In theory we should call
+ * rcm_register_interest() for each node before we free it, but the
+ * framework does not provide the rcm_handle to allow us to do so.
+ */
+ cache_free();
+ (void) mutex_destroy(&cache_lock);
+
+ dladm_close(dld_handle);
+ return (RCM_SUCCESS);
+}
+
+/*
+ * bridge_register() - Make sure the cache is properly sync'ed, and its
+ * registrations are in order.
+ */
+static int
+bridge_register(rcm_handle_t *hd)
+{
+ int retv;
+
+ rcm_log_message(RCM_TRACE1, "Bridge: register\n");
+
+ if ((retv = cache_update(hd)) != RCM_SUCCESS)
+ return (retv);
+
+ /*
+ * Need to register interest in all new resources
+ * getting attached, so we get attach event notifications
+ */
+ if (!events_registered) {
+ retv = rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL);
+ if (retv != RCM_SUCCESS) {
+ rcm_log_message(RCM_ERROR,
+ _("Bridge: failed to register %s\n"),
+ RCM_RESOURCE_LINK_NEW);
+ } else {
+ rcm_log_message(RCM_DEBUG, "Bridge: registered %s\n",
+ RCM_RESOURCE_LINK_NEW);
+ events_registered = B_TRUE;
+ }
+ }
+
+ return (retv);
+}
+
+/*
+ * bridge_unregister() - Walk the cache, unregistering all the links.
+ */
+static int
+bridge_unregister(rcm_handle_t *hd)
+{
+ link_cache_t *node;
+ int retv = RCM_SUCCESS;
+
+ rcm_log_message(RCM_TRACE1, "Bridge: unregister\n");
+
+ /* Walk the cache, unregistering everything */
+ (void) mutex_lock(&cache_lock);
+ node = cache_head.vc_next;
+ while (node != &cache_tail) {
+ retv = rcm_unregister_interest(hd, node->vc_resource, 0);
+ if (retv != RCM_SUCCESS)
+ break;
+ cache_remove(node);
+ node_free(node);
+ node = cache_head.vc_next;
+ }
+ (void) mutex_unlock(&cache_lock);
+ if (retv != RCM_SUCCESS) {
+ rcm_log_message(RCM_ERROR,
+ _("Bridge: failed to unregister %s\n"), node->vc_resource);
+ return (retv);
+ }
+
+ /*
+ * Unregister interest in all new resources
+ */
+ if (events_registered) {
+ retv = rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0);
+ if (retv != RCM_SUCCESS) {
+ rcm_log_message(RCM_ERROR,
+ _("Bridge: failed to unregister %s\n"),
+ RCM_RESOURCE_LINK_NEW);
+ } else {
+ rcm_log_message(RCM_DEBUG, "Bridge: unregistered %s\n",
+ RCM_RESOURCE_LINK_NEW);
+ events_registered = B_FALSE;
+ }
+ }
+
+ return (retv);
+}
+
+/*
+ * bridge_offline() - Offline the bridge on a specific link.
+ */
+static int
+bridge_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
+ char **errorp, rcm_info_t **info)
+{
+ link_cache_t *node;
+ dladm_status_t status;
+
+ rcm_log_message(RCM_TRACE1, "Bridge: offline(%s)\n", rsrc);
+
+ /* Lock the cache and lookup the resource */
+ (void) mutex_lock(&cache_lock);
+ node = cache_lookup(hd, rsrc, CACHE_REFRESH);
+ if (node == NULL) {
+ /* should not happen because the resource is registered. */
+ bridge_log_err(DATALINK_INVALID_LINKID, errorp,
+ "unrecognized resource");
+ (void) mutex_unlock(&cache_lock);
+ return (RCM_SUCCESS);
+ }
+
+ /* Check if it's a query */
+ if (flags & RCM_QUERY) {
+ rcm_log_message(RCM_TRACE1,
+ "Bridge: offline query succeeded(%s)\n", rsrc);
+ (void) mutex_unlock(&cache_lock);
+ return (RCM_SUCCESS);
+ }
+
+ status = dladm_bridge_setlink(dld_handle, node->vc_linkid, "");
+ if (status != DLADM_STATUS_OK) {
+ bridge_log_err(node->vc_linkid, errorp, "offline failed");
+ (void) mutex_unlock(&cache_lock);
+ return (RCM_FAILURE);
+ }
+
+ node->vc_state |= CACHE_NODE_OFFLINED;
+
+ rcm_log_message(RCM_TRACE1, "Bridge: Offline succeeded(%s %s)\n", rsrc,
+ node->vc_bridge);
+ (void) mutex_unlock(&cache_lock);
+ return (RCM_SUCCESS);
+}
+
+/*
+ * bridge_undo_offline() - Undo offline of a previously offlined node.
+ */
+/*ARGSUSED*/
+static int
+bridge_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
+ char **errorp, rcm_info_t **info)
+{
+ link_cache_t *node;
+ dladm_status_t status;
+ char errmsg[DLADM_STRSIZE];
+
+ rcm_log_message(RCM_TRACE1, "Bridge: online(%s)\n", rsrc);
+
+ (void) mutex_lock(&cache_lock);
+ node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
+ if (node == NULL) {
+ bridge_log_err(DATALINK_INVALID_LINKID, errorp, "no such link");
+ (void) mutex_unlock(&cache_lock);
+ errno = ENOENT;
+ return (RCM_FAILURE);
+ }
+
+ /* Check if no attempt should be made to online the link here */
+ if (!(node->vc_state & CACHE_NODE_OFFLINED)) {
+ bridge_log_err(node->vc_linkid, errorp, "link not offlined");
+ (void) mutex_unlock(&cache_lock);
+ errno = ENOTSUP;
+ return (RCM_SUCCESS);
+ }
+
+ /*
+ * Try to bring on an offlined bridge link.
+ */
+ status = dladm_bridge_setlink(dld_handle, node->vc_linkid,
+ node->vc_bridge);
+ if (status != DLADM_STATUS_OK) {
+ /*
+ * Print a warning message.
+ */
+ rcm_log_message(RCM_WARNING,
+ _("Bridge: Bridge online failed %u %s: %s\n"),
+ node->vc_linkid, node->vc_bridge,
+ dladm_status2str(status, errmsg));
+ }
+
+ node->vc_state &= ~CACHE_NODE_OFFLINED;
+ rcm_log_message(RCM_TRACE1, "Bridge: online succeeded(%s)\n", rsrc);
+ (void) mutex_unlock(&cache_lock);
+ return (RCM_SUCCESS);
+}
+
+/*
+ * bridge_get_info() - Gather usage information for this resource.
+ */
+/*ARGSUSED*/
+int
+bridge_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
+ char **usagep, char **errorp, nvlist_t *props, rcm_info_t **info)
+{
+ link_cache_t *node;
+
+ rcm_log_message(RCM_TRACE1, "Bridge: get_info(%s)\n", rsrc);
+
+ (void) mutex_lock(&cache_lock);
+ node = cache_lookup(hd, rsrc, CACHE_REFRESH);
+ if (node == NULL) {
+ rcm_log_message(RCM_INFO,
+ _("Bridge: get_info(%s) unrecognized resource\n"), rsrc);
+ (void) mutex_unlock(&cache_lock);
+ errno = ENOENT;
+ return (RCM_FAILURE);
+ }
+
+ *usagep = bridge_usage(node);
+ (void) mutex_unlock(&cache_lock);
+ if (*usagep == NULL) {
+ /* most likely malloc failure */
+ rcm_log_message(RCM_ERROR,
+ _("Bridge: get_info(%s) malloc failure\n"), rsrc);
+ (void) mutex_unlock(&cache_lock);
+ errno = ENOMEM;
+ return (RCM_FAILURE);
+ }
+
+ /* Set client/role properties */
+ (void) nvlist_add_string(props, RCM_CLIENT_NAME, "Bridge");
+
+ rcm_log_message(RCM_TRACE1, "Bridge: get_info(%s) info = %s\n",
+ rsrc, *usagep);
+ return (RCM_SUCCESS);
+}
+
+/*
+ * bridge_suspend() - Nothing to do, always okay
+ */
+/*ARGSUSED*/
+static int
+bridge_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval,
+ uint_t flags, char **errorp, rcm_info_t **info)
+{
+ rcm_log_message(RCM_TRACE1, "Bridge: suspend(%s)\n", rsrc);
+ return (RCM_SUCCESS);
+}
+
+/*
+ * bridge_resume() - Nothing to do, always okay
+ */
+/*ARGSUSED*/
+static int
+bridge_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
+ char **errorp, rcm_info_t **info)
+{
+ rcm_log_message(RCM_TRACE1, "Bridge: resume(%s)\n", rsrc);
+ return (RCM_SUCCESS);
+}
+
+/*
+ * bridge_remove() - remove a resource from cache
+ */
+/*ARGSUSED*/
+static int
+bridge_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
+ char **errorp, rcm_info_t **info)
+{
+ link_cache_t *node;
+
+ rcm_log_message(RCM_TRACE1, "Bridge: remove(%s)\n", rsrc);
+
+ (void) mutex_lock(&cache_lock);
+ node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
+ if (node == NULL) {
+ rcm_log_message(RCM_INFO,
+ _("Bridge: remove(%s) unrecognized resource\n"), rsrc);
+ (void) mutex_unlock(&cache_lock);
+ errno = ENOENT;
+ return (RCM_FAILURE);
+ }
+
+ /* remove the cached entry for the resource */
+ rcm_log_message(RCM_TRACE2,
+ "Bridge: remove succeeded(%s, %s)\n", rsrc, node->vc_bridge);
+ cache_remove(node);
+ (void) mutex_unlock(&cache_lock);
+
+ node_free(node);
+ return (RCM_SUCCESS);
+}
+
+/*
+ * bridge_notify_event - Project private implementation to receive new resource
+ * events. It intercepts all new resource events. If the
+ * new resource is a network resource, pass up a notify
+ * for it too. The new resource need not be cached, since
+ * it is done at register again.
+ */
+/*ARGSUSED*/
+static int
+bridge_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
+ char **errorp, nvlist_t *nvl, rcm_info_t **info)
+{
+ nvpair_t *nvp = NULL;
+ datalink_id_t linkid;
+ uint64_t id64;
+ int rv, lastrv;
+
+ rcm_log_message(RCM_TRACE1, "Bridge: notify_event(%s)\n", rsrc);
+
+ if (strcmp(rsrc, RCM_RESOURCE_LINK_NEW) != 0) {
+ bridge_log_err(DATALINK_INVALID_LINKID, errorp,
+ "unrecognized event");
+ errno = EINVAL;
+ return (RCM_FAILURE);
+ }
+
+ /* Update cache to reflect latest Bridges */
+ if ((lastrv = cache_update(hd)) != RCM_SUCCESS) {
+ bridge_log_err(DATALINK_INVALID_LINKID, errorp,
+ "private Cache update failed");
+ return (lastrv);
+ }
+
+ /*
+ * Try best to recover all configuration.
+ */
+ rcm_log_message(RCM_DEBUG, "Bridge: process_nvlist\n");
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) != 0)
+ continue;
+
+ if (nvpair_value_uint64(nvp, &id64) != 0) {
+ bridge_log_err(DATALINK_INVALID_LINKID, errorp,
+ "cannot get linkid");
+ lastrv = RCM_FAILURE;
+ continue;
+ }
+
+ linkid = (datalink_id_t)id64;
+ if ((rv = bridge_configure(hd, linkid)) != RCM_SUCCESS) {
+ bridge_log_err(linkid, errorp, "configuring failed");
+ lastrv = rv;
+ }
+ }
+
+ rcm_log_message(RCM_TRACE1,
+ "Bridge: notify_event: link configuration complete\n");
+ return (lastrv);
+}
+
+/*
+ * bridge_usage - Determine the usage of a link.
+ * The returned buffer is owned by caller, and the caller
+ * must free it up when done.
+ */
+static char *
+bridge_usage(link_cache_t *node)
+{
+ char *buf;
+ const char *fmt;
+ char errmsg[DLADM_STRSIZE];
+ char name[MAXLINKNAMELEN];
+ char bridge[MAXLINKNAMELEN];
+ dladm_status_t status;
+
+ rcm_log_message(RCM_TRACE2, "Bridge: usage(%s)\n", node->vc_resource);
+
+ assert(MUTEX_HELD(&cache_lock));
+
+ status = dladm_datalink_id2info(dld_handle, node->vc_linkid, NULL,
+ NULL, NULL, name, sizeof (name));
+
+ if (status != DLADM_STATUS_OK) {
+ rcm_log_message(RCM_ERROR,
+ _("Bridge: usage(%s) get link name failure(%s)\n"),
+ node->vc_resource, dladm_status2str(status, errmsg));
+ return (NULL);
+ }
+
+ (void) dladm_bridge_getlink(dld_handle, node->vc_linkid, bridge,
+ sizeof (bridge));
+
+ if (node->vc_state & CACHE_NODE_OFFLINED)
+ fmt = _("%1$s offlined");
+ else if (bridge[0] == '\0')
+ fmt = _("%1$s not bridged");
+ else
+ fmt = _("%1$s bridge: %2$s");
+
+ (void) asprintf(&buf, fmt, name, bridge);
+
+ rcm_log_message(RCM_TRACE2, "Bridge: usage (%s) info = %s\n",
+ node->vc_resource, buf);
+
+ return (buf);
+}
+
+/*
+ * Cache management routines, all cache management functions should be
+ * be called with cache_lock held.
+ */
+
+/*
+ * cache_lookup() - Get a cache node for a resource.
+ * Call with cache lock held.
+ *
+ * This ensures that the cache is consistent with the system state and
+ * returns a pointer to the cache element corresponding to the resource.
+ */
+static link_cache_t *
+cache_lookup(rcm_handle_t *hd, char *rsrc, uint_t options)
+{
+ link_cache_t *node;
+
+ rcm_log_message(RCM_TRACE2, "Bridge: cache lookup(%s)\n", rsrc);
+
+ assert(MUTEX_HELD(&cache_lock));
+ if (options & CACHE_REFRESH) {
+ /* drop lock since update locks cache again */
+ (void) mutex_unlock(&cache_lock);
+ (void) cache_update(hd);
+ (void) mutex_lock(&cache_lock);
+ }
+
+ node = cache_head.vc_next;
+ for (; node != &cache_tail; node = node->vc_next) {
+ if (strcmp(rsrc, node->vc_resource) == 0) {
+ rcm_log_message(RCM_TRACE2,
+ "Bridge: cache lookup succeeded(%s, %s)\n", rsrc,
+ node->vc_bridge);
+ return (node);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * node_free - Free a node from the cache
+ */
+static void
+node_free(link_cache_t *node)
+{
+ if (node != NULL) {
+ free(node->vc_resource);
+ free(node);
+ }
+}
+
+/*
+ * cache_insert - Insert a resource node in cache
+ */
+static void
+cache_insert(link_cache_t *node)
+{
+ assert(MUTEX_HELD(&cache_lock));
+
+ /* insert at the head for best performance */
+ node->vc_next = cache_head.vc_next;
+ node->vc_prev = &cache_head;
+
+ node->vc_next->vc_prev = node;
+ node->vc_prev->vc_next = node;
+}
+
+/*
+ * cache_remove() - Remove a resource node from cache.
+ */
+static void
+cache_remove(link_cache_t *node)
+{
+ assert(MUTEX_HELD(&cache_lock));
+ node->vc_next->vc_prev = node->vc_prev;
+ node->vc_prev->vc_next = node->vc_next;
+ node->vc_next = NULL;
+ node->vc_prev = NULL;
+}
+
+typedef struct bridge_update_arg_s {
+ rcm_handle_t *hd;
+ int retval;
+} bridge_update_arg_t;
+
+/*
+ * bridge_update() - Update physical interface properties
+ */
+static int
+bridge_update(dladm_handle_t handle, datalink_id_t linkid, void *arg)
+{
+ bridge_update_arg_t *bua = arg;
+ rcm_handle_t *hd = bua->hd;
+ link_cache_t *node;
+ char *rsrc;
+ dladm_status_t status;
+ char errmsg[DLADM_STRSIZE];
+ char bridge[MAXLINKNAMELEN];
+ int ret = RCM_FAILURE;
+
+ rcm_log_message(RCM_TRACE2, "Bridge: bridge_update(%u)\n", linkid);
+
+ assert(MUTEX_HELD(&cache_lock));
+ status = dladm_bridge_getlink(dld_handle, linkid, bridge,
+ sizeof (bridge));
+ if (status != DLADM_STATUS_OK) {
+ rcm_log_message(RCM_TRACE1,
+ "Bridge: no bridge information for %u (%s)\n",
+ linkid, dladm_status2str(status, errmsg));
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ (void) asprintf(&rsrc, "%s/%u", RCM_LINK_PREFIX, linkid);
+ if (rsrc == NULL) {
+ rcm_log_message(RCM_ERROR,
+ _("Bridge: allocation failure: %s %u: %s\n"),
+ bridge, linkid, strerror(errno));
+ goto done;
+ }
+
+ node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
+ if (node != NULL) {
+ rcm_log_message(RCM_DEBUG, "Bridge: %s already registered\n",
+ rsrc);
+ free(rsrc);
+ node->vc_state &= ~CACHE_NODE_STALE;
+ } else {
+ rcm_log_message(RCM_DEBUG,
+ "Bridge: %s is a new resource (bridge %s)\n",
+ rsrc, bridge);
+ if ((node = calloc(1, sizeof (link_cache_t))) == NULL) {
+ free(rsrc);
+ rcm_log_message(RCM_ERROR, _("Bridge: calloc: %s\n"),
+ strerror(errno));
+ goto done;
+ }
+
+ node->vc_resource = rsrc;
+ node->vc_linkid = linkid;
+ (void) strlcpy(node->vc_bridge, bridge,
+ sizeof (node->vc_bridge));
+ node->vc_state |= CACHE_NODE_NEW;
+ cache_insert(node);
+ }
+
+ rcm_log_message(RCM_TRACE3, "Bridge: bridge_update: succeeded(%u %s)\n",
+ linkid, node->vc_bridge);
+ ret = RCM_SUCCESS;
+done:
+ bua->retval = ret;
+ return (ret == RCM_SUCCESS ? DLADM_WALK_CONTINUE :
+ DLADM_WALK_TERMINATE);
+}
+
+/*
+ * cache_update() - Update cache with latest interface info
+ */
+static int
+cache_update(rcm_handle_t *hd)
+{
+ link_cache_t *node, *nnode;
+ int rv, lastrv;
+ bridge_update_arg_t bua;
+
+ rcm_log_message(RCM_TRACE2, "Bridge: cache_update\n");
+
+ (void) mutex_lock(&cache_lock);
+
+ /* first we walk the entire cache, marking each entry stale */
+ node = cache_head.vc_next;
+ for (; node != &cache_tail; node = node->vc_next)
+ node->vc_state |= CACHE_NODE_STALE;
+
+ /* now walk the links and update all of the entries */
+ bua.hd = hd;
+ bua.retval = RCM_SUCCESS;
+ (void) dladm_walk_datalink_id(bridge_update, dld_handle, &bua,
+ DATALINK_CLASS_AGGR | DATALINK_CLASS_PHYS |
+ DATALINK_CLASS_ETHERSTUB, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ lastrv = bua.retval;
+
+ /*
+ * Continue to delete all stale nodes from the cache even if the walk
+ * above failed. Unregister links that are not offlined and still in
+ * the cache.
+ */
+ for (node = cache_head.vc_next; node != &cache_tail; node = nnode) {
+ nnode = node->vc_next;
+
+ if (node->vc_state & CACHE_NODE_STALE) {
+ (void) rcm_unregister_interest(hd, node->vc_resource,
+ 0);
+ rcm_log_message(RCM_DEBUG,
+ "Bridge: unregistered %s %s\n",
+ node->vc_resource, node->vc_bridge);
+ cache_remove(node);
+ node_free(node);
+ continue;
+ }
+
+ if (!(node->vc_state & CACHE_NODE_NEW))
+ continue;
+
+ rv = rcm_register_interest(hd, node->vc_resource, 0, NULL);
+ if (rv != RCM_SUCCESS) {
+ rcm_log_message(RCM_ERROR,
+ _("Bridge: failed to register %s\n"),
+ node->vc_resource);
+ lastrv = rv;
+ } else {
+ rcm_log_message(RCM_DEBUG, "Bridge: registered %s\n",
+ node->vc_resource);
+ node->vc_state &= ~CACHE_NODE_NEW;
+ }
+ }
+
+ (void) mutex_unlock(&cache_lock);
+ return (lastrv);
+}
+
+/*
+ * cache_free() - Empty the cache
+ */
+static void
+cache_free(void)
+{
+ link_cache_t *node;
+
+ rcm_log_message(RCM_TRACE2, "Bridge: cache_free\n");
+
+ (void) mutex_lock(&cache_lock);
+ node = cache_head.vc_next;
+ while (node != &cache_tail) {
+ cache_remove(node);
+ node_free(node);
+ node = cache_head.vc_next;
+ }
+ (void) mutex_unlock(&cache_lock);
+}
+
+/*
+ * bridge_log_err() - RCM error log wrapper
+ */
+static void
+bridge_log_err(datalink_id_t linkid, char **errorp, char *errmsg)
+{
+ char link[MAXLINKNAMELEN];
+ char errstr[DLADM_STRSIZE];
+ dladm_status_t status;
+ char *error;
+
+ link[0] = '\0';
+ if (linkid != DATALINK_INVALID_LINKID) {
+ char rsrc[RCM_LINK_RESOURCE_MAX];
+
+ (void) snprintf(rsrc, sizeof (rsrc), "%s/%u",
+ RCM_LINK_PREFIX, linkid);
+
+ rcm_log_message(RCM_ERROR, _("Bridge: %s(%s)\n"), errmsg, rsrc);
+ if ((status = dladm_datalink_id2info(dld_handle, linkid, NULL,
+ NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
+ rcm_log_message(RCM_WARNING,
+ _("Bridge: cannot get link name for (%s) %s\n"),
+ rsrc, dladm_status2str(status, errstr));
+ }
+ } else {
+ rcm_log_message(RCM_ERROR, _("Bridge: %s\n"), errmsg);
+ }
+
+ if (link[0] != '\0')
+ (void) asprintf(&error, _("Bridge: %s(%s)"), errmsg, link);
+ else
+ (void) asprintf(&error, _("Bridge: %s"), errmsg);
+
+ if (errorp != NULL)
+ *errorp = error;
+}
+
+/*
+ * bridge_configure() - Configure bridge on a physical link after it attaches
+ */
+static int
+bridge_configure(rcm_handle_t *hd, datalink_id_t linkid)
+{
+ char rsrc[RCM_LINK_RESOURCE_MAX];
+ link_cache_t *node;
+ char bridge[MAXLINKNAMELEN];
+
+ /* Check for the bridge links in the cache */
+ (void) snprintf(rsrc, sizeof (rsrc), "%s/%u", RCM_LINK_PREFIX, linkid);
+
+ rcm_log_message(RCM_TRACE2, "Bridge: bridge_configure(%s)\n", rsrc);
+
+ /* Check if the link is new or was previously offlined */
+ (void) mutex_lock(&cache_lock);
+ if (((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) != NULL) &&
+ (!(node->vc_state & CACHE_NODE_OFFLINED))) {
+ rcm_log_message(RCM_TRACE2,
+ "Bridge: Skipping configured interface(%s)\n", rsrc);
+ (void) mutex_unlock(&cache_lock);
+ return (RCM_SUCCESS);
+ }
+ (void) mutex_unlock(&cache_lock);
+
+ /* clear out previous bridge, if any */
+ if (dladm_bridge_getlink(dld_handle, linkid, bridge, sizeof (bridge)) ==
+ DLADM_STATUS_OK) {
+ if (bridge[0] != '\0')
+ (void) dladm_bridge_setlink(dld_handle, linkid, "");
+ }
+
+ /* now set up the new one */
+ if (node != NULL && node->vc_bridge[0] != '\0' &&
+ dladm_bridge_setlink(dld_handle, linkid, node->vc_bridge) !=
+ DLADM_STATUS_OK)
+ return (RCM_FAILURE);
+ else
+ return (RCM_SUCCESS);
+}