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