Home | History | Annotate | Download | only in aggr
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * IEEE 802.3ad Link Aggregation - Send code.
     28  *
     29  * Implements the Distributor function.
     30  */
     31 
     32 #include <sys/conf.h>
     33 #include <sys/modctl.h>
     34 #include <sys/sunddi.h>
     35 #include <sys/vlan.h>
     36 #include <sys/strsun.h>
     37 #include <sys/strsubr.h>
     38 #include <sys/dlpi.h>
     39 
     40 #include <inet/common.h>
     41 #include <inet/led.h>
     42 #include <inet/ip.h>
     43 #include <inet/ip6.h>
     44 #include <inet/tcp.h>
     45 #include <netinet/udp.h>
     46 
     47 #include <sys/aggr.h>
     48 #include <sys/aggr_impl.h>
     49 
     50 /*
     51  * Update the TX load balancing policy of the specified group.
     52  */
     53 void
     54 aggr_send_update_policy(aggr_grp_t *grp, uint32_t policy)
     55 {
     56 	uint8_t mac_policy = 0;
     57 
     58 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
     59 
     60 	if ((policy & AGGR_POLICY_L2) != 0)
     61 		mac_policy |= MAC_PKT_HASH_L2;
     62 	if ((policy & AGGR_POLICY_L3) != 0)
     63 		mac_policy |= MAC_PKT_HASH_L3;
     64 	if ((policy & AGGR_POLICY_L4) != 0)
     65 		mac_policy |= MAC_PKT_HASH_L4;
     66 
     67 	grp->lg_tx_policy = policy;
     68 	grp->lg_mac_tx_policy = mac_policy;
     69 }
     70 
     71 /*
     72  * Send function invoked by the MAC service module.
     73  */
     74 mblk_t *
     75 aggr_m_tx(void *arg, mblk_t *mp)
     76 {
     77 	aggr_grp_t *grp = arg;
     78 	aggr_port_t *port;
     79 	mblk_t *nextp;
     80 	mac_tx_cookie_t	cookie;
     81 	uint64_t hash;
     82 	void	*mytx_handle;
     83 
     84 	for (;;) {
     85 		rw_enter(&grp->lg_tx_lock, RW_READER);
     86 		if (grp->lg_ntx_ports == 0) {
     87 			/*
     88 			 * We could have returned from aggr_m_start() before
     89 			 * the ports were actually attached. Drop the chain.
     90 			 */
     91 			rw_exit(&grp->lg_tx_lock);
     92 			freemsgchain(mp);
     93 			return (NULL);
     94 		}
     95 
     96 		nextp = mp->b_next;
     97 		mp->b_next = NULL;
     98 
     99 		hash = mac_pkt_hash(DL_ETHER, mp, grp->lg_mac_tx_policy,
    100 		    B_TRUE);
    101 		port = grp->lg_tx_ports[hash % grp->lg_ntx_ports];
    102 
    103 		/*
    104 		 * Bump the active Tx ref count so that the port won't
    105 		 * be deleted. The reference count will be dropped in mac_tx().
    106 		 */
    107 		mytx_handle = mac_tx_hold(port->lp_mch);
    108 		rw_exit(&grp->lg_tx_lock);
    109 
    110 		if (mytx_handle == NULL) {
    111 			/*
    112 			 * The port is quiesced.
    113 			 */
    114 			freemsg(mp);
    115 		} else {
    116 			mblk_t	*ret_mp = NULL;
    117 
    118 			/*
    119 			 * It is fine that the port state changes now.
    120 			 * Set MAC_TX_NO_HOLD to inform mac_tx() not to bump
    121 			 * the active Tx ref again. Use hash as the hint so
    122 			 * to direct traffic to different TX rings. Note below
    123 			 * bit operation is needed to get the most benefit
    124 			 * from the mac_tx() hash algorithm.
    125 			 */
    126 			hash = (hash << 24 | hash << 16 | hash);
    127 			hash = (hash << 32 | hash);
    128 			cookie = mac_tx(port->lp_mch, mp, (uintptr_t)hash,
    129 			    MAC_TX_NO_ENQUEUE | MAC_TX_NO_HOLD, &ret_mp);
    130 
    131 			mac_tx_rele(port->lp_mch, mytx_handle);
    132 
    133 			if (cookie != NULL) {
    134 				ret_mp->b_next = nextp;
    135 				mp = ret_mp;
    136 				break;
    137 			}
    138 		}
    139 
    140 		if ((mp = nextp) == NULL)
    141 			break;
    142 	}
    143 	return (mp);
    144 }
    145 
    146 /*
    147  * Enable sending on the specified port.
    148  */
    149 void
    150 aggr_send_port_enable(aggr_port_t *port)
    151 {
    152 	aggr_grp_t *grp = port->lp_grp;
    153 
    154 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
    155 
    156 	if (port->lp_tx_enabled || (port->lp_state !=
    157 	    AGGR_PORT_STATE_ATTACHED)) {
    158 		/* already enabled or port not yet attached */
    159 		return;
    160 	}
    161 
    162 	/*
    163 	 * Add to group's array of tx ports.
    164 	 */
    165 	rw_enter(&grp->lg_tx_lock, RW_WRITER);
    166 	if (grp->lg_tx_ports_size < grp->lg_ntx_ports+1) {
    167 		/* current array too small */
    168 		aggr_port_t **new_ports;
    169 		uint_t new_size;
    170 
    171 		new_size = grp->lg_ntx_ports+1;
    172 		new_ports = kmem_zalloc(new_size * sizeof (aggr_port_t *),
    173 		    KM_SLEEP);
    174 
    175 		if (grp->lg_tx_ports_size > 0) {
    176 			ASSERT(grp->lg_tx_ports != NULL);
    177 			bcopy(grp->lg_tx_ports, new_ports,
    178 			    grp->lg_ntx_ports * sizeof (aggr_port_t *));
    179 			kmem_free(grp->lg_tx_ports,
    180 			    grp->lg_tx_ports_size * sizeof (aggr_port_t *));
    181 		}
    182 
    183 		grp->lg_tx_ports = new_ports;
    184 		grp->lg_tx_ports_size = new_size;
    185 	}
    186 
    187 	grp->lg_tx_ports[grp->lg_ntx_ports++] = port;
    188 	port->lp_tx_idx = grp->lg_ntx_ports-1;
    189 	rw_exit(&grp->lg_tx_lock);
    190 
    191 	port->lp_tx_enabled = B_TRUE;
    192 }
    193 
    194 /*
    195  * Disable sending from the specified port.
    196  */
    197 void
    198 aggr_send_port_disable(aggr_port_t *port)
    199 {
    200 	uint_t idx, ntx;
    201 	aggr_grp_t *grp = port->lp_grp;
    202 
    203 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
    204 	ASSERT(MAC_PERIM_HELD(port->lp_mh));
    205 
    206 	if (!port->lp_tx_enabled) {
    207 		/* not yet enabled */
    208 		return;
    209 	}
    210 
    211 	rw_enter(&grp->lg_tx_lock, RW_WRITER);
    212 	idx = port->lp_tx_idx;
    213 	ntx = grp->lg_ntx_ports;
    214 	ASSERT(idx < ntx);
    215 
    216 	/* remove from array of attached ports */
    217 	if (idx == (ntx - 1)) {
    218 		grp->lg_tx_ports[idx] = NULL;
    219 	} else {
    220 		/* not the last entry, replace with last one */
    221 		aggr_port_t *victim;
    222 
    223 		victim = grp->lg_tx_ports[ntx - 1];
    224 		grp->lg_tx_ports[ntx - 1] = NULL;
    225 		victim->lp_tx_idx = idx;
    226 		grp->lg_tx_ports[idx] = victim;
    227 	}
    228 
    229 	port->lp_tx_idx = 0;
    230 	grp->lg_ntx_ports--;
    231 	rw_exit(&grp->lg_tx_lock);
    232 
    233 	port->lp_tx_enabled = B_FALSE;
    234 }
    235