aggr_rcm.c revision d4d1f7bf736e9deaf99b53c54fc31cecd3a9e435
/*
* 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
* 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 AGGR links
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <alloca.h>
#include <synch.h>
#include <assert.h>
#include <strings.h>
#include "rcm_module.h"
#include <libintl.h>
#include <libdllink.h>
#include <libdlaggr.h>
/*
* Definitions
*/
#ifndef lint
#define _(x) gettext(x)
#else
#define _(x) x
#endif
/* Some generic well-knowns and defaults used in this module */
/* AGGR link representation */
typedef struct dl_aggr {
} dl_aggr_t;
/* AGGR Cache state flags */
typedef enum {
/* Network Cache lookup options */
/*
* Cache element. It is used to keep a list of links on the system and
* their associated aggregations.
*/
typedef struct link_cache {
char *vc_resource; /* resource name */
} link_cache_t;
/*
* Global cache for network AGGRs
*/
static link_cache_t cache_head;
static link_cache_t cache_tail;
static mutex_t cache_lock;
static mutex_t aggr_list_lock;
static int events_registered = 0;
/*
* RCM module interface prototypes
*/
static int aggr_register(rcm_handle_t *);
static int aggr_unregister(rcm_handle_t *);
char **, char **, nvlist_t *, rcm_info_t **);
char **, rcm_info_t **);
char **, rcm_info_t **);
char **, rcm_info_t **);
char **, rcm_info_t **);
char **, nvlist_t *, rcm_info_t **);
boolean_t *);
/* Module private routines */
static int cache_update(rcm_handle_t *);
static void cache_remove(link_cache_t *);
static void cache_insert(link_cache_t *);
static void node_free(link_cache_t *);
static void aggr_list_remove(dl_aggr_t *);
static void aggr_list_insert(dl_aggr_t *);
static void aggr_list_free();
char **, uint_t, rcm_info_t **);
char **, uint_t, rcm_info_t **);
static char *aggr_usage(link_cache_t *);
static void aggr_log_err(datalink_id_t, char **, char *);
char **, uint_t, rcm_info_t **);
/* Module-Private data */
static struct rcm_mod_ops aggr_ops =
{
NULL,
NULL,
};
/*
* rcm_mod_init() - Update registrations, and return the ops structure.
*/
struct rcm_mod_ops *
rcm_mod_init(void)
{
char errmsg[DLADM_STRSIZE];
"AGGR: mod_init failed: cannot open datalink handle: %s\n",
return (NULL);
}
/* Return the ops vectors */
return (&aggr_ops);
}
/*
* rcm_mod_info() - Return a string describing this module.
*/
const char *
rcm_mod_info(void)
{
return ("AGGR module version 1.1");
}
/*
* rcm_mod_fini() - Destroy the network AGGR cache.
*/
int
rcm_mod_fini(void)
{
/*
* Note that aggr_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, the
* framework does not provide the rcm_handle to allow us to do so.
*/
(void) mutex_lock(&cache_lock);
while (node != &cache_tail) {
}
(void) mutex_unlock(&cache_lock);
(void) mutex_destroy(&cache_lock);
(void) mutex_destroy(&aggr_list_lock);
return (RCM_SUCCESS);
}
/*
* aggr_list_insert - Insert an aggr in the global aggr list
*/
static void
{
/* insert at the head for best performance */
}
/*
* aggr_list_remove - Remove an aggr from the global aggr list
*/
static void
{
}
static void
{
(void) mutex_lock(&aggr_list_lock);
}
(void) mutex_unlock(&aggr_list_lock);
}
/*
* aggr_register() - Make sure the cache is properly sync'ed, and its
* registrations are in order.
*/
static int
{
if (cache_update(hd) < 0)
return (RCM_FAILURE);
/*
* Need to register interest in all new resources
* getting attached, so we get attach event notifications
*/
if (!events_registered) {
!= RCM_SUCCESS) {
_("AGGR: failed to register %s\n"),
return (RCM_FAILURE);
} else {
}
}
return (RCM_SUCCESS);
}
/*
* aggr_unregister() - Walk the cache, unregistering all the networks.
*/
static int
{
/* Walk the cache, unregistering everything */
(void) mutex_lock(&cache_lock);
while (node != &cache_tail) {
!= RCM_SUCCESS) {
/* unregister failed for whatever reason */
_("AGGR: failed to unregister %s\n"),
node->vc_resource);
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
}
(void) mutex_unlock(&cache_lock);
/*
* Unregister interest in all new resources
*/
if (events_registered) {
!= RCM_SUCCESS) {
_("AGGR: failed to unregister %s\n"),
return (RCM_FAILURE);
} else {
}
}
return (RCM_SUCCESS);
}
/*
* aggr_offline() - Offline AGGRs on a specific link.
*/
static int
{
/* Lock the cache and lookup the resource */
(void) mutex_lock(&cache_lock);
/* should not happen because the resource is registered. */
"offline, unrecognized resource");
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*
* If this given link is the only port in the aggregation, inform
* VLANs and IP interfaces on associated AGGRs to be offlined
*/
depend_info) == RCM_SUCCESS) {
"AGGR: consumers agreed on offline\n");
} else {
"consumers offline failed");
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
}
/* Check if it's a query */
"AGGR: offline query succeeded(%s)\n", rsrc);
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*
* aggr_undo_offline() - Undo offline of a previously offlined link.
*/
/*ARGSUSED*/
static int
{
(void) mutex_lock(&cache_lock);
"undo offline, unrecognized resource");
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/* Check if no attempt should be made to online the link here */
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/*
* Inform VLANs and IP interfaces on associated AGGRs to be online
*/
if (!up)
goto done;
RCM_SUCCESS) {
} else {
_("AGGR: Consumers online failed (%s)\n"), rsrc);
}
done:
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
static int
{
char errmsg[DLADM_STRSIZE];
node->vc_resource);
/*
* Try to remove the given port from the AGGR or delete the AGGR
*/
} else {
"AGGR: remove port (%s) from aggregation %u\n",
&port, DLADM_OPT_ACTIVE);
}
if (status != DLADM_STATUS_OK) {
_("AGGR: AGGR offline port failed (%u): %s\n"),
return (RCM_FAILURE);
} else {
"AGGR: AGGR offline port succeeded (%u)\n",
return (RCM_SUCCESS);
}
}
static int
{
char errmsg[DLADM_STRSIZE];
node->vc_resource);
return (RCM_SUCCESS);
/*
* Either add the port into the AGGR or recreate specific AGGR
* depending on whether this link is the only port in the aggregation.
*/
} else {
"AGGR: add port (%s) to aggregation %u\n",
}
if (status != DLADM_STATUS_OK) {
_("AGGR: AGGR online failed (%u): %s\n"),
return (RCM_FAILURE);
}
return (RCM_SUCCESS);
}
/*
* aggr_get_info() - Gather usage information for this resource.
*/
/*ARGSUSED*/
int
{
(void) mutex_lock(&cache_lock);
_("AGGR: get_info(%s) unrecognized resource\n"), rsrc);
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/*
* *usagep will be freed by the caller.
*/
(void) mutex_unlock(&cache_lock);
/* most likely malloc failure */
_("AGGR: get_info(%s) malloc failure\n"), rsrc);
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
return (RCM_SUCCESS);
}
/*
* aggr_suspend() - Nothing to do, always okay
*/
/*ARGSUSED*/
static int
{
return (RCM_SUCCESS);
}
/*
* aggr_resume() - Nothing to do, always okay
*/
/*ARGSUSED*/
static int
{
return (RCM_SUCCESS);
}
/*
* aggr_remove() - remove a resource from cache
*/
/*ARGSUSED*/
static int
{
char *exported;
int rv = RCM_SUCCESS;
(void) mutex_lock(&cache_lock);
_("AGGR: remove(%s) unrecognized resource\n"), rsrc);
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/* remove the cached entry for the resource */
(void) mutex_unlock(&cache_lock);
/*
* If this link is not the only port in the associated aggregation,
* the CACHE_AGGR_CONSUMER_OFFLINED flags won't be set.
*/
if (rv != RCM_SUCCESS) {
_("AGGR: failed to notify remove dependent %s\n"),
exported);
}
}
return (rv);
}
/*
* aggr_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
{
int rv = RCM_SUCCESS;
"unrecognized event");
return (RCM_FAILURE);
}
/* Update cache to reflect latest AGGRs */
if (cache_update(hd) < 0) {
"private Cache update failed");
return (RCM_FAILURE);
}
/* Process the nvlist for the event */
continue;
"cannot get linkid");
return (RCM_FAILURE);
}
"failed configuring AGGR links");
rv = RCM_FAILURE;
}
/* Notify all VLAN and IP AGGR consumers */
depend_info) != 0) {
rv = RCM_FAILURE;
}
}
"AGGR: notify_event: link configuration complete\n");
return (rv);
}
/*
* aggr_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 *
{
char *buf;
const char *fmt;
char errmsg[DLADM_STRSIZE];
char name[MAXLINKNAMELEN];
fmt = _("%s offlined");
else
fmt = _("%s is part of AGGR ");
_("AGGR: usage(%s) get port name failure(%s)\n"),
return (NULL);
}
/* space for resources and message */
_("AGGR: usage(%s) malloc failure(%s)\n"),
return (NULL);
}
/* Nothing else to do */
return (buf);
}
sizeof (name))) != DLADM_STATUS_OK) {
_("AGGR: usage(%s) get aggr %u name failure(%s)\n"),
return (NULL);
}
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 *
{
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);
}
"AGGR: cache lookup succeeded(%s)\n", rsrc);
return (node);
}
}
return (NULL);
}
/*
* node_free - Free a node from the cache
*/
static void
{
}
/*
* cache_insert - Insert a resource node in cache
*/
static void
{
/* insert at the head for best performance */
}
/*
* cache_remove() - Remove a resource node from cache.
* Call with the cache_lock held.
*/
static void
{
}
static int
{
char *rsrc;
int ret = -1;
"AGGR: aggr_port_update aggr:%u port:%u\n",
goto done;
}
"AGGR: %s already registered (aggrid:%u)\n",
/*
* Update vc_aggr directly as only one aggregation can be
* created on one port.
*/
} else {
"AGGR: %s is a new resource (aggrid:%u)\n",
return (ret);
}
}
ret = 0;
done:
return (ret);
}
typedef struct aggr_update_arg_s {
int retval;
/*
* aggr_update() - Update physical interface properties
*/
static int
{
char errmsg[DLADM_STRSIZE];
uint32_t i;
int ret = -1;
if (status != DLADM_STATUS_OK) {
"AGGR: cannot get aggr information for %u error(%s)\n",
return (DLADM_WALK_CONTINUE);
}
/*
* Try to find the aggr from the aggr list.
*/
break;
} else {
goto done;
}
}
/* Update aggregation information. */
else
goto done;
}
if (!exist)
"AGGR: aggr_update: succeeded(%u)\n", aggrid);
ret = 0;
done:
}
/*
* aggr_update_all() - Determine all AGGR links in the system
*/
static int
{
}
/*
* cache_update() - Update cache with latest interface info
*/
static int
{
int ret = 0;
(void) mutex_lock(&aggr_list_lock);
(void) mutex_lock(&cache_lock);
/* first we walk the entire aggr list, marking each entry stale */
/* then we walk the entire cache, marking each entry stale */
/*
* Even aggr_update_all() fails, continue to delete all the stale
* resources. First, unregister links that are not offlined and
* still in cache.
*/
0);
continue;
}
continue;
NULL) != RCM_SUCCESS) {
_("AGGR: failed to register %s\n"),
node->vc_resource);
ret = -1;
} else {
node->vc_resource);
}
}
/* delete stale AGGRs */
}
}
done:
(void) mutex_unlock(&cache_lock);
(void) mutex_unlock(&aggr_list_lock);
return (ret);
}
/*
* aggr_log_err() - RCM error log wrapper
*/
static void
{
char link[MAXLINKNAMELEN];
char errstr[DLADM_STRSIZE];
int len;
const char *errfmt;
char *error;
link[0] = '\0';
if (linkid != DATALINK_INVALID_LINKID) {
char rsrc[RCM_LINK_RESOURCE_MAX];
_("AGGR: cannot get link name of (%s) %s\n"),
}
} else {
}
else
}
}
/*
* aggr_consumer_offline()
*
* Offline AGGR consumers.
*/
static int
{
char rsrc[RCM_LINK_RESOURCE_MAX];
int ret;
node->vc_resource);
/*
* Inform associated VLANs and IP interfaces to be offlined
*/
if (ret != RCM_SUCCESS) {
"AGGR: rcm_request_offline failed (%s)\n", rsrc);
return (ret);
}
return (ret);
}
/*
* aggr_consumer_online()
*
* online AGGR consumers.
*/
static int
{
char rsrc[RCM_LINK_RESOURCE_MAX];
int ret;
node->vc_resource);
return (RCM_SUCCESS);
}
if (ret != RCM_SUCCESS) {
"AGGR: rcm_notify_online failed (%s)\n", rsrc);
return (ret);
}
return (ret);
}
/*
* Send RCM_RESOURCE_LINK_NEW events to other modules about new aggregations.
* Return 0 on success, -1 on failure.
*/
static int
{
int ret = -1;
/* Check for the interface in the cache */
(void) mutex_lock(&cache_lock);
"AGGR: aggr_notify_new_aggr() unrecognized resource (%s)\n",
rsrc);
(void) mutex_unlock(&cache_lock);
return (0);
}
if (nvlist_alloc(&nvl, 0, 0) != 0) {
_("AGGR: failed to allocate nvlist\n"));
(void) mutex_unlock(&cache_lock);
goto done;
}
if (is_only_port) {
"AGGR: aggr_notify_new_aggr add (%u)\n",
_("AGGR: failed to construct nvlist\n"));
(void) mutex_unlock(&cache_lock);
goto done;
}
}
(void) mutex_unlock(&cache_lock);
/*
* If this link is not the only port in the aggregation, the aggregation
* is not new. No need to inform other consumers in that case.
*/
_("AGGR: failed to notify %s event for %s\n"),
goto done;
}
ret = 0;
done:
return (ret);
}
/*
* aggr_consumer_notify() - Notify consumers of AGGRs coming back online.
*/
static int
{
char rsrc[RCM_LINK_RESOURCE_MAX];
/*
* Inform IP and VLAN consumers to be online.
*/
(void) mutex_lock(&cache_lock);
(void) mutex_unlock(&cache_lock);
"AGGR: aggr_notify_new_aggr failed(%s)\n", rsrc);
return (-1);
}
return (0);
}
typedef struct aggr_configure_arg {
int retval;
static int
{
char errmsg[DLADM_STRSIZE];
int i;
NULL, 0);
if (status != DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
if (status != DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
break;
/*
* The aggregation doesn't contain this port.
*/
return (DLADM_WALK_CONTINUE);
}
/*
* If this aggregation already exists, add this port to this
* aggregation, otherwise, bring up this aggregation.
*/
if (flags & DLADM_OPT_ACTIVE) {
"AGGR: aggr_configure dladm_aggr_add port %u (%u)\n",
} else {
"AGGR: aggr_configure dladm_aggr_up (%u)\n", aggrid);
}
if (status != DLADM_STATUS_OK) {
/*
* Print a warning message and continue to UP other AGGRs.
*/
_("AGGR: AGGR online failed (%u): %s\n"),
} else if (!(flags & DLADM_OPT_ACTIVE)) {
}
return (DLADM_WALK_TERMINATE);
}
/*
* aggr_configure_all() - Configure AGGRs over a physical link after it attaches
*/
static int
{
char rsrc[RCM_LINK_RESOURCE_MAX];
/* Check for the AGGRs in the cache */
/* Check if the link is new or was previously offlined */
(void) mutex_lock(&cache_lock);
"AGGR: Skipping configured link(%s)\n", rsrc);
(void) mutex_unlock(&cache_lock);
return (0);
}
(void) mutex_unlock(&cache_lock);
"AGGR: aggr_configure_all succeeded(%s)\n", rsrc);
}
}