xref: /illumos-gate/usr/src/uts/common/io/fcoe/fcoe_fc.c (revision 7a088f03b431bdffa96c3b2175964d4d38420caa)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This file defines interfaces between fcoe and its clients (FCoEI/FCoET)
29  */
30 
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/sunndi.h>
34 #include <sys/byteorder.h>
35 #include <sys/atomic.h>
36 #include <sys/sysmacros.h>
37 #include <sys/cmn_err.h>
38 #include <sys/crc32.h>
39 #include <sys/fcntl.h>
40 #include <sys/unistd.h>
41 #include <sys/mac_client.h>
42 
43 /*
44  * FCoE header files
45  */
46 #include <sys/fcoe/fcoeio.h>
47 #include <sys/fcoe/fcoe_common.h>
48 
49 /*
50  * Driver's own header files
51  */
52 #include <fcoe.h>
53 #include <fcoe_fc.h>
54 #include <fcoe_eth.h>
55 
56 static void fcoe_fill_frame_headers(fcoe_frame_t *frm);
57 static void fcoe_fill_frame_tailers(fcoe_frame_t *frm);
58 static void fcoe_deregister_client(fcoe_port_t *eport);
59 static int fcoe_ctl(fcoe_port_t *eport, int cmd, void *arg);
60 static void fcoe_tx_frame(fcoe_frame_t *frm);
61 static void *fcoe_alloc_netb(fcoe_port_t *eport,
62     uint32_t fc_frame_size, uint8_t **ppfc);
63 static void fcoe_free_netb(void *netb);
64 
65 /*
66  * Only this function will be called explicitly by clients
67  * Register the specified client port (fcoei/fcoet)
68  */
69 fcoe_port_t *
70 fcoe_register_client(fcoe_client_t *client)
71 {
72 	fcoe_mac_t	*mac;
73 	fcoe_port_t	*eport;
74 
75 	ASSERT(mutex_owned(&fcoe_global_ss->ss_ioctl_mutex));
76 
77 	/*
78 	 * We will not come here, when someone is changing ss_mac_list,
79 	 * so it's safe to go through ss_mac_list.
80 	 */
81 	for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
82 	    mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
83 		if (strcmp(client->ect_channel_name, mac->fm_link_name)
84 		    == 0) {
85 			break;
86 		}
87 	}
88 
89 	if (mac == NULL) {
90 		FCOE_LOG(0, "can't find the MAC you want to bind");
91 		return (NULL);
92 	}
93 
94 	if (mac->fm_flags & FCOE_MAC_FLAG_BOUND) {
95 		FCOE_LOG(0, "the MAC you want to bind is bound already");
96 		return (NULL);
97 	}
98 
99 	atomic_or_32(&mac->fm_flags, FCOE_MAC_FLAG_BOUND);
100 	bcopy(client, &mac->fm_client, sizeof (fcoe_client_t));
101 
102 	/*
103 	 * fcoe_port_t initialization
104 	 */
105 	eport = &mac->fm_eport;
106 	eport->eport_flags = client->ect_eport_flags | EPORT_FLAG_MAC_IN_USE;
107 	eport->eport_fcoe_private = mac;
108 	eport->eport_client_private = client->ect_client_port_struct;
109 	eport->eport_max_fc_frame_size = 2136;
110 	eport->eport_tx_frame = fcoe_tx_frame;
111 	eport->eport_alloc_frame = fcoe_allocate_frame;
112 	eport->eport_release_frame = fcoe_release_frame;
113 	eport->eport_alloc_netb = fcoe_alloc_netb;
114 	eport->eport_free_netb = fcoe_free_netb;
115 	eport->eport_deregister_client = fcoe_deregister_client;
116 	eport->eport_ctl = fcoe_ctl;
117 	eport->eport_set_mac_address = fcoe_mac_set_address;
118 
119 	return (eport);
120 }
121 
122 /*
123  * The following routines will be called through vectors in fcoe_port_t
124  */
125 
126 /*
127  * Deregister fcoet/fcoei modules, client should make sure the port is in
128  * offline status already
129  */
130 static void
131 fcoe_deregister_client(fcoe_port_t *eport)
132 {
133 	fcoe_mac_t	*mac = EPORT2MAC(eport);
134 
135 	ASSERT(mutex_owned(&fcoe_global_ss->ss_ioctl_mutex));
136 
137 	/*
138 	 * Wait for all the related frame to be freed, this should be fast
139 	 * because before deregister fcoei/fcoet will make sure its port
140 	 * is already in offline status so no frame will be received or sent
141 	 * any more
142 	 */
143 	while (mac->fm_frm_cnt > 0) {
144 		delay(10);
145 	}
146 
147 	atomic_and_32(&EPORT2MAC(eport)->fm_flags, ~FCOE_MAC_FLAG_BOUND);
148 }
149 
150 /* ARGSUSED */
151 static int
152 fcoe_ctl(fcoe_port_t *eport, int cmd, void *arg)
153 {
154 	fcoe_mac_t	*mac = EPORT2MAC(eport);
155 
156 	switch (cmd) {
157 		case FCOE_CMD_PORT_ONLINE:
158 			/*
159 			 * client ask us to online, so it's safe to post event
160 			 * and data up
161 			 */
162 			if (fcoe_enable_callback(mac) == FCOE_FAILURE) {
163 				return (FCOE_FAILURE);
164 			}
165 			mac->fm_state = FCOE_MAC_STATE_ONLINE;
166 			if (mac->fm_link_state == FCOE_MAC_LINK_STATE_UP)
167 				(void) ddi_taskq_dispatch(
168 				    fcoe_global_ss->ss_watchdog_taskq,
169 				    fcoe_mac_notify_link_up, mac, DDI_SLEEP);
170 			break;
171 		case FCOE_CMD_PORT_OFFLINE:
172 			if (fcoe_disable_callback(mac) == FCOE_FAILURE) {
173 				return (FCOE_FAILURE);
174 			}
175 			mac->fm_state = FCOE_MAC_STATE_OFFLINE;
176 			// in case there are threads waiting
177 			mutex_enter(&mac->fm_mutex);
178 			cv_broadcast(&mac->fm_tx_cv);
179 			mutex_exit(&mac->fm_mutex);
180 			break;
181 		default:
182 			FCOE_LOG("fcoe", "fcoe_ctl, unsupported cmd %x", cmd);
183 			break;
184 	}
185 
186 	return (FCOE_SUCCESS);
187 }
188 
189 /*
190  * Transmit the specified frame to the link
191  */
192 static void
193 fcoe_tx_frame(fcoe_frame_t *frm)
194 {
195 	mblk_t		*ret_mblk = NULL;
196 	fcoe_mac_t	*mac = FRM2MAC(frm);
197 	mac_tx_cookie_t	ret_cookie;
198 
199 	fcoe_fill_frame_headers(frm);
200 	fcoe_fill_frame_tailers(frm);
201 
202 tx_frame:
203 	ret_cookie = mac_tx(mac->fm_cli_handle, FRM2MBLK(frm), 0,
204 	    MAC_TX_NO_ENQUEUE, &ret_mblk);
205 	if (ret_cookie != NULL) {
206 		mutex_enter(&mac->fm_mutex);
207 		(void) cv_timedwait(&mac->fm_tx_cv, &mac->fm_mutex,
208 		    ddi_get_lbolt() + drv_usectohz(100000));
209 		mutex_exit(&mac->fm_mutex);
210 
211 		if (mac->fm_state == FCOE_MAC_STATE_OFFLINE) {
212 			/*
213 			 * we are doing offline, so just tell the upper that
214 			 * this is finished, the cmd will be aborted soon.
215 			 */
216 			fcoe_free_netb(ret_mblk);
217 		} else {
218 			goto tx_frame;
219 		}
220 	}
221 
222 	/*
223 	 * MAC driver will release the mblk of the frame
224 	 * We need only release the frame itself
225 	 */
226 	mutex_enter(&FRM2MAC(frm)->fm_ss->ss_watch_mutex);
227 	list_insert_tail(&FRM2MAC(frm)->fm_ss->ss_pfrm_list,
228 	    FRM2FMI(frm));
229 	mac->fm_frm_cnt ++;
230 	if (FRM2MAC(frm)->fm_ss->ss_flags & SS_FLAG_DOG_WAITING) {
231 		cv_signal(&FRM2MAC(frm)->fm_ss->ss_watch_cv);
232 	}
233 	mutex_exit(&FRM2MAC(frm)->fm_ss->ss_watch_mutex);
234 }
235 
236 /*
237  * Consider cache allocation in the future
238  */
239 void
240 fcoe_release_frame(fcoe_frame_t *frame)
241 {
242 	kmem_free(frame, frame->frm_alloc_size);
243 }
244 
245 static void *
246 fcoe_alloc_netb(fcoe_port_t *eport, uint32_t fc_frame_size, uint8_t **ppfc)
247 {
248 	mblk_t *mp;
249 
250 	mp = fcoe_get_mblk(eport->eport_fcoe_private,
251 	    fc_frame_size + PADDING_SIZE);
252 	if (mp != NULL) {
253 		*ppfc = mp->b_rptr + PADDING_HEADER_SIZE;
254 	}
255 
256 	return (mp);
257 }
258 
259 static void
260 fcoe_free_netb(void *netb)
261 {
262 	freeb((mblk_t *)netb);
263 }
264 
265 fcoe_frame_t *
266 fcoe_allocate_frame(fcoe_port_t *eport, uint32_t fc_frame_size, void *xmp)
267 {
268 	fcoe_frame_t	*frm;
269 	fcoe_i_frame_t	*fmi;
270 	mblk_t		*mp = xmp;
271 	uint32_t	 alloc_size;
272 	uint32_t	 raw_frame_size;
273 
274 	if (fc_frame_size > 2136) {
275 		FCOE_LOG("fcoe", "fcoe_allocate_frame %d > 2136",
276 		    fc_frame_size);
277 		return (NULL);
278 	}
279 
280 	if (mp == NULL) {
281 		/*
282 		 * We are allocating solicited frame now
283 		 */
284 		raw_frame_size = PADDING_SIZE + fc_frame_size;
285 		mp = fcoe_get_mblk(EPORT2MAC(eport), raw_frame_size);
286 		if (mp == NULL) {
287 			return (NULL);
288 		}
289 	}
290 
291 	alloc_size = sizeof (fcoe_frame_t) + sizeof (fcoe_i_frame_t) +
292 	    EPORT2MAC(eport)->fm_client.ect_private_frame_struct_size;
293 
294 	/*
295 	 * fcoe_frame_t initialization
296 	 */
297 	frm = (fcoe_frame_t *)kmem_alloc(alloc_size, KM_SLEEP);
298 	frm->frm_alloc_size = alloc_size;
299 	frm->frm_fc_frame_size = fc_frame_size;
300 	frm->frm_payload_size = fc_frame_size -
301 	    sizeof (fcoe_fc_frame_header_t);
302 	frm->frm_fcoe_private = sizeof (fcoe_frame_t) + (uint8_t *)frm;
303 	frm->frm_client_private = sizeof (fcoe_i_frame_t) +
304 	    (uint8_t *)frm->frm_fcoe_private;
305 	frm->frm_flags = 0;
306 	frm->frm_eport = eport;
307 	frm->frm_netb = mp;
308 
309 	/*
310 	 * fcoe_i_frame_t initialization
311 	 */
312 	fmi = FRM2FMI(frm);
313 	fmi->fmi_frame = frm;
314 	fmi->fmi_mac = EPORT2MAC(eport);
315 	fmi->fmi_efh = (void *)mp->b_rptr;
316 
317 	fmi->fmi_ffh = (fcoe_frame_header_t *)
318 	    (sizeof (struct ether_header) + (uint8_t *)fmi->fmi_efh);
319 
320 	fmi->fmi_fc_frame = sizeof (fcoe_frame_header_t) +
321 	    (uint8_t *)fmi->fmi_ffh;
322 	fmi->fmi_fft = (fcoe_frame_tailer_t *)
323 	    (fc_frame_size + (uint8_t *)fmi->fmi_fc_frame);
324 
325 	/*
326 	 * Continue to initialize fcoe_frame_t
327 	 */
328 	frm->frm_hdr = (fcoe_fc_frame_header_t *)fmi->fmi_fc_frame;
329 	frm->frm_ofh1 = NULL;
330 	frm->frm_ofh2 = NULL;
331 	frm->frm_fc_frame = (uint8_t *)frm->frm_hdr;
332 	frm->frm_payload = sizeof (fcoe_fc_frame_header_t) +
333 	    (uint8_t *)frm->frm_fc_frame;
334 	return (frm);
335 }
336 
337 /*
338  * Sub routines called by interface functions
339  */
340 
341 /*
342  * According to spec, fill EthernetII frame header, FCoE frame header
343  * VLAN (not included for now)
344  */
345 static void
346 fcoe_fill_frame_headers(fcoe_frame_t *frm)
347 {
348 	fcoe_i_frame_t *fmi = FRM2FMI(frm);
349 
350 	/*
351 	 * Initialize ethernet frame header
352 	 */
353 	bcopy(FRM2MAC(frm)->fm_current_addr, &fmi->fmi_efh->ether_shost,
354 	    ETHERADDRL);
355 	bcopy(frm->frm_eport->eport_efh_dst,
356 	    &fmi->fmi_efh->ether_dhost, ETHERADDRL);
357 	fmi->fmi_efh->ether_type = htons(ETHERTYPE_FCOE);
358 
359 	/*
360 	 * Initialize FCoE frame header
361 	 */
362 	bzero(fmi->fmi_ffh, sizeof (fcoe_frame_header_t));
363 	FCOE_ENCAPS_VER(fmi->fmi_ffh, FCOE_VER);
364 	/* set to SOFi3 for the first frame of a sequence */
365 	if (FRM_SEQ_CNT(frm) == 0) {
366 		FCOE_V2B_1(0x2E, fmi->fmi_ffh->ffh_sof);
367 	} else {
368 		FCOE_V2B_1(0x36, fmi->fmi_ffh->ffh_sof);
369 	}
370 }
371 
372 /*
373  * According to spec, fill FCOE frame tailer including CRC
374  * VLAN (not included for now)
375  */
376 static void
377 fcoe_fill_frame_tailers(fcoe_frame_t *frm)
378 {
379 	uint32_t crc;
380 
381 	/*
382 	 * Initialize FCoE frame tailer
383 	 * CRC is not big endian, can't use macro V2B
384 	 */
385 	CRC32(crc, frm->frm_fc_frame, frm->frm_fc_frame_size,
386 	    (uint32_t)~0, crc32_table);
387 	FRM2FMI(frm)->fmi_fft->fft_crc[0] = 0xFF & (~crc);
388 	FRM2FMI(frm)->fmi_fft->fft_crc[1] = 0xFF & (~crc >> 8);
389 	FRM2FMI(frm)->fmi_fft->fft_crc[2] = 0xFF & (~crc >> 16);
390 	FRM2FMI(frm)->fmi_fft->fft_crc[3] = 0xFF & (~crc >> 24);
391 	if (FRM_F_CTL(frm) & 0x080000) {
392 		FCOE_V2B_1(0x42, FRM2FMI(frm)->fmi_fft->fft_eof);
393 	} else {
394 		FCOE_V2B_1(0x41, FRM2FMI(frm)->fmi_fft->fft_eof);
395 	}
396 
397 	FRM2FMI(frm)->fmi_fft->fft_resvd[0] = 0;
398 	FRM2FMI(frm)->fmi_fft->fft_resvd[1] = 0;
399 	FRM2FMI(frm)->fmi_fft->fft_resvd[2] = 0;
400 }
401 
402 void
403 fcoe_mac_notify_link_up(void *arg)
404 {
405 	fcoe_mac_t *mac = (fcoe_mac_t *)arg;
406 
407 	ASSERT(mac->fm_flags & FCOE_MAC_FLAG_BOUND);
408 
409 	mac->fm_client.ect_port_event(&mac->fm_eport,
410 	    FCOE_NOTIFY_EPORT_LINK_UP);
411 }
412 void
413 fcoe_mac_notify_link_down(void *arg)
414 {
415 	fcoe_mac_t *mac = (fcoe_mac_t *)arg;
416 
417 	if (mac->fm_flags & FCOE_MAC_FLAG_BOUND) {
418 		mac->fm_client.ect_port_event(&mac->fm_eport,
419 		    FCOE_NOTIFY_EPORT_LINK_DOWN);
420 	}
421 }
422 
423 int
424 fcoe_create_port(dev_info_t *parent, fcoe_mac_t *mac, int is_target)
425 {
426 	int		 rval	  = 0;
427 	dev_info_t	*child	  = NULL;
428 	char		*mac_name = mac->fm_link_name;
429 	char *devname = is_target ? FCOET_DRIVER_NAME : FCOEI_DRIVER_NAME;
430 
431 	ndi_devi_alloc_sleep(parent, devname, DEVI_PSEUDO_NODEID, &child);
432 	if (child == NULL) {
433 		FCOE_LOG("fcoe", "fail to create new devinfo");
434 		return (NDI_FAILURE);
435 	}
436 
437 	if (ddi_prop_update_string(DDI_DEV_T_NONE, child,
438 	    "mac_name", mac_name) != DDI_PROP_SUCCESS) {
439 		FCOE_LOG("fcoe",
440 		    "fcoe%d: prop_update port mac name failed for mac %s",
441 		    ddi_get_instance(parent), mac_name);
442 		(void) ndi_devi_free(child);
443 		return (NDI_FAILURE);
444 	}
445 
446 	rval = ndi_devi_online(child, NDI_ONLINE_ATTACH);
447 	if (rval != NDI_SUCCESS) {
448 		FCOE_LOG("fcoe", "fcoe%d: online_driver failed for mac %s",
449 		    ddi_get_instance(parent), mac->fm_link_name);
450 		return (NDI_FAILURE);
451 	}
452 	mac->fm_client_dev = child;
453 
454 	return (rval);
455 }
456 
457 int
458 fcoe_delete_port(dev_info_t *parent, fcoeio_t *fcoeio, uint8_t *mac_name)
459 {
460 	int		 rval = 0;
461 	fcoe_mac_t	*mac;
462 
463 	mac = fcoe_lookup_mac_by_name(mac_name);
464 	if (mac == NULL) {
465 		fcoeio->fcoeio_status = FCOEIOE_MAC_NOT_FOUND;
466 		return (EINVAL);
467 	}
468 
469 	if ((mac->fm_flags & FCOE_MAC_FLAG_ENABLED) != FCOE_MAC_FLAG_ENABLED) {
470 		fcoeio->fcoeio_status = FCOEIOE_ALREADY;
471 		return (EALREADY);
472 	}
473 
474 	atomic_and_32(&mac->fm_eport.eport_flags, ~EPORT_FLAG_MAC_IN_USE);
475 
476 	rval = ndi_devi_offline(mac->fm_client_dev, NDI_DEVI_REMOVE);
477 	if (rval != NDI_SUCCESS) {
478 		FCOE_LOG("fcoe", "fcoe%d: offline_driver %s failed",
479 		    ddi_get_instance(parent),
480 		    ddi_get_name(mac->fm_client_dev));
481 		atomic_or_32(&mac->fm_eport.eport_flags,
482 		    EPORT_FLAG_MAC_IN_USE);
483 
484 		fcoeio->fcoeio_status = FCOEIOE_OFFLINE_FAILURE;
485 		return (EBUSY);
486 	}
487 	(void) fcoe_close_mac(mac);
488 	fcoe_destroy_mac(mac);
489 	return (0);
490 }
491