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