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 { 458 int rval = 0; 459 fcoe_mac_t *mac; 460 461 mac = fcoe_lookup_mac_by_id(linkid); 462 if (mac == NULL) { 463 fcoeio->fcoeio_status = FCOEIOE_MAC_NOT_FOUND; 464 return (EINVAL); 465 } 466 467 if ((mac->fm_flags & FCOE_MAC_FLAG_ENABLED) != FCOE_MAC_FLAG_ENABLED) { 468 fcoeio->fcoeio_status = FCOEIOE_ALREADY; 469 return (EALREADY); 470 } 471 472 atomic_and_32(&mac->fm_eport.eport_flags, ~EPORT_FLAG_MAC_IN_USE); 473 474 rval = ndi_devi_offline(mac->fm_client_dev, NDI_DEVI_REMOVE); 475 if (rval != NDI_SUCCESS) { 476 FCOE_LOG("fcoe", "fcoe%d: offline_driver %s failed", 477 ddi_get_instance(parent), 478 ddi_get_name(mac->fm_client_dev)); 479 atomic_or_32(&mac->fm_eport.eport_flags, 480 EPORT_FLAG_MAC_IN_USE); 481 482 fcoeio->fcoeio_status = FCOEIOE_OFFLINE_FAILURE; 483 return (EBUSY); 484 } 485 (void) fcoe_close_mac(mac); 486 fcoe_destroy_mac(mac); 487 return (0); 488 } 489