aggr_grp.c revision c615009f11c436137940dc0ca4ec7691781c8c67
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* IEEE 802.3ad Link Aggregation -- Link Aggregation Groups.
*
* An instance of the structure aggr_grp_t is allocated for each
* link aggregation group. When created, aggr_grp_t objects are
* entered into the aggr_grp_hash hash table maintained by the modhash
* module. The hash key is the port number associated with the link
* aggregation group. The port number associated with a group corresponds
* the key associated with the group.
*
* A set of MAC ports are associated with each association group.
*/
#include <sys/sysmacros.h>
#include <sys/aggr_impl.h>
static void aggr_m_info(void *, mac_info_t *);
static int aggr_m_start(void *);
static void aggr_m_stop(void *);
static int aggr_m_promisc(void *, boolean_t);
static int aggr_m_unicst(void *, const uint8_t *);
static void aggr_m_resources(void *);
boolean_t *);
static void aggr_grp_capab_set(aggr_grp_t *);
static kmem_cache_t *aggr_grp_cache;
static mod_hash_t *aggr_grp_hash;
static krwlock_t aggr_grp_lock;
static uint_t aggr_grp_cnt;
#define GRP_HASHSZ 64
static uchar_t aggr_zero_mac[] = {0, 0, 0, 0, 0, 0};
/* used by grp_info_walker */
typedef struct aggr_grp_info_state {
void *ls_fn_arg;
int ls_rc;
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static void
{
}
}
void
aggr_grp_init(void)
{
sizeof (aggr_grp_t), 0, aggr_grp_constructor,
aggr_grp_cnt = 0;
}
void
aggr_grp_fini(void)
{
}
aggr_grp_count(void)
{
return (count);
}
/*
* Attach a port to a link aggregation group.
*
* A port is attached to a link aggregation group once its speed
* and link state have been verified.
*
* Returns B_TRUE if the group link state or speed has changed. If
* it's the case, the caller must notify the MAC layer via a call
* to mac_link().
*/
{
return (B_FALSE);
/*
* Validate the MAC port link speed and update the group
* link speed if needed.
*/
if (port->lp_ifspeed == 0 ||
/*
* Can't attach a MAC port with unknown link speed,
* down link, or not in full duplex mode.
*/
return (B_FALSE);
}
if (grp->lg_ifspeed == 0) {
/*
* The group inherits the speed of the first link being
* attached.
*/
/*
* The link speed of the MAC port must be the same as
* the group link speed, as per 802.3ad. Since it is
* not, the attach is cancelled.
*/
return (B_FALSE);
}
/*
* Update the group link state.
*/
}
/*
* Update port's state.
*/
/*
* Set port's receive callback
*/
/*
* If LACP is OFF, the port can be used to send data as soon
* as its link is up and verified to be compatible with the
* aggregation.
*
* If LACP is active or passive, notify the LACP subsystem, which
* will enable sending on the port following the LACP protocol.
*/
else
return (link_state_changed);
}
{
/* update state */
return (B_FALSE);
else
if (grp->lg_nattached_ports == 0) {
/* the last attached MAC port of the group is being detached */
grp->lg_ifspeed = 0;
}
return (link_state_changed);
}
/*
* Update the MAC addresses of the constituent ports of the specified
* group. This function is invoked:
* - after creating a new aggregation group.
* - after adding new ports to an aggregation group.
* - after removing a port from a group when the MAC address of
* that port was used for the MAC address of the group.
* - after the MAC address of a port changed when the MAC address
* of that port was used for the MAC address of the group.
*
* Return true if the link state of the aggregation changed, for example
* as a result of a failure changing the MAC address of one of the
* constituent ports.
*/
{
if (grp->lg_closing)
return (link_state_changed);
} else {
/*
* If a port was detached because of a previous
* failure changing the MAC address, the port is
* reattached when it successfully changes the MAC
* address now, and this might cause the link state
* of the aggregation to change.
*/
}
}
return (link_state_changed);
}
/*
* Invoked when the MAC address of a port has changed. If the port's
* MAC address was used for the group MAC address, set mac_addr_changedp
* to B_TRUE to indicate to the caller that it should send a MAC_NOTE_UNICST
* the constituent port, set link_state_changedp to B_TRUE to indicate
* to the caller that it should send a MAC_NOTE_LINK notification. In both
* cases, it is the responsibility of the caller to invoke notification
* functions after releasing the the port lock.
*/
void
{
if (grp->lg_addr_fixed) {
/*
* The group is using a fixed MAC address or an automatic
* MAC address has not been set.
*/
return;
}
/*
* The MAC address of the port was assigned to the group
* MAC address. Update the group MAC address.
*/
} else {
/*
* Update the actual port MAC address to the MAC address
* of the group.
*/
} else {
/*
* If a port was detached because of a previous
* failure changing the MAC address, the port is
* reattached when it successfully changes the MAC
* address now, and this might cause the link state
* of the aggregation to change.
*/
}
}
}
/*
* Add a port to a link aggregation group.
*/
static int
aggr_port_t **pp)
{
int err;
/* create new port */
if (err != 0)
return (err);
/* add port to list of group constituent ports */
/*
* Back reference to the group it is member of. A port always
* holds a reference to its group to ensure that the back
* reference is always valid.
*/
/*
* Initialize the callback functions for this port. Note that this
* can only be done after the lp_grp field is set.
*/
return (0);
}
/*
* Add one or more ports to an existing link aggregation group.
*/
int
{
/* get group corresponding to key */
(mod_hash_val_t *)&grp) != 0) {
return (ENOENT);
}
/* add the specified ports to group */
for (i = 0; i < nports; i++) {
/* add port to group */
goto bail;
nadded++;
/* check capabilities */
goto bail;
}
/* start port if group has already been started */
if (grp->lg_started) {
if (rc != 0) {
goto bail;
}
/* set port promiscuous mode */
if (rc != 0) {
goto bail;
}
}
/*
* Attach each port if necessary.
*/
}
/* update the MAC address of the constituent ports */
if (link_state_changed)
bail:
if (rc != 0) {
/* stop and remove ports that have been added */
if (grp->lg_started) {
}
}
}
return (rc);
}
/*
* Update properties of an existing link aggregation group.
*/
int
{
int rc = 0;
/* get group corresponding to key */
(mod_hash_val_t *)&grp) != 0) {
goto bail;
}
} else {
}
/* validate fixed address if specified */
(mac_addr[0] & 0x01))) {
goto bail;
}
/* update policy if requested */
if (update_mask & AGGR_MODIFY_POLICY)
/* update unicast MAC address if requested */
if (update_mask & AGGR_MODIFY_MAC) {
if (mac_fixed) {
/* user-supplied MAC address */
}
} else if (grp->lg_addr_fixed) {
/* switch from user-supplied to automatic */
}
}
if (mac_addr_changed)
if (update_mask & AGGR_MODIFY_LACP_MODE)
bail:
/*
* If grp_arg is non-NULL, this function is called from
* mac_unicst_set(), and the MAC_NOTE_UNICST notification
* will be sent there.
*/
if (link_state_changed)
}
}
}
return (rc);
}
/*
* Create a new link aggregation group upon request from administrator.
* Returns 0 on success, an errno on failure.
*/
int
{
int err;
int i;
/* need at least one port */
if (nports == 0)
return (EINVAL);
/* does a group with the same key already exist? */
(mod_hash_val_t *)&grp);
if (err == 0) {
return (EEXIST);
}
grp->lg_ifspeed = 0;
/* add MAC ports to group */
grp->lg_nattached_ports = 0;
grp->lg_ntx_ports = 0;
for (i = 0; i < nports; i++) {
if (err != 0)
goto bail;
}
/*
* If no explicit MAC address was specified by the administrator,
* set it to the MAC address of the first port.
*/
if (grp->lg_addr_fixed) {
/* validate specified address */
goto bail;
}
} else {
}
/*
* Update the MAC address of the constituent ports.
* None of the port is attached at this time, the link state of the
* aggregation will not change.
*/
/* update outbound load balancing policy */
/* register with the MAC module */
mip->mi_sdu_min = 0;
/* set the initial group capabilities */
goto bail;
/* set LACP mode */
/*
* Attach each port if necessary.
*/
/* add new group to hash table */
aggr_grp_cnt++;
return (0);
bail:
}
}
return (err);
}
/*
* Return a pointer to the member of a group with specified device name
* and port number.
*/
static aggr_port_t *
{
break;
}
return (port);
}
/*
* Stop, detach and remove a port from a link aggregation group.
*/
static int
{
int rc = 0;
aggr_port_t **pport;
uint_t i;
/* unlink port */
goto done;
}
}
/*
* If the MAC address of the port being removed was assigned
* to the group, update the group MAC address
* using the MAC address of a different port.
*/
/*
* Set the MAC address of the group to the
* MAC address of its first port.
*/
}
/*
* Add the statistics of the ports while it was aggregated
* to the group's residual statistics.
*/
/* avoid stats that are not counters */
if (i == MAC_STAT_IFSPEED || i == MAC_STAT_LINK_DUPLEX)
continue;
/* get current value */
/* subtract value at the point of aggregation */
/* add to the residual stat */
}
/*
* If the group MAC address has changed, update the MAC address of
* the remaining consistuent ports according to the new MAC
* address of the group.
*/
if (mac_addr_changed)
done:
if (mac_addr_changedp != NULL)
if (link_state_changedp != NULL)
return (rc);
}
/*
* Remove one or more ports from an existing link aggregation group.
*/
int
{
int rc = 0, i;
/* get group corresponding to key */
(mod_hash_val_t *)&grp) != 0) {
return (ENOENT);
}
/* we need to keep at least one port per group */
goto bail;
}
/* first verify that all the groups are valid */
for (i = 0; i < nports; i++) {
/* port not found */
goto bail;
}
}
/* remove the specified ports from group */
/* lookup port */
/* stop port if group has already been started */
if (grp->lg_started) {
}
/* remove port from group */
}
bail:
if (mac_addr_update)
if (link_state_update)
if (rc == 0)
return (rc);
}
int
{
(mod_hash_val_t *)&grp) != 0) {
return (ENOENT);
}
/*
* Unregister from the MAC service module. Since this can
* fail if a client hasn't closed the MAC port, we gracefully
* fail the operation.
*/
return (EBUSY);
}
/* detach and free MAC ports associated with group */
if (grp->lg_started)
}
ASSERT(aggr_grp_cnt > 0);
aggr_grp_cnt--;
return (0);
}
void
{
}
/*
* Walker invoked when building the list of configured groups and
* their ports that must be passed up to user-space.
*/
/*ARGSUSED*/
static uint_t
{
return (MH_WALK_TERMINATE); /* terminate walk */
goto bail;
goto bail;
goto bail;
}
bail:
}
int
{
int rc = 0;
*ngroups = aggr_grp_cnt;
return (rc);
}
static void
aggr_m_resources(void *arg)
{
/* Call each port's m_resources function */
}
/*ARGSUSED*/
static void
{
}
static uint64_t
{
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_LINK_DUPLEX:
break;
default:
/*
* The remaining statistics are counters. They are computed
* by aggregating the counters of the members MACs while they
* were aggregated, plus the residual counter of the group
* itself, which is updated each time a MAC is removed from
* the group.
*/
val = 0;
/* actual port statistic */
/* minus the port stat when it was added */
/* plus any residual amount for the group */
}
}
return (val);
}
static int
aggr_m_start(void *arg)
{
/*
* Attempts to start all configured members of the group.
* Group members will be attached when their link-up notification
* is received.
*/
if (aggr_port_start(port) != 0) {
continue;
}
/* set port promiscuous mode */
}
return (0);
}
static void
aggr_m_stop(void *arg)
{
}
}
static int
{
goto bail;
if (port->lp_started) {
} else {
/*
* If a port was detached because of a previous
* failure changing the promiscuity, the port
* is reattached when it successfully changes
* the promiscuity now, and this might cause
* the link state of the aggregation to change.
*/
}
}
}
if (link_state_changed)
bail:
return (0);
}
/*
* Add or remove the multicast addresses that are defined for the group
* to or from the specified port.
* This function is called before stopping a port, before a port
* is detached from a group, and when attaching a port to a group.
*/
void
{
if (!port->lp_started)
return;
add);
}
static int
{
continue;
}
return (err);
}
static int
{
int rc;
0, 0);
return (rc);
}
/*
* Initialize the capabilities that are advertised for the group
* according to the capabilities of the constituent ports.
*/
static void
{
const mac_info_t *port_mi;
}
}
/*
* Checks whether the capabilities of the ports being added are compatible
* with the current capabilities of the aggregation.
*/
static boolean_t
{
}