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