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