xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_send.c (revision e44e85a7f9935f0428e188393e3da61b17e83884)
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