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