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