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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * This file defines interfaces between fcoe and its clients (FCoEI/FCoET)
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>
46 #include <sys/fcoe/fcoeio.h>
47 #include <sys/fcoe/fcoe_common.h>
50 * Driver's own header files
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
);
66 * Only this function will be called explicitly by clients
67 * Register the specified client port (fcoei/fcoet)
70 fcoe_register_client(fcoe_client_t
*client
)
75 if (client
->ect_fcoe_ver
!= fcoe_ver_now
) {
76 cmn_err(CE_WARN
, "FCoE modules version mismatch, "
77 "fail registering client.");
82 * We will not come here, when someone is changing ss_mac_list,
83 * so it's safe to go through ss_mac_list.
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
) {
93 FCOE_LOG(0, "can't find the MAC you want to bind");
97 if (mac
->fm_flags
& FCOE_MAC_FLAG_BOUND
) {
98 FCOE_LOG(0, "the MAC you want to bind is bound already");
102 atomic_or_32(&mac
->fm_flags
, FCOE_MAC_FLAG_BOUND
);
103 bcopy(client
, &mac
->fm_client
, sizeof (fcoe_client_t
));
106 * fcoe_port_t initialization
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
;
126 * The following routines will be called through vectors in fcoe_port_t
130 * Deregister fcoet/fcoei modules, client should make sure the port is in
131 * offline status already
134 fcoe_deregister_client(fcoe_port_t
*eport
)
136 fcoe_mac_t
*mac
= EPORT2MAC(eport
);
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
144 while (mac
->fm_frm_cnt
> 0) {
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
);
158 fcoe_ctl(fcoe_port_t
*eport
, int cmd
, void *arg
)
160 fcoe_mac_t
*mac
= EPORT2MAC(eport
);
163 case FCOE_CMD_PORT_ONLINE
:
165 * client ask us to online, so it's safe to post event
168 if (fcoe_enable_callback(mac
) == FCOE_FAILURE
) {
169 return (FCOE_FAILURE
);
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
);
177 case FCOE_CMD_PORT_OFFLINE
:
178 if (fcoe_disable_callback(mac
) == FCOE_FAILURE
) {
179 return (FCOE_FAILURE
);
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
);
188 FCOE_LOG("fcoe", "fcoe_ctl, unsupported cmd %x", cmd
);
192 return (FCOE_SUCCESS
);
196 * Transmit the specified frame to the link
199 fcoe_tx_frame(fcoe_frame_t
*frm
)
201 mblk_t
*ret_mblk
= NULL
;
202 fcoe_mac_t
*mac
= FRM2MAC(frm
);
203 mac_tx_cookie_t ret_cookie
;
205 fcoe_fill_frame_headers(frm
);
206 fcoe_fill_frame_tailers(frm
);
209 ret_cookie
= mac_tx(mac
->fm_cli_handle
, FRM2MBLK(frm
), 0,
210 MAC_TX_NO_ENQUEUE
, &ret_mblk
);
211 if (ret_cookie
!= (uintptr_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
);
217 if (mac
->fm_state
== FCOE_MAC_STATE_OFFLINE
) {
219 * we are doing offline, so just tell the upper that
220 * this is finished, the cmd will be aborted soon.
222 fcoe_free_netb(ret_mblk
);
229 * MAC driver will release the mblk of the frame
230 * We need only release the frame itself
232 mutex_enter(&FRM2MAC(frm
)->fm_ss
->ss_watch_mutex
);
233 list_insert_tail(&FRM2MAC(frm
)->fm_ss
->ss_pfrm_list
,
236 if (FRM2MAC(frm
)->fm_ss
->ss_flags
& SS_FLAG_DOG_WAITING
) {
237 cv_signal(&FRM2MAC(frm
)->fm_ss
->ss_watch_cv
);
239 mutex_exit(&FRM2MAC(frm
)->fm_ss
->ss_watch_mutex
);
243 * Consider cache allocation in the future
246 fcoe_release_frame(fcoe_frame_t
*frame
)
248 kmem_free(frame
, frame
->frm_alloc_size
);
252 fcoe_alloc_netb(fcoe_port_t
*eport
, uint32_t fc_frame_size
, uint8_t **ppfc
)
256 mp
= fcoe_get_mblk(eport
->eport_fcoe_private
,
257 fc_frame_size
+ PADDING_SIZE
);
259 *ppfc
= mp
->b_rptr
+ PADDING_HEADER_SIZE
;
266 fcoe_free_netb(void *netb
)
268 freeb((mblk_t
*)netb
);
272 fcoe_allocate_frame(fcoe_port_t
*eport
, uint32_t fc_frame_size
, void *xmp
)
278 uint32_t raw_frame_size
;
280 if (fc_frame_size
> 2136) {
281 FCOE_LOG("fcoe", "fcoe_allocate_frame %d > 2136",
288 * We are allocating solicited frame now
290 raw_frame_size
= PADDING_SIZE
+ fc_frame_size
;
291 mp
= fcoe_get_mblk(EPORT2MAC(eport
), raw_frame_size
);
297 alloc_size
= sizeof (fcoe_frame_t
) + sizeof (fcoe_i_frame_t
) +
298 EPORT2MAC(eport
)->fm_client
.ect_private_frame_struct_size
;
301 * fcoe_frame_t initialization
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
;
312 frm
->frm_eport
= eport
;
316 * fcoe_i_frame_t initialization
319 fmi
->fmi_frame
= frm
;
320 fmi
->fmi_mac
= EPORT2MAC(eport
);
321 fmi
->fmi_efh
= (void *)mp
->b_rptr
;
323 fmi
->fmi_ffh
= (fcoe_frame_header_t
*)
324 (sizeof (struct ether_header
) + (uint8_t *)fmi
->fmi_efh
);
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
);
332 * Continue to initialize fcoe_frame_t
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
;
344 * Sub routines called by interface functions
348 * According to spec, fill EthernetII frame header, FCoE frame header
349 * VLAN (not included for now)
352 fcoe_fill_frame_headers(fcoe_frame_t
*frm
)
354 fcoe_i_frame_t
*fmi
= FRM2FMI(frm
);
357 * Initialize ethernet frame header
359 bcopy(FRM2MAC(frm
)->fm_current_addr
, &fmi
->fmi_efh
->ether_shost
,
361 bcopy(frm
->frm_eport
->eport_efh_dst
,
362 &fmi
->fmi_efh
->ether_dhost
, ETHERADDRL
);
363 fmi
->fmi_efh
->ether_type
= htons(ETHERTYPE_FCOE
);
366 * Initialize FCoE frame header
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
);
374 FCOE_V2B_1(0x36, fmi
->fmi_ffh
->ffh_sof
);
379 * According to spec, fill FCOE frame tailer including CRC
380 * VLAN (not included for now)
383 fcoe_fill_frame_tailers(fcoe_frame_t
*frm
)
388 * Initialize FCoE frame tailer
389 * CRC is not big endian, can't use macro V2B
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
);
400 FCOE_V2B_1(0x41, FRM2FMI(frm
)->fmi_fft
->fft_eof
);
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;
409 fcoe_mac_notify_link_up(void *arg
)
411 fcoe_mac_t
*mac
= (fcoe_mac_t
*)arg
;
413 ASSERT(mac
->fm_flags
& FCOE_MAC_FLAG_BOUND
);
415 mac
->fm_client
.ect_port_event(&mac
->fm_eport
,
416 FCOE_NOTIFY_EPORT_LINK_UP
);
419 fcoe_mac_notify_link_down(void *arg
)
421 fcoe_mac_t
*mac
= (fcoe_mac_t
*)arg
;
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
);
430 fcoe_create_port(dev_info_t
*parent
, fcoe_mac_t
*mac
, int is_target
)
433 dev_info_t
*child
= NULL
;
434 char *devname
= is_target
? FCOET_DRIVER_NAME
: FCOEI_DRIVER_NAME
;
436 ndi_devi_alloc_sleep(parent
, devname
, DEVI_PSEUDO_NODEID
, &child
);
438 FCOE_LOG("fcoe", "fail to create new devinfo");
439 return (NDI_FAILURE
);
442 if (ddi_prop_update_int(DDI_DEV_T_NONE
, child
,
443 "mac_id", mac
->fm_linkid
) != DDI_PROP_SUCCESS
) {
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
);
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
);
457 mac
->fm_client_dev
= child
;
463 fcoe_delete_port(dev_info_t
*parent
, fcoeio_t
*fcoeio
, datalink_id_t linkid
,
469 mac
= fcoe_lookup_mac_by_id(linkid
);
471 fcoeio
->fcoeio_status
= FCOEIOE_MAC_NOT_FOUND
;
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
;
481 if (!(mac
->fm_flags
& FCOE_MAC_FLAG_BOUND
)) {
483 * It means that deferred detach has finished
484 * of last delete operation
486 goto skip_devi_offline
;
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
);
499 fcoeio
->fcoeio_status
= FCOEIOE_OFFLINE_FAILURE
;
504 (void) fcoe_close_mac(mac
);
505 fcoe_destroy_mac(mac
);