/*
* 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 2015 Joyent, Inc.
*/
/*
* 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 linkid associated with the link
* aggregation group.
*
* A set of MAC ports are associated with each association group.
*
* Aggr pseudo TX rings
* --------------------
* The underlying ports (NICs) in an aggregation can have TX rings. To
* enhance aggr's performance, these TX rings are made available to the
* aggr layer as pseudo TX rings. The concept of pseudo rings are not new.
* They are already present and implemented on the RX side. It is called
* as pseudo RX rings. The same concept is extended to the TX side where
* each TX ring of an underlying port is reflected in aggr as a pseudo
* TX ring. Thus each pseudo TX ring will map to a specific hardware TX
* ring. Even in the case of a NIC that does not have a TX ring, a pseudo
* TX ring is given to the aggregation layer.
*
* With this change, the outgoing stack depth looks much better:
*
* mac_tx() -> mac_tx_aggr_mode() -> mac_tx_soft_ring_process() ->
* mac_tx_send() -> aggr_ring_rx() -> <driver>_ring_tx()
*
* Two new modes are introduced to mac_tx() to handle aggr pseudo TX rings:
* SRS_TX_AGGR and SRS_TX_BW_AGGR.
*
* In SRS_TX_AGGR mode, mac_tx_aggr_mode() routine is called. This routine
* invokes an aggr function, aggr_find_tx_ring(), to find a (pseudo) TX
* ring belonging to a port on which the packet has to be sent.
* policy and then uses the fanout_hint passed to it to pick a TX ring from
* the selected port.
*
* In SRS_TX_BW_AGGR mode, mac_tx_bw_mode() function is called where
* bandwidth limit is applied first on the outgoing packet and the packets
* allowed to go out would call mac_tx_aggr_mode() to send the packet on a
* particular TX ring.
*/
#include <sys/sysmacros.h>
#include <sys/id_space.h>
#include <sys/mac_provider.h>
#include <sys/aggr_impl.h>
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 *);
const void *);
static void aggr_m_propinfo(void *, const char *, mac_prop_id_t,
boolean_t *);
static void aggr_grp_capab_set(aggr_grp_t *);
static int aggr_pseudo_disable_intr(mac_intr_handle_t);
static int aggr_pseudo_enable_intr(mac_intr_handle_t);
static void aggr_pseudo_stop_ring(mac_ring_driver_t);
static int aggr_addmac(void *, const uint8_t *);
static int aggr_remmac(void *, const uint8_t *);
static mblk_t *aggr_rx_poll(void *, int);
static void aggr_fill_ring(void *, mac_ring_type_t, const int,
const int, mac_ring_info_t *, mac_ring_handle_t);
static void aggr_fill_group(void *, mac_ring_type_t, const int,
#define AGGR_M_CALLBACK_FLAGS \
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
/*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;
/*
* Allocate an id space to manage key values (when key is not
* specified). The range of the id space will be from
* (AGGR_MAX_KEY + 1) to UINT16_MAX, because the LACP protocol
* uses a 16-bit key.
*/
}
void
aggr_grp_fini(void)
{
}
aggr_grp_count(void)
{
return (count);
}
/*
* Since both aggr_port_notify_cb() and aggr_port_timer_thread() functions
* requires the mac perimeter, this function holds a reference of the aggr
* and aggr won't call mac_unregister() until this reference drops to 0.
*/
void
{
grp->lg_port_ref++;
}
/*
* Release the reference of the grp and inform aggr_grp_delete() calling
* mac_unregister() is now safe.
*/
void
{
if (--grp->lg_port_ref == 0)
}
/*
* Wait for the port's lacp timer thread and the port's notification callback
* to exit.
*/
void
{
if (grp->lg_port_ref != 0)
}
/*
* 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 (aggr_port_unicst(cport) != 0) {
} 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.
*/
if (aggr_port_unicst(port) != 0) {
} 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;
/* The port must be int the same zone as the aggregation. */
return (EBUSY);
/*
* lg_mh could be NULL when the function is called during the creation
* of the aggregation.
*/
/* 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.
*/
return (0);
}
/*
* This is called in response to either our LACP state machine or a MAC
* notification that the link has gone down via aggr_send_port_disable(). At
* this point, we may need to update our default ring. To that end, we go
* through the set of ports (underlying datalinks in an aggregation) that are
* currently enabled to transmit data. If all our links have been disabled for
* transmit, then we don't do anything.
*
* Note, because we only have a single TX group, we don't have to worry about
* the rings moving between groups and the chance that mac will reassign it
* unless someone removes a port, at which point, we play it safe and call this
* again.
*/
void
{
if (grp->lg_ntx_ports == 0) {
return;
}
}
/*
* Add a pseudo RX ring for the given HW ring handle.
*/
static int
{
int err;
int j;
for (j = 0; j < MAX_RINGS_PER_GROUP; j++) {
break;
}
/*
* No slot for this new RX ring.
*/
if (j == MAX_RINGS_PER_GROUP)
return (EIO);
rx_grp->arg_ring_cnt++;
/*
* The group is already registered, dynamically add a new ring to the
* mac group.
*/
rx_grp->arg_ring_cnt--;
} else {
}
return (err);
}
/*
* Remove the pseudo RX ring of the given HW ring handle.
*/
static void
{
int j;
for (j = 0; j < MAX_RINGS_PER_GROUP; j++) {
continue;
}
rx_grp->arg_ring_cnt--;
break;
}
}
/*
* This function is called to create pseudo rings over the hardware rings of
* the underlying device. Note that there is a 1:1 mapping between the pseudo
* RX rings of the aggr and the hardware rings of the underlying port.
*/
static int
{
int hw_rh_cnt, i = 0, j;
int err = 0;
/*
* This function must be called after the aggr registers its mac
* and its RX group has been initialized.
*/
/*
* Get the list the the underlying HW rings.
*/
/*
* Quiesce the HW ring and the mac srs on the ring. Note
* that the HW ring will be restarted when the pseudo ring
* is started. At that time all the packets will be
* directly passed up to the pseudo RX ring and handled
* by mac srs created over the pseudo RX ring.
*/
}
/*
* Add all the unicast addresses to the newly added port.
*/
break;
}
if (err != 0) {
for (j = 0; j < i; j++)
}
} else {
}
done:
return (err);
}
/*
* This function is called by aggr to remove pseudo RX rings over the
* HW rings of the underlying port.
*/
static void
{
int hw_rh_cnt, i;
if (!port->lp_rx_grp_added)
goto done;
/*
* If hw_rh_cnt is 0, it means that the underlying port does not
* support RX rings. Directly return in this case.
*/
for (i = 0; i < hw_rh_cnt; i++)
/*
* First clear the permanent-quiesced flag of the RX srs then
* restart the HW ring and the mac srs on the ring. Note that
* the HW ring and associated SRS will soon been removed when
* the port is removed from the aggr.
*/
}
done:
}
/*
* Add a pseudo TX ring for the given HW ring handle.
*/
static int
{
int err;
int i;
for (i = 0; i < MAX_RINGS_PER_GROUP; i++) {
break;
}
/*
* No slot for this new TX ring.
*/
if (i == MAX_RINGS_PER_GROUP)
return (EIO);
/*
* The following 4 statements needs to be done before
* calling mac_group_add_ring(). Otherwise it will
* result in an assertion failure in mac_init_ring().
*/
tx_grp->atg_ring_cnt++;
/*
* The TX side has no concept of ring groups unlike RX groups.
* There is just a single group which stores all the TX rings.
* This group will be used to store aggr's pseudo TX rings.
*/
tx_grp->atg_ring_cnt--;
} else {
}
}
return (err);
}
/*
* Remove the pseudo TX ring of the given HW ring handle.
*/
static void
{
int i;
for (i = 0; i < MAX_RINGS_PER_GROUP; i++) {
continue;
tx_grp->atg_ring_cnt--;
break;
}
}
/*
* This function is called to create pseudo rings over hardware rings of
* the underlying device. There is a 1:1 mapping between the pseudo TX
* rings of the aggr and the hardware rings of the underlying port.
*/
static int
{
int hw_rh_cnt, i = 0, j;
int err = 0;
/*
* Get the list the the underlying HW rings.
*/
/*
* Even if the underlying NIC does not have TX rings, we
* still make a psuedo TX ring for that NIC with NULL as
* the ring handle.
*/
if (hw_rh_cnt == 0)
else
if (hw_rh_cnt == 0) {
}
} else {
if (err != 0)
break;
}
}
if (err != 0) {
if (hw_rh_cnt != 0) {
for (j = 0; j < i; j++) {
port->lp_pseudo_tx_rings[j]);
}
}
port->lp_tx_ring_cnt = 0;
} else {
}
return (err);
}
/*
* This function is called by aggr to remove pseudo TX rings over the
* HW rings of the underlying port.
*/
static void
{
int i;
if (!port->lp_tx_grp_added)
goto done;
for (i = 0; i < port->lp_tx_ring_cnt; i++)
port->lp_tx_ring_cnt = 0;
done:
}
static int
{
}
static int
{
}
static int
{
int err;
if (err == 0)
return (err);
}
static void
{
}
/*
* Add one or more ports to an existing link aggregation group.
*/
int
{
/* get group corresponding to linkid */
(mod_hash_val_t *)&grp) != 0) {
return (ENOENT);
}
/*
* Hold the perimeter so that the aggregation won't be destroyed.
*/
/* add the specified ports to group */
for (i = 0; i < nports; i++) {
/* add port to group */
goto bail;
}
nadded++;
/* check capabilities */
goto bail;
}
/*
* Create the pseudo ring for each HW ring of the underlying
* port.
*/
if (rc != 0)
goto bail;
if (rc != 0)
goto bail;
/* set LACP mode */
/* start port if group has already been started */
if (grp->lg_started) {
if (rc != 0) {
goto bail;
}
/*
* Turn on the promiscuous mode over the port when it
* is requested to be turned on to receive the
* non-primary address over a port, or the promiscous
* mode is enabled over the aggr.
*/
if (rc != 0) {
goto bail;
}
}
}
/*
* Attach each port if necessary.
*/
/*
* Initialize the callback functions for this port.
*/
}
/* update the MAC address of the constituent ports */
if (aggr_grp_update_ports_mac(grp))
if (link_state_changed)
bail:
if (rc != 0) {
/* stop and remove ports that have been added */
for (i = 0; i < nadded; i++) {
if (grp->lg_started) {
}
}
}
return (rc);
}
static int
{
/* validate fixed address if specified */
(mac_addr[0] & 0x01))) {
return (EINVAL);
}
/* 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)
if (link_state_changed)
if (mac_addr_changed)
return (0);
}
/*
* Update properties of an existing link aggregation group.
*/
int
{
int err;
/* get group corresponding to linkid */
(mod_hash_val_t *)&grp) != 0) {
return (ENOENT);
}
/*
* Hold the perimeter so that the aggregation won't be destroyed.
*/
return (err);
}
/*
* 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 linkid already exist? */
(mod_hash_val_t *)&grp);
if (err == 0) {
return (EEXIST);
}
grp->lg_ifspeed = 0;
grp->lg_tx_blocked_cnt = 0;
/* add MAC ports to group */
grp->lg_nattached_ports = 0;
grp->lg_ntx_ports = 0;
/*
* If key is not specified by the user, allocate the key.
*/
goto bail;
}
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 {
}
/* set the initial group capabilities */
goto bail;
}
if (err != 0)
goto bail;
if (err != 0) {
goto bail;
}
/*
* 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 */
/* set LACP mode */
/*
* Attach each port if necessary.
*/
/*
* Create the pseudo ring for each HW ring of the underlying
* port. Note that this is done after the aggr registers the
* mac.
*/
/*
* Initialize the callback functions for this port.
*/
}
if (link_state_changed)
/* add new group to hash table */
aggr_grp_cnt++;
return (0);
bail:
}
/*
* Inform the lacp_rx thread to exit.
*/
/*
* Inform the tx_notify thread to exit.
*/
}
if (tid != 0)
(sizeof (mac_ring_handle_t *) * MAX_RINGS_PER_GROUP));
return (err);
}
/*
* Return a pointer to the member of a group with specified linkid.
*/
static aggr_port_t *
{
break;
}
return (port);
}
/*
* Stop, detach and remove a port from a link aggregation group.
*/
static int
{
int rc = 0;
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 counter statistics of the ports while it was aggregated
* to the group's residual statistics. This is done by obtaining
* the current counter from the underlying MAC then subtracting the
* value of the counter at the moment it was added to the
* aggregation.
*/
for (i = 0; i < MAC_NSTAT; i++) {
stat = i + MAC_STAT_MIN;
if (!MAC_STAT_ISACOUNTER(stat))
continue;
}
for (i = 0; i < ETHER_NSTAT; i++) {
stat = i + MACTYPE_STAT_MIN;
if (!ETHER_STAT_ISACOUNTER(stat))
continue;
}
/*
* If the group MAC address has changed, update the MAC address of
* the remaining constituent ports according to the new MAC
* address of the group.
*/
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 linkid */
(mod_hash_val_t *)&grp) != 0) {
return (ENOENT);
}
/*
* Hold the perimeter so that the aggregation won't be destroyed.
*/
/* 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;
}
}
/* clear the promiscous mode for the specified ports */
/* lookup port */
}
if (rc != 0) {
for (i = 0; i < nports; i++) {
/*
* Turn the promiscuous mode back on if it is required
* to receive the non-primary address over a port, or
* the promiscous mode is enabled over the aggr.
*/
}
}
goto bail;
}
/* remove the specified ports from group */
for (i = 0; i < nports; i++) {
/* lookup port */
/* stop port if group has already been started */
if (grp->lg_started) {
}
/*
* aggr_rem_pseudo_tx_group() is not called here. Instead
* it is called from inside aggr_grp_rem_port() after the
* port has been detached. The reason is that
* aggr_rem_pseudo_tx_group() removes one ring at a time
* and if there is still traffic going on, then there
* is the possibility of aggr_find_tx_ring() returning a
* removed ring for transmission. Once the port has been
* detached, that port will not be used and
* aggr_find_tx_ring() will not return any rings
* belonging to it.
*/
/* remove port from group */
}
bail:
if (mac_addr_update)
if (link_state_update)
return (rc);
}
int
{
int err;
(mod_hash_val_t *)&grp) != 0) {
return (ENOENT);
}
/*
* Note that dls_devnet_destroy() must be called before lg_lock is
* held. Otherwise, it will deadlock if another thread is in
* aggr_m_stat() and thus has a kstat_hold() on the kstats that
* dls_devnet_destroy() needs to delete.
*/
return (err);
}
/*
* 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 (err);
}
ASSERT(aggr_grp_cnt > 0);
aggr_grp_cnt--;
/*
* Inform the lacp_rx thread to exit.
*/
/*
* Inform the tx_notify_thread to exit.
*/
}
if (tid != 0)
/* detach and free MAC ports associated with group */
if (grp->lg_started)
}
(sizeof (mac_ring_handle_t *) * MAX_RINGS_PER_GROUP));
/*
* Wait for the port's lacp timer thread and its notification callback
* to exit before calling mac_unregister() since both needs to access
* the mac perimeter of the grp.
*/
return (0);
}
void
{
}
}
int
{
int rc = 0;
/*
* Make sure that the aggregation link is visible from the caller's
* zone.
*/
return (ENOENT);
(mod_hash_val_t *)&grp) != 0) {
return (ENOENT);
}
if (rc != 0)
goto bail;
if (rc != 0)
goto bail;
}
bail:
return (rc);
}
/*ARGSUSED*/
static void
{
}
static int
{
/* We only aggregate counter statistics. */
return (ENOTSUP);
}
/*
* Counter statistics for a group 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.
*/
if (IS_MAC_STAT(stat)) {
} else if (IS_MACTYPE_STAT(stat)) {
}
}
return (0);
}
int
{
} else {
}
return (0);
}
int
{
} else {
}
return (0);
}
static int
{
int rval = 0;
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case ETHER_STAT_LINK_DUPLEX:
break;
default:
/*
* For all other statistics, we return the aggregated stat
* from the underlying ports. aggr_grp_stat() will set
* rval appropriately if the statistic isn't a counter.
*/
}
return (rval);
}
static int
{
/*
* 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;
}
/*
* Turn on the promiscuous mode if it is required to receive
* the non-primary address over a port, or the promiscous
* mode is enabled over the aggr.
*/
}
}
return (0);
}
static void
{
/* reset port promiscuous mode */
}
}
static int
{
goto bail;
int err = 0;
if (err != 0) {
} 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);
}
static void
{
/*
* aggr port's mac client name is the format of "aggr link name" plus
* AGGR_PORT_NAME_DELIMIT plus "underneath link name".
*/
char *clnt_name;
}
}
/*
* Initialize the capabilities that are advertised for the group
* according to the capabilities of the constituent ports.
*/
static boolean_t
{
switch (cap) {
case MAC_CAPAB_HCKSUM: {
break;
}
case MAC_CAPAB_LSO: {
break;
} else {
return (B_FALSE);
}
}
case MAC_CAPAB_NO_NATIVEVLAN:
case MAC_CAPAB_NO_ZCOPY:
case MAC_CAPAB_RINGS: {
/*
* An aggregation advertises only one (pseudo) RX
* the underlying devices.
*/
} else {
}
break;
}
case MAC_CAPAB_AGGR:
{
}
return (B_TRUE);
}
default:
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Callback funtion for MAC layer to register groups.
*/
static void
{
if (rtype == MAC_RING_TYPE_RX) {
} else {
}
}
/*
* Callback funtion for MAC layer to register all rings.
*/
static void
{
switch (rtype) {
case MAC_RING_TYPE_RX: {
/*
* Entrypoint to enable interrupt (disable poll) and
* disable interrupt (enable poll).
*/
break;
}
case MAC_RING_TYPE_TX: {
/*
* Use the hw TX ring handle to find if the ring needs
* serialization or not. For NICs that do not expose
* Tx rings, atr_hw_rh will be NULL.
*/
}
break;
}
default:
break;
}
}
static mblk_t *
{
return (mp_chain);
continue;
}
}
if (!port->lp_collector_enabled) {
continue;
}
}
return (mp_chain);
}
static int
{
int err = 0;
return (0);
}
/*
* Insert this mac address into the list of mac addresses owned by
* the aggregation pseudo group.
*/
return (EEXIST);
}
}
break;
if (err != 0) {
aggr_port_remmac(p, mac_addr);
}
return (err);
}
static int
{
int err = 0;
return (0);
}
/*
* Insert this mac address into the list of mac addresses owned by
* the aggregation pseudo group.
*/
continue;
}
break;
}
return (EINVAL);
}
return (err);
}
/*
* Add or remove the multicast addresses that are defined for the group
* to or from the specified port.
*
* Note that aggr_grp_multicst_port(..., B_TRUE) is called when the port
* is started and attached, and aggr_grp_multicst_port(..., B_FALSE) is
* called when the port is either stopped or detached.
*/
void
{
return;
}
static int
{
int err = 0;
!port->lp_started) {
continue;
}
if (err != 0) {
break;
}
}
/*
* At least one port caused error return and this error is returned to
* mac, eventually a NAK would be sent upwards.
* Some ports have this multicast address listed now, and some don't.
* Treat this error as a whole aggr failure not individual port failure.
* Therefore remove this multicast address from other ports.
*/
!port->lp_started) {
continue;
}
}
}
return (err);
}
static int
{
int err;
0, 0);
return (err);
}
/*
* Initialize the capabilities that are advertised for the group
* according to the capabilities of the constituent ports.
*/
static void
{
cksum = 0;
}
}
}
/*
* Checks whether the capabilities of the port being added are compatible
* with the current capabilities of the aggregation.
*/
static boolean_t
{
return (B_FALSE);
}
return (B_FALSE);
}
if (grp->lg_hcksum_txflags != 0)
return (B_FALSE);
grp->lg_hcksum_txflags) {
return (B_FALSE);
}
return (B_FALSE);
return (B_FALSE);
} else {
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* Returns the maximum SDU according to the SDU of the constituent ports.
*/
static uint_t
{
if (max_sdu > port_sdu_max)
}
return (max_sdu);
}
/*
* Checks if the maximum SDU of the specified port is compatible
* with the maximum SDU of the specified aggregation group, returns
* B_TRUE if it is, B_FALSE otherwise.
*/
static boolean_t
{
}
/*
* Returns the maximum margin according to the margin of the constituent ports.
*/
static uint32_t
{
}
return (margin);
}
/*
* Checks if the maximum margin of the specified port is compatible
* with the maximum margin of the specified aggregation group, returns
* B_TRUE if it is, B_FALSE otherwise.
*/
static boolean_t
{
return (B_TRUE);
/*
* See whether the current margin value is allowed to be changed to
* the new value.
*/
return (B_FALSE);
return (B_TRUE);
}
/*
* Set MTU on individual ports of an aggregation group
*/
static int
{
}
/*
* following is a workaround for a bug in 'bge' driver.
* See CR 6794654 for more information and this work around
* will be removed once the CR is fixed.
*/
goto try_again;
}
/*
* if mac_unicast_add() failed while setting the MTU,
* detach the port from the group.
*/
"setting MTU. Detaching the port from the aggregation.",
}
return (err);
}
static int
{
/*
* If the MTU being set is equal to aggr group's maximum
* allowable value, then there is nothing to change
*/
return (0);
/* 0 is aggr group's min sdu */
if (sdu == 0)
return (EINVAL);
}
if (err != 0) {
/* recover from error: reset the mtus of the ports */
}
goto bail;
}
bail:
return (err);
}
/*
*/
/*ARGSUSED*/
static int
{
switch (pr_num) {
case MAC_PROP_MTU: {
if (pr_valsize < sizeof (mtu)) {
break;
}
break;
}
default:
break;
}
return (err);
}
typedef struct rboundary {
int btype;
} rboundary_t;
/*
* This function finds the intersection of mtu ranges stored in arrays -
* mrange[0] ... mrange[mcount -1]. It returns the intersection in rval.
* Individual arrays are assumed to contain non-overlapping ranges.
* Algorithm:
* A range has two boundaries - min and max. We scan all arrays and store
* each boundary as a separate element in a temporary array. We also store
* the boundary types, min or max, as +1 or -1 respectively in the temporary
* array. Then we sort the temporary array in ascending order. We scan the
* sorted array from lower to higher values and keep a cumulative sum of
* boundary types. Element in the temporary array for which the sum reaches
* mcount is a min boundary of a range in the result and next element will be
* max boundary.
*
* Example for mcount = 3,
*
* ----|_________|-------|_______|----|__|------ mrange[0]
*
* -------|________|--|____________|-----|___|-- mrange[1]
*
* --------|________________|-------|____|------ mrange[2]
*
* 3 2 1
* \|/
* 1 23 2 1 2 3 2 1 01 2 V 0 <- the sum
* ----|--||-----|-|--|--|--|----|-||-|--|---|-- sorted array
*
* same min and max
* V
* --------|_____|-------|__|------------|------ intersecting ranges
*/
void
{
int i, j, m, sum;
sz_range32 = sizeof (mac_propval_uint32_range_t);
/* Allocate enough space to store the results */
/* Number of boundaries are twice as many as ranges */
for (i = 0, m = 0; i < mcount; i++) {
}
}
/*
* Sort the temporary array in ascending order of bval;
* if boundary values are same then sort on btype.
*/
for (i = 0; i < m-1; i++) {
for (j = i+1; j < m; j++) {
}
}
}
/* Walk through temporary array to find all ranges in the results */
}
}
}
/*
* Returns the mtu ranges which could be supported by aggr group.
* prmaxcnt returns the size of the buffer prval, prcount returns
* the number of valid entries in prval. Caller is responsible
* for freeing up prval.
*/
int
{
int err = 0;
sz_propval = sizeof (mac_propval_range_t);
sz_range32 = sizeof (mac_propval_uint32_range_t);
KM_SLEEP);
size = sz_propval;
/*
* Not enough space to hold all ranges.
* Allocate extra space as indicated and retry.
*/
}
if (err != 0) {
break;
}
}
/*
* if any of the underlying ports does not support changing MTU then
* just return ENOTSUP
*/
goto done;
}
prcount);
done:
}
}
return (err);
}
static void
{
int err = 0;
switch (pr_num) {
case MAC_PROP_MTU:
&rcount);
if (err != 0) {
return;
}
for (i = 0; i < rcount; i++) {
}
break;
}
}