/*
* 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);
}