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