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 * The following notice accompanied the original version of this file: 29 * 30 * BSD LICENSE 31 * 32 * Copyright(c) 2007 Intel Corporation. All rights reserved. 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 39 * * Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * * Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in 43 * the documentation and/or other materials provided with the 44 * distribution. 45 * * Neither the name of Intel Corporation nor the names of its 46 * contributors may be used to endorse or promote products derived 47 * from this software without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 50 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 51 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 52 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 53 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 54 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 55 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 59 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60 */ 61 62 /* 63 * This file defines interfaces between fcoe and fcoet driver. 64 */ 65 66 /* 67 * Driver kernel header files 68 */ 69 #include <sys/conf.h> 70 #include <sys/ddi.h> 71 #include <sys/stat.h> 72 #include <sys/pci.h> 73 #include <sys/sunddi.h> 74 #include <sys/modctl.h> 75 #include <sys/file.h> 76 #include <sys/cred.h> 77 #include <sys/byteorder.h> 78 #include <sys/atomic.h> 79 #include <sys/modhash.h> 80 #include <sys/scsi/scsi.h> 81 #include <sys/ethernet.h> 82 83 /* 84 * COMSTAR header files 85 */ 86 #include <sys/stmf_defines.h> 87 #include <sys/fct_defines.h> 88 #include <sys/stmf.h> 89 #include <sys/portif.h> 90 #include <sys/fct.h> 91 92 /* 93 * FCoE header files 94 */ 95 #include <sys/fcoe/fcoe_common.h> 96 97 /* 98 * Driver's own header files 99 */ 100 #include <fcoet.h> 101 #include <fcoet_eth.h> 102 103 /* 104 * function forward declaration 105 */ 106 static fcoet_exchange_t *fcoet_create_unsol_exchange(fcoe_frame_t *frame); 107 static int fcoet_process_sol_fcp_data(fcoe_frame_t *frm); 108 static int fcoet_process_unsol_fcp_cmd(fcoe_frame_t *frm); 109 static int fcoet_process_unsol_els_req(fcoe_frame_t *frm); 110 static int fcoet_process_sol_els_rsp(fcoe_frame_t *frm); 111 static int fcoet_process_unsol_abts_req(fcoe_frame_t *frame); 112 static int fcoet_process_sol_abts_acc(fcoe_frame_t *frame); 113 static int fcoet_process_sol_abts_rjt(fcoe_frame_t *frame); 114 static int fcoet_process_unsol_ct_req(fcoe_frame_t *frm); 115 static int fcoet_process_sol_ct_rsp(fcoe_frame_t *frame); 116 static int fcoet_process_sol_flogi_rsp(fcoe_frame_t *frame); 117 static int fcoet_send_sol_fcp_data_done(fcoe_frame_t *frm); 118 static int fcoet_send_fcp_status_done(fcoe_frame_t *frm); 119 static int fcoet_send_unsol_els_rsp_done(fcoe_frame_t *frm); 120 static int fcoet_send_sol_els_req_done(fcoe_frame_t *frm); 121 static int fcoet_send_unsol_bls_acc_done(fcoe_frame_t *frm); 122 static int fcoet_send_unsol_bls_rjt_done(fcoe_frame_t *frm); 123 static int fcoet_send_sol_bls_req_done(fcoe_frame_t *frm); 124 static int fcoet_send_sol_ct_req_done(fcoe_frame_t *frm); 125 static int fcoet_process_unsol_flogi_req(fcoet_exchange_t *xch); 126 127 /* 128 * rx_frame & release_sol_frame 129 * There should be no same OXID/RXID in on-going exchanges. 130 * RXID -> unsol_rxid_hash 131 * OXID -> sol_oxid_hash 132 */ 133 134 void 135 fcoet_rx_frame(fcoe_frame_t *frm) 136 { 137 uint8_t rctl = FRM_R_CTL(frm); 138 139 switch (rctl) { 140 case 0x01: 141 /* 142 * Solicited data 143 */ 144 if (fcoet_process_sol_fcp_data(frm)) { 145 FCOET_LOG("fcoet_rx_frame", 146 "fcoet_process_sol_fcp_data failed"); 147 } 148 break; 149 150 case 0x06: 151 /* 152 * Unsolicited fcp_cmnd 153 */ 154 if (fcoet_process_unsol_fcp_cmd(frm)) { 155 FCOET_LOG("fcoet_rx_frame", 156 "fcoet_process_unsol_fcp_cmd failed"); 157 } 158 break; 159 160 case 0x22: 161 /* 162 * unsolicited ELS req 163 */ 164 if (fcoet_process_unsol_els_req(frm)) { 165 FCOET_LOG("fcoet_rx_frame", 166 "fcoet_process_unsol_els_req failed"); 167 } 168 break; 169 170 case 0x23: 171 /* 172 * solicited ELS rsp 173 */ 174 if (fcoet_process_sol_els_rsp(frm)) { 175 FCOET_LOG("fcoet_rx_frame", 176 "fcoet_process_sol_els_rsp failed"); 177 } 178 break; 179 180 case 0x81: 181 /* 182 * unsolicted ABTS req 183 */ 184 if (fcoet_process_unsol_abts_req(frm)) { 185 FCOET_LOG("fcoet_rx_frame", 186 "fcoet_process_unsol_abts_req failed"); 187 } 188 break; 189 190 case 0x84: 191 /* 192 * solicited ABTS acc response 193 */ 194 if (fcoet_process_sol_abts_acc(frm)) { 195 FCOET_LOG("fcoet_rx_frame", 196 "fcoet_process_sol_abts_acc failed"); 197 } 198 break; 199 case 0x85: 200 /* 201 * solcited ABTS rjt response 202 */ 203 if (fcoet_process_sol_abts_rjt(frm)) { 204 FCOET_LOG("fcoet_rx_frame", 205 "fcoet_process_sol_abts_rjt failed"); 206 } 207 break; 208 209 case 0x02: 210 /* 211 * unsolicited CT req 212 */ 213 if (fcoet_process_unsol_ct_req(frm)) { 214 FCOET_LOG("fcoet_rx_frame", 215 "fcoet_process_sol_ct_rsp failed"); 216 } 217 break; 218 219 case 0x03: 220 /* 221 * sol ct rsp 222 */ 223 if (fcoet_process_sol_ct_rsp(frm)) { 224 FCOET_LOG("fcoet_rx_frame", 225 "fcoet_process_sol_ct_rsp failed"); 226 } 227 break; 228 229 default: 230 /* 231 * Unsupported frame 232 */ 233 PRT_FRM_HDR("Unsupported unsol frame: ", frm); 234 break; 235 } 236 237 /* 238 * Release the frame in the end 239 */ 240 frm->frm_eport->eport_free_netb(frm->frm_netb); 241 frm->frm_eport->eport_release_frame(frm); 242 } 243 244 /* 245 * For solicited frames, after FCoE has sent it out, it will call this 246 * to notify client(FCoEI/FCoET) about its completion. 247 */ 248 void 249 fcoet_release_sol_frame(fcoe_frame_t *frm) 250 { 251 fcoet_exchange_t *xch = FRM2TFM(frm)->tfm_xch; 252 253 /* 254 * From now, we should not access both frm_hdr and frm_payload. Its 255 * mblk could have been released by MAC driver. 256 */ 257 switch (FRM2TFM(frm)->tfm_rctl) { 258 case 0x01: 259 if (xch && xch->xch_flags & XCH_FLAG_FCT_CALLED_ABORT) { 260 FCOET_RELE_XCHG(xch); 261 break; 262 } 263 if (fcoet_send_sol_fcp_data_done(frm)) { 264 ASSERT(0); 265 } 266 break; 267 268 case 0x05: 269 break; 270 271 case 0x07: 272 if (xch && xch->xch_flags & XCH_FLAG_FCT_CALLED_ABORT) { 273 FCOET_RELE_XCHG(xch); 274 break; 275 } 276 277 if (fcoet_send_fcp_status_done(frm)) { 278 ASSERT(0); 279 } 280 break; 281 282 case 0x23: 283 if (xch && xch->xch_flags & XCH_FLAG_FCT_CALLED_ABORT) { 284 FCOET_RELE_XCHG(xch); 285 break; 286 } 287 if (fcoet_send_unsol_els_rsp_done(frm)) { 288 ASSERT(0); 289 } 290 break; 291 292 case 0x22: 293 if (fcoet_send_sol_els_req_done(frm)) { 294 ASSERT(0); 295 } 296 break; 297 298 case 0x84: 299 if (fcoet_send_unsol_bls_acc_done(frm)) { 300 ASSERT(0); 301 } 302 break; 303 304 case 0x85: 305 if (fcoet_send_unsol_bls_rjt_done(frm)) { 306 ASSERT(0); 307 } 308 break; 309 310 case 0x81: 311 if (fcoet_send_sol_bls_req_done(frm)) { 312 ASSERT(0); 313 } 314 break; 315 316 case 0x02: 317 if (fcoet_send_sol_ct_req_done(frm)) { 318 ASSERT(0); 319 } 320 break; 321 322 case 0x03: 323 default: 324 /* 325 * Unsupported frame 326 */ 327 PRT_FRM_HDR("Unsupported sol frame: ", frm); 328 break; 329 } 330 331 /* 332 * We should release the frame 333 */ 334 FRM2SS(frm)->ss_eport->eport_release_frame(frm); 335 } 336 337 void 338 fcoet_port_event(fcoe_port_t *eport, uint32_t event) 339 { 340 fcoet_soft_state_t *ss = EPORT2SS(eport); 341 switch (event) { 342 case FCOE_NOTIFY_EPORT_LINK_UP: 343 if (eport->eport_mtu >= FCOE_MIN_MTU_SIZE) { 344 ss->ss_fcp_data_payload_size = 345 FCOE_DEFAULT_FCP_DATA_PAYLOAD_SIZE; 346 } else { 347 ss->ss_fcp_data_payload_size = 348 FCOE_MIN_FCP_DATA_PAYLOAD_SIZE; 349 } 350 FCOET_LOG("fcoet_port_event", "LINK UP notified"); 351 mutex_enter(&ss->ss_watch_mutex); 352 ss->ss_sol_flogi_state = SFS_FLOGI_INIT; 353 cv_signal(&ss->ss_watch_cv); 354 mutex_exit(&ss->ss_watch_mutex); 355 break; 356 case FCOE_NOTIFY_EPORT_LINK_DOWN: 357 fct_handle_event(ss->ss_port, 358 FCT_EVENT_LINK_DOWN, 0, 0); 359 /* Need clear up all other things */ 360 FCOET_LOG("fcoet_port_event", "LINK DOWN notified"); 361 ss->ss_sol_flogi_state = SFS_WAIT_LINKUP; 362 break; 363 default: 364 break; 365 } 366 } 367 368 /* 369 * For unsolicited exchanges, FCoET is only responsible for allocation of 370 * req_payload. FCT will allocate resp_payload after the exchange is 371 * passed on. 372 */ 373 static fcoet_exchange_t * 374 fcoet_create_unsol_exchange(fcoe_frame_t *frm) 375 { 376 uint8_t r_ctl; 377 int cdb_size; 378 fcoet_exchange_t *xch, *xch_tmp; 379 fct_cmd_t *cmd; 380 fcoe_fcp_cmnd_t *ffc; 381 uint32_t task_expected_len = 0; 382 383 r_ctl = FRM_R_CTL(frm); 384 switch (r_ctl) { 385 case 0x22: 386 /* 387 * FCoET's unsolicited ELS 388 */ 389 cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_RCVD_ELS, 390 GET_STRUCT_SIZE(fcoet_exchange_t) + 391 frm->frm_payload_size, 0); 392 if (cmd == NULL) { 393 FCOET_EXT_LOG(0, "can't get cmd"); 394 return (NULL); 395 } 396 break; 397 398 case 0x06: 399 /* 400 * FCoET's unsolicited SCSI cmd 401 */ 402 cdb_size = 16; /* need improve later */ 403 cmd = fct_scsi_task_alloc(FRM2SS(frm)->ss_port, FCT_HANDLE_NONE, 404 FRM_S_ID(frm), frm->frm_payload, cdb_size, 405 STMF_TASK_EXT_NONE); 406 if (cmd == NULL) { 407 FCOET_EXT_LOG(0, "can't get fcp cmd"); 408 return (NULL); 409 } 410 ffc = (fcoe_fcp_cmnd_t *)frm->frm_payload; 411 task_expected_len = FCOE_B2V_4(ffc->ffc_fcp_dl); 412 break; 413 414 default: 415 FCOET_EXT_LOG(0, "unsupported R_CTL: %x", r_ctl); 416 return (NULL); 417 } 418 419 /* 420 * xch initialization 421 */ 422 xch = CMD2XCH(cmd); 423 xch->xch_oxid = FRM_OXID(frm); 424 xch->xch_flags = 0; 425 xch->xch_ss = FRM2SS(frm); 426 xch->xch_cmd = cmd; 427 xch->xch_current_seq = NULL; 428 xch->xch_left_data_size = 0; 429 if (task_expected_len) { 430 xch->xch_dbuf_num = 431 (task_expected_len + FCOET_MAX_DBUF_LEN - 1) / 432 FCOET_MAX_DBUF_LEN; 433 xch->xch_dbufs = 434 kmem_zalloc(xch->xch_dbuf_num * sizeof (stmf_data_buf_t *), 435 KM_SLEEP); 436 } 437 xch->xch_start_time = ddi_get_lbolt(); 438 do { 439 xch->xch_rxid = atomic_add_16_nv( 440 &xch->xch_ss->ss_next_unsol_rxid, 1); 441 if (xch->xch_rxid == 0xFFFF) { 442 xch->xch_rxid = atomic_add_16_nv( 443 &xch->xch_ss->ss_next_unsol_rxid, 1); 444 } 445 } while (mod_hash_find(FRM2SS(frm)->ss_unsol_rxid_hash, 446 (mod_hash_key_t)(intptr_t)xch->xch_rxid, 447 (mod_hash_val_t)&xch_tmp) == 0); 448 449 xch->xch_sequence_no = 0; 450 xch->xch_ref = 0; 451 (void) mod_hash_insert(xch->xch_ss->ss_unsol_rxid_hash, 452 (mod_hash_key_t)(intptr_t)xch->xch_rxid, (mod_hash_val_t)xch); 453 xch->xch_flags |= XCH_FLAG_IN_HASH_TABLE; 454 455 /* 456 * cmd initialization 457 */ 458 cmd->cmd_port = FRM2SS(frm)->ss_port; 459 cmd->cmd_rp_handle = FCT_HANDLE_NONE; 460 cmd->cmd_rportid = FRM_S_ID(frm); 461 cmd->cmd_lportid = FRM_D_ID(frm); 462 cmd->cmd_oxid = xch->xch_oxid; 463 cmd->cmd_rxid = xch->xch_rxid; 464 465 fcoet_init_tfm(frm, xch); 466 return (xch); 467 } 468 469 int 470 fcoet_clear_unsol_exchange(fcoet_exchange_t *xch) 471 { 472 mod_hash_val_t val = NULL; 473 474 if (mod_hash_remove(xch->xch_ss->ss_unsol_rxid_hash, 475 (mod_hash_key_t)(intptr_t)xch->xch_rxid, &val) == 0) { 476 if (xch->xch_dbuf_num) { 477 kmem_free((void*)xch->xch_dbufs, 478 xch->xch_dbuf_num * sizeof (void *)); 479 xch->xch_dbufs = NULL; 480 xch->xch_dbuf_num = 0; 481 } 482 ASSERT(xch->xch_flags & XCH_FLAG_IN_HASH_TABLE); 483 ASSERT((fcoet_exchange_t *)val == xch); 484 xch->xch_flags &= ~XCH_FLAG_IN_HASH_TABLE; 485 return (FCOE_SUCCESS); 486 } 487 488 FCOET_LOG("fcoet_clear_unsol_exchange", "xch %p already cleared from " 489 "hash table", xch); 490 return (FCOE_FAILURE); 491 } 492 493 void 494 fcoet_clear_sol_exchange(fcoet_exchange_t *xch) 495 { 496 mod_hash_val_t val = NULL; 497 498 if (xch->xch_flags & XCH_FLAG_IN_HASH_TABLE) { 499 (void) mod_hash_remove(xch->xch_ss->ss_sol_oxid_hash, 500 (mod_hash_key_t)(intptr_t)xch->xch_oxid, &val); 501 ASSERT((fcoet_exchange_t *)val == xch); 502 xch->xch_flags &= ~XCH_FLAG_IN_HASH_TABLE; 503 } 504 } 505 506 static int 507 fcoet_process_sol_fcp_data(fcoe_frame_t *frm) 508 { 509 fcoet_exchange_t *xch = NULL; 510 fcoet_soft_state_t *ss = NULL; 511 fct_status_t fc_st; 512 uint32_t iof; 513 uint16_t unsol_rxid; 514 int sge_idx; 515 stmf_data_buf_t *dbuf; 516 int data_offset; 517 518 unsol_rxid = FRM_RXID(frm); 519 if (mod_hash_find_cb(FRM2SS(frm)->ss_unsol_rxid_hash, 520 (mod_hash_key_t)(intptr_t)unsol_rxid, 521 (mod_hash_val_t)&xch, fcoet_modhash_find_cb) != 0) { 522 return (FCOE_FAILURE); 523 } 524 525 /* 526 * we will always have a buf waiting there 527 */ 528 data_offset = FRM_PARAM(frm); 529 dbuf = xch->xch_dbufs[data_offset/FCOET_MAX_DBUF_LEN]; 530 ASSERT(dbuf); 531 ss = xch->xch_ss; 532 sge_idx = (data_offset % FCOET_MAX_DBUF_LEN)/ 533 ss->ss_fcp_data_payload_size; 534 535 ASSERT(((sge_idx < FCOET_GET_SEG_NUM(dbuf) - 1) && 536 (frm->frm_payload_size == ss->ss_fcp_data_payload_size)) || 537 ((sge_idx == FCOET_GET_SEG_NUM(dbuf) - 1) && 538 (frm->frm_payload_size % ss->ss_fcp_data_payload_size == 539 dbuf->db_data_size % ss->ss_fcp_data_payload_size))); 540 541 bcopy(frm->frm_payload, dbuf->db_sglist[sge_idx].seg_addr, 542 frm->frm_payload_size); 543 atomic_add_16(&dbuf->db_sglist_length, 1); 544 545 xch->xch_left_data_size -= frm->frm_payload_size; 546 if ((xch->xch_left_data_size <= 0) || 547 dbuf->db_sglist_length >= FCOET_GET_SEG_NUM(dbuf)) { 548 fc_st = FCT_SUCCESS; 549 iof = 0; 550 dbuf->db_xfer_status = fc_st; 551 dbuf->db_flags |= DB_DONT_REUSE; 552 fct_scsi_data_xfer_done(xch->xch_cmd, dbuf, iof); 553 } 554 555 FCOET_RELE_XCHG(xch); 556 return (FCOE_SUCCESS); 557 } 558 559 static int 560 fcoet_process_unsol_fcp_cmd(fcoe_frame_t *frm) 561 { 562 fcoet_exchange_t *xch; 563 fcoe_fcp_cmnd_t *ffc; 564 uint8_t tm; 565 scsi_task_t *task; 566 567 xch = fcoet_create_unsol_exchange(frm); 568 if (xch == NULL) { 569 FCOET_LOG("fcoet_process_unsol_fcp_cmd", "can't get exchange"); 570 return (FCOE_FAILURE); 571 } 572 573 ffc = (fcoe_fcp_cmnd_t *)frm->frm_payload; 574 task = XCH2TASK(xch); 575 task->task_csn_size = 8; 576 task->task_max_nbufs = 1; 577 task->task_cmd_seq_no = FCOE_B2V_1(ffc->ffc_ref_num); 578 task->task_flags = FCOE_B2V_1(ffc->ffc_attribute) & 0x07; 579 task->task_flags |= 580 (FCOE_B2V_1(ffc->ffc_addlen_rdwr) & 0x03) << 5; 581 task->task_expected_xfer_length = FCOE_B2V_4(ffc->ffc_fcp_dl); 582 583 tm = FCOE_B2V_1(ffc->ffc_management_flags); 584 if (tm) { 585 if (tm & BIT_1) { 586 task->task_mgmt_function = TM_ABORT_TASK_SET; 587 } else if (tm & BIT_2) { 588 task->task_mgmt_function = TM_CLEAR_TASK_SET; 589 } else if (tm & BIT_4) { 590 task->task_mgmt_function = TM_LUN_RESET; 591 } else if (tm & BIT_5) { 592 task->task_mgmt_function = TM_TARGET_COLD_RESET; 593 } else if (tm & BIT_6) { 594 task->task_mgmt_function = TM_CLEAR_ACA; 595 } else { 596 task->task_mgmt_function = TM_ABORT_TASK; 597 } 598 } 599 600 bcopy(ffc->ffc_cdb, task->task_cdb, 16); 601 fct_post_rcvd_cmd(xch->xch_cmd, NULL); 602 return (FCOE_SUCCESS); 603 } 604 /* 605 * It must be from link 606 * req_payload has been allocated when create_unsol_exchange 607 */ 608 static int 609 fcoet_process_unsol_els_req(fcoe_frame_t *frm) 610 { 611 int ret = FCOE_SUCCESS; 612 fcoet_exchange_t *xch; 613 614 xch = fcoet_create_unsol_exchange(frm); 615 ASSERT(xch); 616 ASSERT(FRM_IS_LAST_FRAME(frm)); 617 618 /* 619 * For the reason of keeping symmetric, we do copy here as in 620 * process_sol_els instead of in create_unsol_exchange. 621 * req_payload depends on how to allocate buf in create_unsol_exchange 622 */ 623 XCH2ELS(xch)->els_req_alloc_size = 0; 624 XCH2ELS(xch)->els_req_size = frm->frm_payload_size; 625 XCH2ELS(xch)->els_req_payload = 626 GET_BYTE_OFFSET(xch, GET_STRUCT_SIZE(fcoet_exchange_t)); 627 bcopy(frm->frm_payload, XCH2ELS(xch)->els_req_payload, 628 XCH2ELS(xch)->els_req_size); 629 if (XCH2ELS(xch)->els_req_payload[0] != ELS_OP_FLOGI) { 630 /* 631 * Ensure LINK_UP event has been handled, or PLOIG has 632 * been processed by FCT, or else it will be discarded. 633 * It need more consideration later ??? 634 */ 635 if ((XCH2ELS(xch)->els_req_payload[0] == ELS_OP_PLOGI) && 636 (xch->xch_ss->ss_flags & SS_FLAG_DELAY_PLOGI)) { 637 delay(STMF_SEC2TICK(1)/2); 638 } 639 640 if ((XCH2ELS(xch)->els_req_payload[0] == ELS_OP_PRLI) && 641 (xch->xch_ss->ss_flags & SS_FLAG_DELAY_PLOGI)) { 642 atomic_and_32(&xch->xch_ss->ss_flags, 643 ~SS_FLAG_DELAY_PLOGI); 644 delay(STMF_SEC2TICK(1)/3); 645 } 646 fct_post_rcvd_cmd(xch->xch_cmd, NULL); 647 } else { 648 /* 649 * We always handle FLOGI internally 650 * Save dst mac address from FLOGI request to restore later 651 */ 652 bcopy((char *)frm->frm_hdr-22, 653 frm->frm_eport->eport_efh_dst, ETHERADDRL); 654 ret = fcoet_process_unsol_flogi_req(xch); 655 } 656 return (ret); 657 } 658 659 660 /* 661 * It must be from link, but could be incomplete because of network problems 662 */ 663 static int 664 fcoet_process_sol_els_rsp(fcoe_frame_t *frm) 665 { 666 uint32_t actual_size; 667 fct_status_t fc_st; 668 uint32_t iof; 669 uint16_t sol_oxid; 670 fcoet_exchange_t *xch = NULL; 671 fct_els_t *els = NULL; 672 int ret = FCOE_SUCCESS; 673 674 sol_oxid = FRM_OXID(frm); 675 if (mod_hash_find_cb(FRM2SS(frm)->ss_sol_oxid_hash, 676 (mod_hash_key_t)(intptr_t)sol_oxid, 677 (mod_hash_val_t *)&xch, fcoet_modhash_find_cb) != 0) { 678 return (FCOE_FAILURE); 679 } 680 if (xch != FRM2SS(frm)->ss_sol_flogi) { 681 fcoet_clear_sol_exchange(xch); 682 } 683 684 fcoet_init_tfm(frm, xch); 685 els = CMD2ELS(xch->xch_cmd); 686 ASSERT(FRM_IS_LAST_FRAME(frm)); 687 actual_size = els->els_resp_size; 688 if (actual_size > frm->frm_payload_size) { 689 actual_size = frm->frm_payload_size; 690 } 691 692 els->els_resp_size = (uint16_t)actual_size; 693 bcopy(frm->frm_payload, els->els_resp_payload, actual_size); 694 695 if (xch->xch_ss->ss_sol_flogi == xch) { 696 /* 697 * We handle FLOGI internally 698 */ 699 ret = fcoet_process_sol_flogi_rsp(frm); 700 FCOET_RELE_XCHG(xch); 701 } else { 702 fc_st = FCT_SUCCESS; 703 iof = FCT_IOF_FCA_DONE; 704 FCOET_RELE_XCHG(xch); 705 fct_send_cmd_done(xch->xch_cmd, fc_st, iof); 706 } 707 return (ret); 708 } 709 710 /* 711 * It's still in the context of being aborted exchange, but FCT can't support 712 * this scheme, so there are two fct_cmd_t that are bound with one exchange. 713 */ 714 static int 715 fcoet_process_unsol_abts_req(fcoe_frame_t *frm) 716 { 717 fct_cmd_t *cmd; 718 fcoet_exchange_t *xch = NULL; 719 uint16_t unsol_rxid; 720 721 FCOET_LOG("fcoet_process_unsol_abts_req", "ABTS: %x/%x", 722 FRM_OXID(frm), FRM_RXID(frm)); 723 unsol_rxid = FRM_RXID(frm); 724 if (mod_hash_find_cb(FRM2SS(frm)->ss_unsol_rxid_hash, 725 (mod_hash_key_t)(intptr_t)unsol_rxid, 726 (mod_hash_val_t *)&xch, fcoet_modhash_find_cb) != 0) { 727 FCOET_LOG("fcoet_process_unsol_abts_req", 728 "can't find aborted exchange"); 729 return (FCOE_SUCCESS); 730 } 731 732 fcoet_init_tfm(frm, xch); 733 if (!FRM_IS_LAST_FRAME(frm)) { 734 FCOET_LOG("fcoet_process_unsol_abts_req", 735 "not supported this kind frame"); 736 FCOET_RELE_XCHG(xch); 737 return (FCOE_FAILURE); 738 } 739 740 cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_RCVD_ABTS, 0, 0); 741 if (cmd == NULL) { 742 FCOET_LOG("fcoet_process_unsol_abts_req", 743 "can't alloc fct_cmd_t"); 744 FCOET_RELE_XCHG(xch); 745 return (FCOE_FAILURE); 746 } 747 748 xch->xch_flags |= XCH_FLAG_INI_ASKED_ABORT; 749 cmd->cmd_fca_private = xch; 750 cmd->cmd_port = xch->xch_cmd->cmd_port; 751 cmd->cmd_rp_handle = xch->xch_cmd->cmd_rp_handle; 752 cmd->cmd_rportid = xch->xch_cmd->cmd_rportid; 753 cmd->cmd_lportid = xch->xch_cmd->cmd_lportid; 754 cmd->cmd_oxid = xch->xch_cmd->cmd_oxid; 755 cmd->cmd_rxid = xch->xch_cmd->cmd_rxid; 756 fct_post_rcvd_cmd(cmd, NULL); 757 FCOET_LOG("fcoet_process_unsol_abts_req", 758 "abts now: xch/%p, frm/%p - time/%p", 759 xch, frm, ddi_get_lbolt()); 760 761 FCOET_RELE_XCHG(xch); 762 return (FCOE_SUCCESS); 763 } 764 765 static int 766 fcoet_process_sol_abts_acc(fcoe_frame_t *frm) 767 { 768 fcoet_exchange_t *xch = NULL; 769 uint16_t sol_oxid; 770 771 sol_oxid = FRM_OXID(frm); 772 if (mod_hash_remove(FRM2SS(frm)->ss_sol_oxid_hash, 773 (mod_hash_key_t)(intptr_t)sol_oxid, 774 (mod_hash_val_t *)&xch) != 0) { 775 /* 776 * So far ABTS for FLOGI might be removed from ss_sol_oxid_hash 777 * in fcoet_watch_handle_sol_flogi, Will improve it later 778 */ 779 return (FCOE_SUCCESS); 780 } 781 782 xch->xch_flags &= ~XCH_FLAG_IN_HASH_TABLE; 783 if (!FRM_IS_LAST_FRAME(frm)) { 784 FCOET_LOG("fcoet_process_sol_abts_acc", 785 "not supported this kind frame"); 786 FCOET_RELE_XCHG(xch); 787 return (FCOE_FAILURE); 788 } 789 FCOET_LOG("fcoet_process_sol_abts_acc", 790 "ABTS received but there is nothing to do"); 791 return (FCOE_SUCCESS); 792 } 793 794 static int 795 fcoet_process_sol_abts_rjt(fcoe_frame_t *frm) 796 { 797 fcoet_exchange_t *xch = NULL; 798 uint16_t sol_oxid; 799 800 sol_oxid = FRM_OXID(frm); 801 if (mod_hash_remove(FRM2SS(frm)->ss_sol_oxid_hash, 802 (mod_hash_key_t)(intptr_t)sol_oxid, 803 (mod_hash_val_t *)&xch) != 0) { 804 /* 805 * So far ABTS for FLOGI might be removed from ss_sol_oxid_hash 806 * in fcoet_watch_handle_sol_flogi, Will improve it later 807 */ 808 return (FCOE_SUCCESS); 809 } 810 811 xch->xch_flags &= ~XCH_FLAG_IN_HASH_TABLE; 812 813 if (!FRM_IS_LAST_FRAME(frm)) { 814 FCOET_LOG("fcoet_process_sol_abts_rjt", 815 "not supported this kind frame"); 816 return (FCOE_FAILURE); 817 } 818 819 FCOET_LOG("fcoet_process_sol_abts_rjt", 820 "ABTS_RJT received rjt reason %x but there is nothing to do", 821 frm->frm_payload[1]); 822 return (FCOE_SUCCESS); 823 } 824 825 static int 826 fcoet_process_unsol_ct_req(fcoe_frame_t *frm) 827 { 828 /* 829 * If you want to implement virtual name server, or FC/ETH 830 * gateway, you can do it here 831 */ 832 if (!FRM_IS_LAST_FRAME(frm)) { 833 FCOET_LOG("fcoet_process_unsol_ct_req", 834 "not supported this kind frame"); 835 return (FCOE_FAILURE); 836 } 837 838 FCOET_LOG("fcoet_process_unsol_ct_req", 839 "No support for unsolicited CT request"); 840 return (FCOE_SUCCESS); 841 } 842 843 static int 844 fcoet_process_sol_ct_rsp(fcoe_frame_t *frm) 845 { 846 uint32_t actual_size; 847 fct_status_t fc_st; 848 uint32_t iof; 849 fct_sol_ct_t *ct = NULL; 850 fcoet_exchange_t *xch = NULL; 851 uint16_t sol_oxid; 852 853 sol_oxid = FRM_OXID(frm); 854 855 if (mod_hash_remove(FRM2SS(frm)->ss_sol_oxid_hash, 856 (mod_hash_key_t)(intptr_t)sol_oxid, 857 (mod_hash_val_t *)&xch) != 0) { 858 return (FCOE_SUCCESS); 859 } 860 861 xch->xch_flags &= ~XCH_FLAG_IN_HASH_TABLE; 862 fcoet_init_tfm(frm, xch); 863 864 ASSERT(FRM_IS_LAST_FRAME(frm)); 865 actual_size = CMD2ELS(xch->xch_cmd)->els_resp_size; 866 if (actual_size > frm->frm_payload_size) { 867 actual_size = frm->frm_payload_size; 868 } 869 ct = CMD2CT(xch->xch_cmd); 870 ct->ct_resp_size = (uint16_t)actual_size; 871 872 bcopy(frm->frm_payload, 873 CMD2CT(xch->xch_cmd)->ct_resp_payload, actual_size); 874 875 fc_st = FCT_SUCCESS; 876 iof = FCT_IOF_FCA_DONE; 877 fct_send_cmd_done(xch->xch_cmd, fc_st, iof); 878 879 return (FCOE_SUCCESS); 880 } 881 882 static int 883 fcoet_send_sol_fcp_data_done(fcoe_frame_t *frm) 884 { 885 fcoet_exchange_t *xch = FRM2TFM(frm)->tfm_xch; 886 stmf_data_buf_t *dbuf; 887 int dbuf_index; 888 uint32_t iof; 889 890 dbuf_index = FRM2TFM(frm)->tfm_buf_idx; 891 xch->xch_left_data_size -= frm->frm_payload_size; 892 dbuf = xch->xch_dbufs[dbuf_index]; 893 ASSERT((dbuf) && (dbuf->db_flags & DB_DIRECTION_TO_RPORT)); 894 895 /* 896 * We decrease db_sglist_length only for READ-type commands. 897 * For INQUIRY, resid could be non-zero, then db_sglist_length will 898 * be useful. 899 */ 900 dbuf->db_sglist_length--; 901 if ((xch->xch_left_data_size <= 0) || (!dbuf->db_sglist_length)) { 902 iof = 0; 903 dbuf->db_xfer_status = FCT_SUCCESS; 904 dbuf->db_flags |= DB_DONT_REUSE; 905 if (dbuf->db_flags & DB_SEND_STATUS_GOOD) { 906 if (fcoet_send_status(xch->xch_cmd) != FCT_SUCCESS) { 907 return (FCOE_FAILURE); 908 } 909 } else { 910 fct_scsi_data_xfer_done(xch->xch_cmd, dbuf, iof); 911 } 912 } 913 FCOET_RELE_XCHG(xch); 914 return (FCOE_SUCCESS); 915 } 916 917 static int 918 fcoet_send_fcp_status_done(fcoe_frame_t *frm) 919 { 920 fcoet_exchange_t *xch = FRM2TFM(frm)->tfm_xch; 921 fct_status_t fc_st = FCT_SUCCESS; 922 uint32_t iof = FCT_IOF_FCA_DONE; 923 924 if (xch->xch_flags & XCH_FLAG_FCT_CALLED_ABORT) { 925 FCOET_RELE_XCHG(xch); 926 return (FCOE_SUCCESS); 927 } 928 929 if (fcoet_clear_unsol_exchange(xch) == FCOE_SUCCESS) { 930 FCOET_RELE_XCHG(xch); 931 fct_send_response_done(xch->xch_cmd, fc_st, iof); 932 } else { 933 /* Already cleared from hash table by abort */ 934 FCOET_RELE_XCHG(xch); 935 } 936 937 return (FCOE_SUCCESS); 938 } 939 940 /* 941 * Solicited frames callback area 942 */ 943 static int 944 fcoet_send_unsol_els_rsp_done(fcoe_frame_t *frm) 945 { 946 fcoet_exchange_t *xch = FRM2TFM(frm)->tfm_xch; 947 fct_status_t fc_st; 948 uint32_t iof; 949 950 FCOET_EXT_LOG("fcoet_send_unsol_els_rsp_done", 951 "frm/oxid/els: %p/%x/%x", 952 frm, FRM_OXID(frm), XCH2ELS(xch)->els_req_payload[0]); 953 if (xch->xch_flags & XCH_FLAG_FCT_CALLED_ABORT) { 954 FCOET_RELE_XCHG(xch); 955 return (FCOE_SUCCESS); 956 } 957 958 if (fcoet_clear_unsol_exchange(xch) == FCOE_FAILURE) { 959 FCOET_RELE_XCHG(xch); 960 return (FCOE_SUCCESS); 961 } 962 963 FCOET_RELE_XCHG(xch); 964 if (XCH2ELS(xch)->els_req_payload[0] != ELS_OP_FLOGI) { 965 fc_st = FCT_SUCCESS; 966 iof = FCT_IOF_FCA_DONE; 967 fct_send_response_done(xch->xch_cmd, fc_st, iof); 968 } else { 969 /* 970 * We need update ss_link_info and flags. 971 */ 972 mutex_enter(&xch->xch_ss->ss_watch_mutex); 973 xch->xch_ss->ss_link_info.portid = 974 xch->xch_cmd->cmd_lportid; 975 xch->xch_ss->ss_link_info.port_topology = 976 PORT_TOPOLOGY_PT_TO_PT; 977 if (frm->frm_eport->eport_link_speed == FCOE_PORT_SPEED_1G) { 978 xch->xch_ss->ss_link_info.port_speed = PORT_SPEED_1G; 979 } else if (frm->frm_eport->eport_link_speed == 980 FCOE_PORT_SPEED_10G) { 981 xch->xch_ss->ss_link_info.port_speed = PORT_SPEED_10G; 982 } 983 xch->xch_ss->ss_link_info.port_no_fct_flogi = 1; 984 xch->xch_ss->ss_link_info.port_fca_flogi_done = 1; 985 xch->xch_ss->ss_link_info.port_fct_flogi_done = 0; 986 bcopy(XCH2ELS(xch)->els_req_payload + 20, 987 xch->xch_ss->ss_link_info.port_rpwwn, 8); 988 bcopy(XCH2ELS(xch)->els_req_payload + 28, 989 xch->xch_ss->ss_link_info.port_rnwwn, 8); 990 atomic_or_32(&xch->xch_ss->ss_flags, 991 SS_FLAG_UNSOL_FLOGI_DONE); 992 atomic_or_32(&xch->xch_ss->ss_flags, 993 SS_FLAG_REPORT_TO_FCT); 994 995 xch->xch_ss->ss_sol_flogi_state = SFS_FLOGI_ACC; 996 mutex_exit(&xch->xch_ss->ss_watch_mutex); 997 998 fct_free(xch->xch_cmd); 999 } 1000 return (FCOE_SUCCESS); 1001 } 1002 1003 /* ARGSUSED */ 1004 static int 1005 fcoet_send_sol_els_req_done(fcoe_frame_t *frm) 1006 { 1007 return (FCOE_SUCCESS); 1008 } 1009 1010 /* 1011 * FCT have released relevant fct_cmd_t and fcoet_exchange_t now, so it's not 1012 * needed to notify FCT anything. Just do nothing. 1013 */ 1014 /* ARGSUSED */ 1015 static int 1016 fcoet_send_unsol_bls_acc_done(fcoe_frame_t *frm) 1017 { 1018 FCOET_LOG("fcoet_send_unsol_bls_acc_done", 1019 "Unsolicited BA_ACC sent out and released "); 1020 1021 return (FCOE_SUCCESS); 1022 } 1023 1024 /* ARGSUSED */ 1025 static int 1026 fcoet_send_unsol_bls_rjt_done(fcoe_frame_t *frm) 1027 { 1028 FCOET_LOG("fcoet_send_unsol_bls_rjt_done", 1029 "Unsolicited BA_RJT sent out and released"); 1030 return (FCOE_SUCCESS); 1031 } 1032 1033 /* ARGSUSED */ 1034 static int 1035 fcoet_send_sol_bls_req_done(fcoe_frame_t *frm) 1036 { 1037 FCOET_LOG("fcoet_send_sol_bls_req_done", 1038 "Soclited ABTS was sent out and released"); 1039 return (FCOE_SUCCESS); 1040 } 1041 1042 /* ARGSUSED */ 1043 static int 1044 fcoet_send_sol_ct_req_done(fcoe_frame_t *frm) 1045 { 1046 FCOET_LOG("fcoet_send_sol_ct_req_done", 1047 "CT request was sent out and released"); 1048 return (FCOE_SUCCESS); 1049 } 1050 1051 /* 1052 * FCoET can only interpret solicited and unsolicited FLOGI, all the other 1053 * ELS/CT/FCP should be passed up to FCT. 1054 */ 1055 static int 1056 fcoet_process_unsol_flogi_req(fcoet_exchange_t *xch) 1057 { 1058 fcoe_frame_t *frm; 1059 1060 atomic_or_32(&xch->xch_ss->ss_flags, SS_FLAG_DELAY_PLOGI); 1061 1062 /* 1063 * In spec, common service parameter should indicate if it's from 1064 * N-port or F-port, but the initial intel implementation is not 1065 * spec-compliant, so we use eport_flags to workaround the problem 1066 */ 1067 if (!(xch->xch_ss->ss_eport->eport_flags & EPORT_FLAG_IS_DIRECT_P2P)) { 1068 /* 1069 * The topology is switch P2P, so there's no need to respond 1070 * to this FLOGI 1071 */ 1072 FCOET_LOG("fcoet_process_unsol_flogi_req", 1073 "skip FLOGI, since we are in switch topology"); 1074 return (FCOE_SUCCESS); 1075 } 1076 1077 /* 1078 * Send ACC according to the spec. 1079 */ 1080 frm = xch->xch_ss->ss_eport->eport_alloc_frame(xch->xch_ss->ss_eport, 1081 FLOGI_ACC_PAYLOAD_SIZE + FCFH_SIZE, 0); 1082 if (frm == NULL) { 1083 ASSERT(0); 1084 return (FCOE_FAILURE); 1085 } else { 1086 fcoet_init_tfm(frm, xch); 1087 bzero(frm->frm_payload, frm->frm_payload_size); 1088 } 1089 1090 FFM_R_CTL(0x23, frm); 1091 FRM2TFM(frm)->tfm_rctl = 0x23; 1092 FFM_TYPE(0x01, frm); 1093 FFM_F_CTL(0x980000, frm); 1094 FFM_OXID(xch->xch_oxid, frm); 1095 FFM_RXID(xch->xch_rxid, frm); 1096 FFM_S_ID(0xFFFFFE, frm); 1097 1098 /* 1099 * ACC 1100 */ 1101 frm->frm_payload[0] = 0x02; 1102 1103 /* 1104 * Common Svc Parameters 1105 */ 1106 frm->frm_payload[4] = 0x20; 1107 frm->frm_payload[5] = 0x20; 1108 frm->frm_payload[7] = 0x0A; 1109 frm->frm_payload[10] = 0x05; 1110 frm->frm_payload[11] = 0xAC; 1111 bcopy(xch->xch_ss->ss_eport->eport_portwwn, frm->frm_payload + 20, 8); 1112 bcopy(xch->xch_ss->ss_eport->eport_nodewwn, frm->frm_payload + 28, 8); 1113 1114 /* 1115 * Class3 Svc Parameters 1116 */ 1117 frm->frm_payload[68] = 0x88; 1118 1119 /* 1120 * Send FLOGI ACC out 1121 * After this, we should never use the exchange, because it could 1122 * have been released. Please pay attention to other similiar cases. 1123 */ 1124 xch->xch_ss->ss_eport->eport_tx_frame(frm); 1125 return (FCOE_SUCCESS); 1126 } 1127 1128 static int 1129 fcoet_process_sol_flogi_rsp(fcoe_frame_t *frm) 1130 { 1131 int ret = FCOE_SUCCESS; 1132 fcoet_exchange_t *xch = FRM2TFM(frm)->tfm_xch; 1133 fct_els_t *els = CMD2ELS(xch->xch_cmd); 1134 fcoet_soft_state_t *ss = FRM2SS(frm); 1135 1136 if (els->els_resp_payload[0] == ELS_OP_ACC) { 1137 /* 1138 * We need always update ss_link_info and flags for solicited 1139 * FLOGI, because someone has assigned address for you. The 1140 * initial intel implementation will always assign address for 1141 * you even you are in back-to-back mode (direct P2P). 1142 */ 1143 mutex_enter(&ss->ss_watch_mutex); 1144 if (ss->ss_flags & SS_FLAG_PORT_DISABLED || 1145 (ss->ss_sol_flogi_state != SFS_FLOGI_INIT && 1146 ss->ss_sol_flogi_state != SFS_FLOGI_CHECK_TIMEOUT && 1147 ss->ss_sol_flogi_state != SFS_ABTS_INIT)) { 1148 /* 1149 * The status is not correct, this response may be 1150 * obsolete. 1151 */ 1152 mutex_exit(&ss->ss_watch_mutex); 1153 FCOET_LOG("fcoet_process_sol_flogi_rsp", 1154 "FLOGI response is obsolete"); 1155 return (FCOE_FAILURE); 1156 } 1157 if (xch->xch_flags & XCH_FLAG_NONFCP_REQ_SENT) { 1158 xch->xch_cmd->cmd_lportid = FRM_D_ID(frm); 1159 xch->xch_ss->ss_link_info.portid = 1160 xch->xch_cmd->cmd_lportid; 1161 /* 1162 * Check the bit 28 in 3rd word of the payload 1163 * in common service parameters to know the 1164 * remote port is F_PORT or N_PORT 1165 */ 1166 if (els->els_resp_payload[8] & 0x10) { 1167 uint8_t src_addr[ETHERADDRL]; 1168 frm->frm_eport->eport_flags &= 1169 ~EPORT_FLAG_IS_DIRECT_P2P; 1170 FCOE_SET_DEFAULT_OUI(src_addr); 1171 bcopy(frm->frm_hdr->hdr_d_id, src_addr + 3, 3); 1172 bcopy((char *)frm->frm_hdr-22, 1173 frm->frm_eport->eport_efh_dst, 1174 ETHERADDRL); 1175 frm->frm_eport->eport_set_mac_address( 1176 frm->frm_eport, src_addr, B_TRUE); 1177 xch->xch_ss->ss_link_info.port_topology = 1178 PORT_TOPOLOGY_FABRIC_PT_TO_PT; 1179 } else { 1180 xch->xch_ss->ss_link_info.port_topology = 1181 PORT_TOPOLOGY_PT_TO_PT; 1182 } 1183 1184 xch->xch_ss->ss_link_info.port_speed = PORT_SPEED_10G; 1185 xch->xch_ss->ss_link_info.port_no_fct_flogi = 1; 1186 xch->xch_ss->ss_link_info.port_fca_flogi_done = 1; 1187 xch->xch_ss->ss_link_info.port_fct_flogi_done = 0; 1188 xch->xch_ss->ss_sol_flogi_state = SFS_FLOGI_ACC; 1189 cv_signal(&xch->xch_ss->ss_watch_cv); 1190 FCOET_LOG("fcoet_process_sol_flogi_rsp", 1191 "FLOGI is accecpted"); 1192 } else { 1193 FCOET_LOG("fcoet_process_sol_flogi_rsp", 1194 "FLOGI xch_flags/%x", xch->xch_flags); 1195 ret = FCOE_FAILURE; 1196 } 1197 mutex_exit(&ss->ss_watch_mutex); 1198 } else { 1199 FCOET_LOG("fcoet_process_sol_flogi_rsp", "FLOGI is rejected"); 1200 ret = FCOE_FAILURE; 1201 } 1202 return (ret); 1203 } 1204