xref: /illumos-gate/usr/src/uts/common/io/fcoe/fcoe_eth.c (revision 09ce0d4acf1a79c720d7e54b60e87cbfa0f1b2d6)
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 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <netinet/in.h>
29 #include <sys/inttypes.h>
30 #include <sys/strsun.h>
31 #include <sys/mac_client.h>
32 
33 /*
34  * FCoE header files
35  */
36 #include <sys/fcoe/fcoeio.h>
37 #include <sys/fcoe/fcoe_common.h>
38 
39 /*
40  * Driver's own header files
41  */
42 #include <fcoe.h>
43 #include <fcoe_eth.h>
44 #include <fcoe_fc.h>
45 
46 static void fcoe_rx(void *arg, mac_resource_handle_t mrh,
47     mblk_t *mp, boolean_t loopback);
48 static void fcoe_mac_notify(void *arg, mac_notify_type_t type);
49 
50 /*
51  * Global variable definitions
52  */
53 
54 /*
55  * Internal tunable, used to enable p2p mode
56  */
57 volatile uint32_t	fcoe_enable_p2pmode = 0;
58 
59 int
60 fcoe_open_mac(fcoe_mac_t *mac, int force_promisc, fcoeio_stat_t *err_detail)
61 {
62 	int		ret;
63 	int		fcoe_ret;
64 	char		cli_name[MAXNAMELEN];
65 	mac_diag_t	diag;
66 	uint16_t	fm_open_flag = 0;
67 
68 	*err_detail = 0;
69 
70 	/*
71 	 * Open MAC interface
72 	 */
73 	ret = mac_open_by_linkname(mac->fm_link_name, &mac->fm_handle);
74 	if (ret != 0) {
75 		cmn_err(CE_WARN, "Open network interface %s failed",
76 		    mac->fm_link_name);
77 		FCOE_LOG("fcoe", "mac_open_by_linkname %s failed %x",
78 		    mac->fm_link_name, ret);
79 		return (FCOE_FAILURE);
80 	}
81 
82 	if (mac_is_vnic(mac->fm_handle)) {
83 		(void) mac_close(mac->fm_handle);
84 		*err_detail = FCOEIOE_VNIC_UNSUPPORT;
85 		return (FCOE_FAILURE);
86 	}
87 
88 	(void) sprintf(cli_name, "%s-%s", mac->fm_link_name, "fcoe");
89 
90 	ret = mac_client_open(mac->fm_handle,
91 	    &mac->fm_cli_handle, cli_name, fm_open_flag);
92 	if (ret != 0) {
93 		(void) fcoe_close_mac(mac);
94 		return (FCOE_FAILURE);
95 	}
96 	/*
97 	 * Cache the pointer of the immutable MAC inforamtion and
98 	 * the current and primary MAC address
99 	 */
100 	mac_unicast_primary_get(mac->fm_handle, mac->fm_primary_addr);
101 	bcopy(mac->fm_primary_addr, mac->fm_current_addr,
102 	    ETHERADDRL);
103 
104 	if (mac_unicast_add(mac->fm_cli_handle, NULL, MAC_UNICAST_PRIMARY,
105 	    &mac->fm_unicst_handle, 0, &diag)) {
106 		(void) fcoe_close_mac(mac);
107 		return (FCOE_FAILURE);
108 	}
109 
110 	if (force_promisc) {
111 		mac->fm_force_promisc = B_TRUE;
112 	}
113 
114 	/* Get mtu */
115 	mac_sdu_get(mac->fm_handle, NULL, &mac->fm_eport.eport_mtu);
116 	if (mac->fm_eport.eport_mtu < FCOE_MIN_MTU_SIZE) {
117 		if (!fcoe_enable_p2pmode || mac->fm_eport.eport_mtu < 1500) {
118 			/*
119 			 * Fail open if fail to get mtu, or we are not
120 			 * using p2p, or we are using p2p, but
121 			 * the mtu is too small
122 			 */
123 			(void) fcoe_close_mac(mac);
124 			*err_detail = FCOEIOE_NEED_JUMBO_FRAME;
125 			return (FCOE_FAILURE);
126 		}
127 	}
128 
129 	mac->fm_eport.eport_link_speed =
130 	    mac_client_stat_get(mac->fm_cli_handle, MAC_STAT_IFSPEED);
131 
132 	cv_init(&mac->fm_tx_cv, NULL, CV_DRIVER, NULL);
133 	mutex_init(&mac->fm_mutex, NULL, MUTEX_DRIVER, NULL);
134 	mac->fm_running = B_TRUE;
135 
136 	fcoe_ret = FCOE_SUCCESS;
137 	return (fcoe_ret);
138 }
139 
140 int
141 fcoe_close_mac(fcoe_mac_t *mac)
142 {
143 	int ret;
144 
145 	if (mac->fm_handle == NULL) {
146 		return (FCOE_SUCCESS);
147 	}
148 
149 	if (mac->fm_running) {
150 		cv_destroy(&mac->fm_tx_cv);
151 		mutex_destroy(&mac->fm_mutex);
152 		mac->fm_running = B_FALSE;
153 	}
154 
155 	if (mac->fm_promisc_handle != NULL) {
156 		mac_promisc_remove(mac->fm_promisc_handle);
157 		mac->fm_promisc_handle = NULL;
158 	} else {
159 		mac_rx_clear(mac->fm_cli_handle);
160 	}
161 
162 	if (mac->fm_notify_handle != NULL) {
163 		ret = mac_notify_remove(mac->fm_notify_handle, B_TRUE);
164 		ASSERT(ret == 0);
165 		mac->fm_notify_handle = NULL;
166 	}
167 
168 	if (mac->fm_unicst_handle != NULL) {
169 		(void) mac_unicast_remove(mac->fm_cli_handle,
170 		    mac->fm_unicst_handle);
171 		mac->fm_unicst_handle = NULL;
172 	}
173 
174 	mac_client_close(mac->fm_cli_handle, 0);
175 	mac->fm_cli_handle = NULL;
176 
177 	(void) mac_close(mac->fm_handle);
178 	mac->fm_handle = NULL;
179 
180 	return (FCOE_SUCCESS);
181 }
182 
183 int
184 fcoe_enable_callback(fcoe_mac_t *mac)
185 {
186 	int ret;
187 
188 	/*
189 	 * Set message callback
190 	 */
191 	if (mac->fm_force_promisc) {
192 		ret = mac_promisc_add(mac->fm_cli_handle,
193 		    MAC_CLIENT_PROMISC_FILTERED, fcoe_rx, mac,
194 		    &mac->fm_promisc_handle,
195 		    MAC_PROMISC_FLAGS_NO_TX_LOOP);
196 		if (ret != 0) {
197 			cmn_err(CE_WARN, "Enable promisc mode on %s failed",
198 			    mac->fm_link_name);
199 			FCOE_LOG("foce", "mac_promisc_add on %s failed %x",
200 			    mac->fm_link_name, ret);
201 			return (FCOE_FAILURE);
202 		}
203 	} else {
204 		mac_rx_set(mac->fm_cli_handle, fcoe_rx, mac);
205 	}
206 
207 	/* Get the link state, if it's up, we will need to notify client */
208 	mac->fm_link_state =
209 	    mac_stat_get(mac->fm_handle, MAC_STAT_LINK_UP)?
210 	    FCOE_MAC_LINK_STATE_UP:FCOE_MAC_LINK_STATE_DOWN;
211 
212 	/*
213 	 * Add a notify function so that we get updates from MAC
214 	 */
215 	mac->fm_notify_handle = mac_notify_add(mac->fm_handle,
216 	    fcoe_mac_notify, (void *)mac);
217 	return (FCOE_SUCCESS);
218 }
219 
220 int
221 fcoe_disable_callback(fcoe_mac_t *mac)
222 {
223 	int ret;
224 
225 	if (mac->fm_promisc_handle) {
226 		mac_promisc_remove(mac->fm_promisc_handle);
227 		mac->fm_promisc_handle = NULL;
228 	} else {
229 		mac_rx_clear(mac->fm_cli_handle);
230 	}
231 
232 	if (mac->fm_notify_handle) {
233 		ret = mac_notify_remove(mac->fm_notify_handle, B_TRUE);
234 		ASSERT(ret == 0);
235 		mac->fm_notify_handle = NULL;
236 	}
237 
238 	ret = fcoe_mac_set_address(&mac->fm_eport,
239 	    mac->fm_primary_addr, B_FALSE);
240 	FCOE_SET_DEFAULT_FPORT_ADDR(mac->fm_eport.eport_efh_dst);
241 	return (ret);
242 }
243 
244 /* ARGSUSED */
245 static void
246 fcoe_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp, boolean_t loopback)
247 {
248 	fcoe_mac_t	*mac = (fcoe_mac_t *)arg;
249 	mblk_t		*next;
250 	fcoe_frame_t	*frm;
251 	uint32_t	raw_frame_size, frame_size;
252 	uint16_t	frm_type;
253 
254 	while (mp != NULL) {
255 		next = mp->b_next;
256 		mp->b_next = NULL;
257 		frm_type = ntohs(*(uint16_t *)((uintptr_t)mp->b_rptr + 12));
258 
259 		if (frm_type != ETHERTYPE_FCOE) {
260 			/*
261 			 * This mp is not allocated in FCoE, but we must free it
262 			 */
263 			freeb(mp);
264 			mp = next;
265 			continue;
266 		}
267 
268 		raw_frame_size = MBLKL(mp);
269 		frame_size = raw_frame_size - PADDING_SIZE;
270 		frm = fcoe_allocate_frame(&mac->fm_eport, frame_size, mp);
271 		if (frm != NULL) {
272 			fcoe_post_frame(frm);
273 		}
274 
275 		mp = next;
276 	}
277 }
278 
279 static void
280 fcoe_mac_notify(void *arg, mac_notify_type_t type)
281 {
282 	fcoe_mac_t *mac = (fcoe_mac_t *)arg;
283 
284 	/*
285 	 * We assume that the calls to this notification callback are serialized
286 	 * by MAC layer
287 	 */
288 
289 	switch (type) {
290 	case MAC_NOTE_LINK:
291 		/*
292 		 * This notification is sent every time the MAC driver
293 		 * updates the link state.
294 		 */
295 		if (mac_stat_get(mac->fm_handle, MAC_STAT_LINK_UP) != 0) {
296 			if (mac->fm_link_state == FCOE_MAC_LINK_STATE_UP) {
297 				break;
298 			}
299 			/* Get speed */
300 			mac->fm_eport.eport_link_speed =
301 			    mac_client_stat_get(mac->fm_cli_handle,
302 			    MAC_STAT_IFSPEED);
303 
304 			(void) fcoe_mac_set_address(&mac->fm_eport,
305 			    mac->fm_primary_addr, B_FALSE);
306 
307 			FCOE_SET_DEFAULT_FPORT_ADDR(
308 			    mac->fm_eport.eport_efh_dst);
309 
310 			mac->fm_link_state = FCOE_MAC_LINK_STATE_UP;
311 			FCOE_LOG(mac->fm_link_name,
312 			    "fcoe_mac_notify: arg/%p LINK up", arg, type);
313 			fcoe_mac_notify_link_up(mac);
314 		} else {
315 			if (mac->fm_link_state == FCOE_MAC_LINK_STATE_DOWN) {
316 				break;
317 			}
318 			mac->fm_link_state = FCOE_MAC_LINK_STATE_DOWN;
319 			FCOE_LOG(mac->fm_link_name,
320 			    "fcoe_mac_notify: arg/%p LINK down", arg, type);
321 			fcoe_mac_notify_link_down(mac);
322 		}
323 		break;
324 
325 	case MAC_NOTE_TX:
326 		/*
327 		 * MAC is not so busy now, then wake up fcoe_tx_frame to try
328 		 */
329 		mutex_enter(&mac->fm_mutex);
330 		cv_broadcast(&mac->fm_tx_cv);
331 		mutex_exit(&mac->fm_mutex);
332 
333 		FCOE_LOG("fcoe_mac_notify", "wake up");
334 		break;
335 
336 	default:
337 		FCOE_LOG("fcoe_mac_notify", "not supported arg/%p, type/%d",
338 		    arg, type);
339 		break;
340 	}
341 }
342 
343 int
344 fcoe_mac_set_address(fcoe_port_t *eport, uint8_t *addr, boolean_t fc_assigned)
345 {
346 	fcoe_mac_t	*mac = EPORT2MAC(eport);
347 	int		ret;
348 
349 	if (bcmp(addr, mac->fm_current_addr, 6) == 0) {
350 		return (FCOE_SUCCESS);
351 	}
352 
353 	mutex_enter(&mac->fm_mutex);
354 	if (mac->fm_promisc_handle == NULL) {
355 		ret = mac_unicast_primary_set(mac->fm_handle, addr);
356 		if (ret != 0) {
357 			mutex_exit(&mac->fm_mutex);
358 			cmn_err(CE_WARN, "Set primary unicast address on %s "
359 			    "failed", mac->fm_link_name);
360 			FCOE_LOG("fcoe", "mac_unicast_primary_set on %s "
361 			    "failed %x", mac->fm_link_name, ret);
362 			return (FCOE_FAILURE);
363 		}
364 	}
365 	if (fc_assigned) {
366 		bcopy(addr, mac->fm_current_addr, ETHERADDRL);
367 	} else {
368 		bcopy(mac->fm_primary_addr,
369 		    mac->fm_current_addr, ETHERADDRL);
370 	}
371 	mutex_exit(&mac->fm_mutex);
372 	return (FCOE_SUCCESS);
373 }
374