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 #include <sys/cpuvar.h> 28 #include <sys/ddi.h> 29 #include <sys/sunddi.h> 30 #include <sys/modctl.h> 31 #include <sys/socket.h> 32 #include <sys/strsubr.h> 33 #include <sys/note.h> 34 #include <sys/sdt.h> 35 36 #define IDM_CONN_SM_STRINGS 37 #define IDM_CN_NOTIFY_STRINGS 38 #include <sys/idm/idm.h> 39 40 boolean_t idm_sm_logging = B_FALSE; 41 42 extern idm_global_t idm; /* Global state */ 43 44 static void 45 idm_conn_event_handler(void *event_ctx_opaque); 46 47 static void 48 idm_state_s1_free(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 49 50 static void 51 idm_state_s2_xpt_wait(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 52 53 static void 54 idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 55 56 static void 57 idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 58 59 static void 60 idm_state_s5_logged_in(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 61 62 static void 63 idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 64 65 static void 66 idm_logout_req_timeout(void *arg); 67 68 static void 69 idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 70 71 static void 72 idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 73 74 static void 75 idm_state_s9_init_error(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 76 77 static void 78 idm_state_s9a_rejected(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 79 80 static void 81 idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 82 83 static void 84 idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 85 86 static void 87 idm_state_s12_enable_dm(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 88 89 static void 90 idm_update_state(idm_conn_t *ic, idm_conn_state_t new_state, 91 idm_conn_event_ctx_t *event_ctx); 92 93 static void 94 idm_conn_unref(void *ic_void); 95 96 static void 97 idm_conn_reject_unref(void *ic_void); 98 99 static idm_pdu_event_action_t 100 idm_conn_sm_validate_pdu(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx, 101 idm_pdu_t *pdu); 102 103 static idm_status_t 104 idm_ffp_enable(idm_conn_t *ic); 105 106 static void 107 idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type); 108 109 static void 110 idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 111 112 static void 113 idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx); 114 115 idm_status_t 116 idm_conn_sm_init(idm_conn_t *ic) 117 { 118 char taskq_name[32]; 119 120 /* 121 * Caller should have assigned a unique connection ID. Use this 122 * connection ID to create a unique connection name string 123 */ 124 ASSERT(ic->ic_internal_cid != 0); 125 (void) snprintf(taskq_name, sizeof (taskq_name) - 1, "conn_sm%08x", 126 ic->ic_internal_cid); 127 128 ic->ic_state_taskq = taskq_create(taskq_name, 1, minclsyspri, 4, 16384, 129 TASKQ_PREPOPULATE); 130 if (ic->ic_state_taskq == NULL) { 131 return (IDM_STATUS_FAIL); 132 } 133 134 idm_sm_audit_init(&ic->ic_state_audit); 135 mutex_init(&ic->ic_state_mutex, NULL, MUTEX_DEFAULT, NULL); 136 cv_init(&ic->ic_state_cv, NULL, CV_DEFAULT, NULL); 137 138 ic->ic_state = CS_S1_FREE; 139 ic->ic_last_state = CS_S1_FREE; 140 141 return (IDM_STATUS_SUCCESS); 142 } 143 144 void 145 idm_conn_sm_fini(idm_conn_t *ic) 146 { 147 148 /* 149 * The connection may only be partially created. If there 150 * is no taskq, then the connection SM was not initialized. 151 */ 152 if (ic->ic_state_taskq == NULL) { 153 return; 154 } 155 156 taskq_destroy(ic->ic_state_taskq); 157 158 cv_destroy(&ic->ic_state_cv); 159 /* 160 * The thread that generated the event that got us here may still 161 * hold the ic_state_mutex. Once it is released we can safely 162 * destroy it since there is no way to locate the object now. 163 */ 164 mutex_enter(&ic->ic_state_mutex); 165 mutex_destroy(&ic->ic_state_mutex); 166 } 167 168 void 169 idm_conn_event(idm_conn_t *ic, idm_conn_event_t event, uintptr_t event_info) 170 { 171 mutex_enter(&ic->ic_state_mutex); 172 idm_conn_event_locked(ic, event, event_info, CT_NONE); 173 mutex_exit(&ic->ic_state_mutex); 174 } 175 176 177 idm_status_t 178 idm_conn_reinstate_event(idm_conn_t *old_ic, idm_conn_t *new_ic) 179 { 180 int result; 181 182 mutex_enter(&old_ic->ic_state_mutex); 183 if (((old_ic->ic_conn_type == CONN_TYPE_INI) && 184 (old_ic->ic_state != CS_S8_CLEANUP)) || 185 ((old_ic->ic_conn_type == CONN_TYPE_TGT) && 186 (old_ic->ic_state < CS_S5_LOGGED_IN))) { 187 result = IDM_STATUS_FAIL; 188 } else { 189 result = IDM_STATUS_SUCCESS; 190 new_ic->ic_reinstate_conn = old_ic; 191 idm_conn_event_locked(new_ic->ic_reinstate_conn, 192 CE_CONN_REINSTATE, (uintptr_t)new_ic, CT_NONE); 193 } 194 mutex_exit(&old_ic->ic_state_mutex); 195 196 return (result); 197 } 198 199 void 200 idm_conn_tx_pdu_event(idm_conn_t *ic, idm_conn_event_t event, 201 uintptr_t event_info) 202 { 203 ASSERT(mutex_owned(&ic->ic_state_mutex)); 204 ic->ic_pdu_events++; 205 idm_conn_event_locked(ic, event, event_info, CT_TX_PDU); 206 } 207 208 void 209 idm_conn_rx_pdu_event(idm_conn_t *ic, idm_conn_event_t event, 210 uintptr_t event_info) 211 { 212 ASSERT(mutex_owned(&ic->ic_state_mutex)); 213 ic->ic_pdu_events++; 214 idm_conn_event_locked(ic, event, event_info, CT_RX_PDU); 215 } 216 217 void 218 idm_conn_event_locked(idm_conn_t *ic, idm_conn_event_t event, 219 uintptr_t event_info, idm_pdu_event_type_t pdu_event_type) 220 { 221 idm_conn_event_ctx_t *event_ctx; 222 223 idm_sm_audit_event(&ic->ic_state_audit, SAS_IDM_CONN, 224 (int)ic->ic_state, (int)event, event_info); 225 226 /* 227 * It's very difficult to prevent a few straggling events 228 * at the end. For example idm_sorx_thread will generate 229 * a CE_TRANSPORT_FAIL event when it exits. Rather than 230 * push complicated restrictions all over the code to 231 * prevent this we will simply drop the events (and in 232 * the case of PDU events release them appropriately) 233 * since they are irrelevant once we are in a terminal state. 234 * Of course those threads need to have appropriate holds on 235 * the connection otherwise it might disappear. 236 */ 237 if ((ic->ic_state == CS_S9_INIT_ERROR) || 238 (ic->ic_state == CS_S9A_REJECTED) || 239 (ic->ic_state == CS_S11_COMPLETE)) { 240 if ((pdu_event_type == CT_TX_PDU) || 241 (pdu_event_type == CT_RX_PDU)) { 242 ic->ic_pdu_events--; 243 idm_pdu_complete((idm_pdu_t *)event_info, 244 IDM_STATUS_SUCCESS); 245 } 246 IDM_SM_LOG(CE_NOTE, "*** Dropping event %s (%d) because of" 247 "state %s (%d)", 248 idm_ce_name[event], event, 249 idm_cs_name[ic->ic_state], ic->ic_state); 250 return; 251 } 252 253 /* 254 * Normal event handling 255 */ 256 idm_conn_hold(ic); 257 258 event_ctx = kmem_zalloc(sizeof (*event_ctx), KM_SLEEP); 259 event_ctx->iec_ic = ic; 260 event_ctx->iec_event = event; 261 event_ctx->iec_info = event_info; 262 event_ctx->iec_pdu_event_type = pdu_event_type; 263 264 (void) taskq_dispatch(ic->ic_state_taskq, &idm_conn_event_handler, 265 event_ctx, TQ_SLEEP); 266 } 267 268 static void 269 idm_conn_event_handler(void *event_ctx_opaque) 270 { 271 idm_conn_event_ctx_t *event_ctx = event_ctx_opaque; 272 idm_conn_t *ic = event_ctx->iec_ic; 273 idm_pdu_t *pdu = (idm_pdu_t *)event_ctx->iec_info; 274 idm_pdu_event_action_t action; 275 276 IDM_SM_LOG(CE_NOTE, "idm_conn_event_handler: conn %p event %s(%d)", 277 (void *)ic, idm_ce_name[event_ctx->iec_event], 278 event_ctx->iec_event); 279 DTRACE_PROBE2(conn__event, 280 idm_conn_t *, ic, idm_conn_event_ctx_t *, event_ctx); 281 282 /* 283 * Validate event 284 */ 285 ASSERT(event_ctx->iec_event != CE_UNDEFINED); 286 ASSERT3U(event_ctx->iec_event, <, CE_MAX_EVENT); 287 288 /* 289 * Validate current state 290 */ 291 ASSERT(ic->ic_state != CS_S0_UNDEFINED); 292 ASSERT3U(ic->ic_state, <, CS_MAX_STATE); 293 294 /* 295 * Validate PDU-related events against the current state. If a PDU 296 * is not allowed in the current state we change the event to a 297 * protocol error. This simplifies the state-specific event handlers. 298 * For example the CS_S2_XPT_WAIT state only needs to handle the 299 * CE_TX_PROTOCOL_ERROR and CE_RX_PROTOCOL_ERROR events since 300 * no PDU's can be transmitted or received in that state. 301 */ 302 event_ctx->iec_pdu_forwarded = B_FALSE; 303 if (event_ctx->iec_pdu_event_type != CT_NONE) { 304 ASSERT(pdu != NULL); 305 action = idm_conn_sm_validate_pdu(ic, event_ctx, pdu); 306 307 switch (action) { 308 case CA_TX_PROTOCOL_ERROR: 309 /* 310 * Change event and forward the PDU 311 */ 312 event_ctx->iec_event = CE_TX_PROTOCOL_ERROR; 313 break; 314 case CA_RX_PROTOCOL_ERROR: 315 /* 316 * Change event and forward the PDU. 317 */ 318 event_ctx->iec_event = CE_RX_PROTOCOL_ERROR; 319 break; 320 case CA_FORWARD: 321 /* 322 * Let the state-specific event handlers take 323 * care of it. 324 */ 325 break; 326 case CA_DROP: 327 /* 328 * It never even happened 329 */ 330 IDM_SM_LOG(CE_NOTE, "*** drop PDU %p", (void *) pdu); 331 idm_pdu_complete(pdu, IDM_STATUS_FAIL); 332 break; 333 default: 334 ASSERT(0); 335 break; 336 } 337 } 338 339 switch (ic->ic_state) { 340 case CS_S1_FREE: 341 idm_state_s1_free(ic, event_ctx); 342 break; 343 case CS_S2_XPT_WAIT: 344 idm_state_s2_xpt_wait(ic, event_ctx); 345 break; 346 case CS_S3_XPT_UP: 347 idm_state_s3_xpt_up(ic, event_ctx); 348 break; 349 case CS_S4_IN_LOGIN: 350 idm_state_s4_in_login(ic, event_ctx); 351 break; 352 case CS_S5_LOGGED_IN: 353 idm_state_s5_logged_in(ic, event_ctx); 354 break; 355 case CS_S6_IN_LOGOUT: 356 idm_state_s6_in_logout(ic, event_ctx); 357 break; 358 case CS_S7_LOGOUT_REQ: 359 idm_state_s7_logout_req(ic, event_ctx); 360 break; 361 case CS_S8_CLEANUP: 362 idm_state_s8_cleanup(ic, event_ctx); 363 break; 364 case CS_S9A_REJECTED: 365 idm_state_s9a_rejected(ic, event_ctx); 366 break; 367 case CS_S9_INIT_ERROR: 368 idm_state_s9_init_error(ic, event_ctx); 369 break; 370 case CS_S10_IN_CLEANUP: 371 idm_state_s10_in_cleanup(ic, event_ctx); 372 break; 373 case CS_S11_COMPLETE: 374 idm_state_s11_complete(ic, event_ctx); 375 break; 376 case CS_S12_ENABLE_DM: 377 idm_state_s12_enable_dm(ic, event_ctx); 378 break; 379 default: 380 ASSERT(0); 381 break; 382 } 383 384 /* 385 * Now that we've updated the state machine, if this was 386 * a PDU-related event take the appropriate action on the PDU 387 * (transmit it, forward it to the clients RX callback, drop 388 * it, etc). 389 */ 390 if (event_ctx->iec_pdu_event_type != CT_NONE) { 391 switch (action) { 392 case CA_TX_PROTOCOL_ERROR: 393 idm_pdu_tx_protocol_error(ic, pdu); 394 break; 395 case CA_RX_PROTOCOL_ERROR: 396 idm_pdu_rx_protocol_error(ic, pdu); 397 break; 398 case CA_FORWARD: 399 if (!event_ctx->iec_pdu_forwarded) { 400 if (event_ctx->iec_pdu_event_type == 401 CT_RX_PDU) { 402 idm_pdu_rx_forward(ic, pdu); 403 } else { 404 idm_pdu_tx_forward(ic, pdu); 405 } 406 } 407 break; 408 default: 409 ASSERT(0); 410 break; 411 } 412 } 413 414 /* 415 * Update outstanding PDU event count (see idm_pdu_tx for 416 * how this is used) 417 */ 418 if ((event_ctx->iec_pdu_event_type == CT_TX_PDU) || 419 (event_ctx->iec_pdu_event_type == CT_RX_PDU)) { 420 mutex_enter(&ic->ic_state_mutex); 421 ic->ic_pdu_events--; 422 mutex_exit(&ic->ic_state_mutex); 423 } 424 425 idm_conn_rele(ic); 426 kmem_free(event_ctx, sizeof (*event_ctx)); 427 } 428 429 static void 430 idm_state_s1_free(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 431 { 432 switch (event_ctx->iec_event) { 433 case CE_CONNECT_REQ: 434 /* T1 */ 435 idm_update_state(ic, CS_S2_XPT_WAIT, event_ctx); 436 break; 437 case CE_CONNECT_ACCEPT: 438 /* T3 */ 439 idm_update_state(ic, CS_S3_XPT_UP, event_ctx); 440 break; 441 case CE_TX_PROTOCOL_ERROR: 442 case CE_RX_PROTOCOL_ERROR: 443 /* This should never happen */ 444 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 445 break; 446 default: 447 ASSERT(0); 448 /*NOTREACHED*/ 449 } 450 } 451 452 453 static void 454 idm_state_s2_xpt_wait(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 455 { 456 switch (event_ctx->iec_event) { 457 case CE_CONNECT_SUCCESS: 458 /* T4 */ 459 idm_update_state(ic, CS_S4_IN_LOGIN, event_ctx); 460 break; 461 case CE_TRANSPORT_FAIL: 462 case CE_CONNECT_FAIL: 463 case CE_LOGOUT_OTHER_CONN_RCV: 464 case CE_TX_PROTOCOL_ERROR: 465 case CE_RX_PROTOCOL_ERROR: 466 /* T2 */ 467 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 468 break; 469 default: 470 ASSERT(0); 471 /*NOTREACHED*/ 472 } 473 } 474 475 476 static void 477 idm_login_timeout(void *arg) 478 { 479 idm_conn_t *ic = arg; 480 481 idm_conn_event(ic, CE_LOGIN_TIMEOUT, NULL); 482 } 483 484 static void 485 idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 486 { 487 switch (event_ctx->iec_event) { 488 case CE_LOGIN_RCV: 489 /* T4 */ 490 idm_initial_login_actions(ic, event_ctx); 491 idm_update_state(ic, CS_S4_IN_LOGIN, event_ctx); 492 break; 493 case CE_LOGIN_TIMEOUT: 494 /* 495 * Don't need to cancel login timer since the timer is 496 * presumed to be the source of this event. 497 */ 498 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL); 499 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 500 break; 501 case CE_CONNECT_REJECT: 502 /* 503 * Iscsit doesn't want to hear from us again in this case. 504 * Since it rejected the connection it doesn't have a 505 * connection context to handle additional notifications. 506 * IDM needs to just clean things up on its own. 507 */ 508 (void) untimeout(ic->ic_state_timeout); 509 idm_update_state(ic, CS_S9A_REJECTED, event_ctx); 510 break; 511 case CE_CONNECT_FAIL: 512 case CE_TRANSPORT_FAIL: 513 case CE_LOGOUT_OTHER_CONN_SND: 514 /* T6 */ 515 (void) untimeout(ic->ic_state_timeout); 516 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL); 517 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 518 break; 519 case CE_TX_PROTOCOL_ERROR: 520 case CE_RX_PROTOCOL_ERROR: 521 /* Don't care */ 522 break; 523 default: 524 ASSERT(0); 525 /*NOTREACHED*/ 526 } 527 } 528 529 static void 530 idm_state_s4_in_login_fail_snd_done(idm_pdu_t *pdu, idm_status_t status) 531 { 532 idm_conn_t *ic = pdu->isp_ic; 533 534 /* 535 * This pdu callback can be invoked by the tx thread, 536 * so run the disconnect code from another thread. 537 */ 538 pdu->isp_status = status; 539 idm_conn_event(ic, CE_LOGIN_FAIL_SND_DONE, (uintptr_t)pdu); 540 } 541 542 static void 543 idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 544 { 545 idm_pdu_t *pdu; 546 547 /* 548 * Login timer should no longer be active after leaving this 549 * state. 550 */ 551 switch (event_ctx->iec_event) { 552 case CE_LOGIN_SUCCESS_RCV: 553 case CE_LOGIN_SUCCESS_SND: 554 (void) untimeout(ic->ic_state_timeout); 555 idm_login_success_actions(ic, event_ctx); 556 if (ic->ic_rdma_extensions) { 557 /* T19 */ 558 idm_update_state(ic, CS_S12_ENABLE_DM, event_ctx); 559 } else { 560 /* T5 */ 561 idm_update_state(ic, CS_S5_LOGGED_IN, event_ctx); 562 } 563 break; 564 case CE_LOGIN_TIMEOUT: 565 /* T7 */ 566 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL); 567 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 568 break; 569 case CE_LOGIN_FAIL_SND_DONE: 570 pdu = (idm_pdu_t *)event_ctx->iec_info; 571 /* restore client callback */ 572 pdu->isp_callback = ic->ic_client_callback; 573 ic->ic_client_callback = NULL; 574 idm_pdu_complete(pdu, pdu->isp_status); 575 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL); 576 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 577 break; 578 case CE_LOGIN_FAIL_SND: 579 /* 580 * Allow the logout response pdu to be sent and defer 581 * the state machine update until the completion callback. 582 * Only 1 level or callback interposition is allowed. 583 */ 584 (void) untimeout(ic->ic_state_timeout); 585 pdu = (idm_pdu_t *)event_ctx->iec_info; 586 ASSERT(ic->ic_client_callback == NULL); 587 ic->ic_client_callback = pdu->isp_callback; 588 pdu->isp_callback = 589 idm_state_s4_in_login_fail_snd_done; 590 break; 591 case CE_LOGIN_FAIL_RCV: 592 /* 593 * Need to deliver this PDU to the initiator now because after 594 * we update the state to CS_S9_INIT_ERROR the initiator will 595 * no longer be in an appropriate state. 596 */ 597 event_ctx->iec_pdu_forwarded = B_TRUE; 598 pdu = (idm_pdu_t *)event_ctx->iec_info; 599 idm_pdu_rx_forward(ic, pdu); 600 /* FALLTHROUGH */ 601 case CE_TRANSPORT_FAIL: 602 case CE_LOGOUT_OTHER_CONN_SND: 603 case CE_LOGOUT_OTHER_CONN_RCV: 604 /* T7 */ 605 (void) untimeout(ic->ic_state_timeout); 606 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL); 607 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 608 break; 609 case CE_LOGIN_SND: 610 /* 611 * Initiator connections will see initial login PDU 612 * in this state. Target connections see initial 613 * login PDU in "xpt up" state. 614 */ 615 mutex_enter(&ic->ic_state_mutex); 616 if (!(ic->ic_state_flags & CF_INITIAL_LOGIN)) { 617 idm_initial_login_actions(ic, event_ctx); 618 } 619 mutex_exit(&ic->ic_state_mutex); 620 break; 621 case CE_MISC_TX: 622 case CE_MISC_RX: 623 case CE_LOGIN_RCV: 624 case CE_TX_PROTOCOL_ERROR: 625 case CE_RX_PROTOCOL_ERROR: 626 /* Don't care */ 627 break; 628 default: 629 ASSERT(0); 630 /*NOTREACHED*/ 631 } 632 } 633 634 635 static void 636 idm_state_s5_logged_in(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 637 { 638 switch (event_ctx->iec_event) { 639 case CE_LOGOUT_THIS_CONN_RCV: 640 case CE_LOGOUT_THIS_CONN_SND: 641 case CE_LOGOUT_OTHER_CONN_RCV: 642 case CE_LOGOUT_OTHER_CONN_SND: 643 /* T9 */ 644 idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */ 645 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx); 646 break; 647 case CE_LOGOUT_SESSION_RCV: 648 case CE_LOGOUT_SESSION_SND: 649 /* T9 */ 650 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */ 651 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx); 652 break; 653 case CE_LOGOUT_SESSION_SUCCESS: 654 /* T8 */ 655 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */ 656 657 /* Close connection */ 658 if (IDM_CONN_ISTGT(ic)) { 659 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 660 } else { 661 ic->ic_transport_ops->it_ini_conn_disconnect(ic); 662 } 663 664 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 665 break; 666 case CE_ASYNC_LOGOUT_RCV: 667 case CE_ASYNC_LOGOUT_SND: 668 /* T11 */ 669 idm_update_state(ic, CS_S7_LOGOUT_REQ, event_ctx); 670 break; 671 case CE_TRANSPORT_FAIL: 672 case CE_ASYNC_DROP_CONN_RCV: 673 case CE_ASYNC_DROP_CONN_SND: 674 case CE_ASYNC_DROP_ALL_CONN_RCV: 675 case CE_ASYNC_DROP_ALL_CONN_SND: 676 /* T15 */ 677 idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */ 678 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 679 break; 680 case CE_MISC_TX: 681 case CE_MISC_RX: 682 case CE_TX_PROTOCOL_ERROR: 683 case CE_RX_PROTOCOL_ERROR: 684 /* Don't care */ 685 break; 686 default: 687 ASSERT(0); 688 } 689 } 690 691 static void 692 idm_state_s6_in_logout_success_snd_done(idm_pdu_t *pdu, idm_status_t status) 693 { 694 idm_conn_t *ic = pdu->isp_ic; 695 696 /* 697 * This pdu callback can be invoked by the tx thread, 698 * so run the disconnect code from another thread. 699 */ 700 pdu->isp_status = status; 701 idm_conn_event(ic, CE_LOGOUT_SUCCESS_SND_DONE, (uintptr_t)pdu); 702 } 703 704 static void 705 idm_state_s6_in_logout_fail_snd_done(idm_pdu_t *pdu, idm_status_t status) 706 { 707 idm_conn_t *ic = pdu->isp_ic; 708 709 /* 710 * This pdu callback can be invoked by the tx thread, 711 * so run the disconnect code from another thread. 712 */ 713 pdu->isp_status = status; 714 idm_conn_event(ic, CE_LOGOUT_FAIL_SND_DONE, (uintptr_t)pdu); 715 } 716 717 static void 718 idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 719 { 720 idm_pdu_t *pdu; 721 722 switch (event_ctx->iec_event) { 723 case CE_LOGOUT_SUCCESS_SND_DONE: 724 pdu = (idm_pdu_t *)event_ctx->iec_info; 725 726 /* Close connection (if it's not already closed) */ 727 ASSERT(IDM_CONN_ISTGT(ic)); 728 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 729 730 /* restore client callback */ 731 pdu->isp_callback = ic->ic_client_callback; 732 ic->ic_client_callback = NULL; 733 idm_pdu_complete(pdu, pdu->isp_status); 734 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 735 break; 736 case CE_LOGOUT_FAIL_SND_DONE: 737 pdu = (idm_pdu_t *)event_ctx->iec_info; 738 /* restore client callback */ 739 pdu->isp_callback = ic->ic_client_callback; 740 ic->ic_client_callback = NULL; 741 idm_pdu_complete(pdu, pdu->isp_status); 742 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 743 break; 744 case CE_LOGOUT_SUCCESS_SND: 745 case CE_LOGOUT_FAIL_SND: 746 /* 747 * Allow the logout response pdu to be sent and defer 748 * the state machine update until the completion callback. 749 * Only 1 level or callback interposition is allowed. 750 */ 751 pdu = (idm_pdu_t *)event_ctx->iec_info; 752 ASSERT(ic->ic_client_callback == NULL); 753 ic->ic_client_callback = pdu->isp_callback; 754 if (event_ctx->iec_event == CE_LOGOUT_SUCCESS_SND) { 755 pdu->isp_callback = 756 idm_state_s6_in_logout_success_snd_done; 757 } else { 758 pdu->isp_callback = 759 idm_state_s6_in_logout_fail_snd_done; 760 } 761 break; 762 case CE_LOGOUT_SUCCESS_RCV: 763 /* 764 * Need to deliver this PDU to the initiator now because after 765 * we update the state to CS_S11_COMPLETE the initiator will 766 * no longer be in an appropriate state. 767 */ 768 event_ctx->iec_pdu_forwarded = B_TRUE; 769 pdu = (idm_pdu_t *)event_ctx->iec_info; 770 idm_pdu_rx_forward(ic, pdu); 771 /* FALLTHROUGH */ 772 case CE_LOGOUT_SESSION_SUCCESS: 773 /* T13 */ 774 775 /* Close connection (if it's not already closed) */ 776 if (IDM_CONN_ISTGT(ic)) { 777 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 778 } else { 779 ic->ic_transport_ops->it_ini_conn_disconnect(ic); 780 } 781 782 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 783 break; 784 case CE_ASYNC_LOGOUT_RCV: 785 /* T14 Do nothing */ 786 break; 787 case CE_TRANSPORT_FAIL: 788 case CE_ASYNC_DROP_CONN_RCV: 789 case CE_ASYNC_DROP_CONN_SND: 790 case CE_ASYNC_DROP_ALL_CONN_RCV: 791 case CE_ASYNC_DROP_ALL_CONN_SND: 792 case CE_LOGOUT_FAIL_RCV: 793 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 794 break; 795 case CE_TX_PROTOCOL_ERROR: 796 case CE_RX_PROTOCOL_ERROR: 797 case CE_MISC_TX: 798 case CE_MISC_RX: 799 /* Don't care */ 800 break; 801 default: 802 ASSERT(0); 803 } 804 } 805 806 807 static void 808 idm_logout_req_timeout(void *arg) 809 { 810 idm_conn_t *ic = arg; 811 812 idm_conn_event(ic, CE_LOGOUT_TIMEOUT, NULL); 813 } 814 815 static void 816 idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 817 { 818 /* Must cancel logout timer before leaving this state */ 819 switch (event_ctx->iec_event) { 820 case CE_LOGOUT_THIS_CONN_RCV: 821 case CE_LOGOUT_THIS_CONN_SND: 822 case CE_LOGOUT_OTHER_CONN_RCV: 823 case CE_LOGOUT_OTHER_CONN_SND: 824 /* T10 */ 825 if (IDM_CONN_ISTGT(ic)) { 826 (void) untimeout(ic->ic_state_timeout); 827 } 828 idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */ 829 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx); 830 break; 831 case CE_LOGOUT_SESSION_RCV: 832 case CE_LOGOUT_SESSION_SND: 833 /* T10 */ 834 if (IDM_CONN_ISTGT(ic)) { 835 (void) untimeout(ic->ic_state_timeout); 836 } 837 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */ 838 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx); 839 break; 840 case CE_ASYNC_LOGOUT_RCV: 841 case CE_ASYNC_LOGOUT_SND: 842 /* T12 Do nothing */ 843 break; 844 case CE_TRANSPORT_FAIL: 845 case CE_ASYNC_DROP_CONN_RCV: 846 case CE_ASYNC_DROP_CONN_SND: 847 case CE_ASYNC_DROP_ALL_CONN_RCV: 848 case CE_ASYNC_DROP_ALL_CONN_SND: 849 /* T16 */ 850 if (IDM_CONN_ISTGT(ic)) { 851 (void) untimeout(ic->ic_state_timeout); 852 } 853 /* FALLTHROUGH */ 854 case CE_LOGOUT_TIMEOUT: 855 idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */ 856 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 857 break; 858 case CE_LOGOUT_SESSION_SUCCESS: 859 /* T18 */ 860 if (IDM_CONN_ISTGT(ic)) { 861 (void) untimeout(ic->ic_state_timeout); 862 } 863 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */ 864 865 /* Close connection (if it's not already closed) */ 866 if (IDM_CONN_ISTGT(ic)) { 867 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 868 } else { 869 ic->ic_transport_ops->it_ini_conn_disconnect(ic); 870 } 871 872 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 873 break; 874 case CE_TX_PROTOCOL_ERROR: 875 case CE_RX_PROTOCOL_ERROR: 876 case CE_MISC_TX: 877 case CE_MISC_RX: 878 /* Don't care */ 879 break; 880 default: 881 ASSERT(0); 882 } 883 } 884 885 886 static void 887 idm_cleanup_timeout(void *arg) 888 { 889 idm_conn_t *ic = arg; 890 891 idm_conn_event(ic, CE_CLEANUP_TIMEOUT, NULL); 892 } 893 894 static void 895 idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 896 { 897 idm_pdu_t *pdu; 898 899 /* 900 * Need to cancel the cleanup timeout before leaving this state 901 * if it hasn't already fired. 902 */ 903 switch (event_ctx->iec_event) { 904 case CE_LOGOUT_SUCCESS_RCV: 905 case CE_LOGOUT_SUCCESS_SND: 906 case CE_LOGOUT_SESSION_SUCCESS: 907 (void) untimeout(ic->ic_state_timeout); 908 /*FALLTHROUGH*/ 909 case CE_CLEANUP_TIMEOUT: 910 /* M1 */ 911 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 912 break; 913 case CE_LOGOUT_OTHER_CONN_RCV: 914 case CE_LOGOUT_OTHER_CONN_SND: 915 /* M2 */ 916 idm_update_state(ic, CS_S10_IN_CLEANUP, event_ctx); 917 break; 918 case CE_LOGOUT_SUCCESS_SND_DONE: 919 case CE_LOGOUT_FAIL_SND_DONE: 920 pdu = (idm_pdu_t *)event_ctx->iec_info; 921 /* restore client callback */ 922 pdu->isp_callback = ic->ic_client_callback; 923 ic->ic_client_callback = NULL; 924 idm_pdu_complete(pdu, pdu->isp_status); 925 break; 926 case CE_LOGOUT_SESSION_RCV: 927 case CE_LOGOUT_SESSION_SND: 928 case CE_TX_PROTOCOL_ERROR: 929 case CE_RX_PROTOCOL_ERROR: 930 case CE_MISC_TX: 931 case CE_MISC_RX: 932 case CE_TRANSPORT_FAIL: 933 case CE_LOGOUT_TIMEOUT: 934 /* Don't care */ 935 break; 936 default: 937 ASSERT(0); 938 } 939 } 940 941 /* ARGSUSED */ 942 static void 943 idm_state_s9_init_error(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 944 { 945 if (ic->ic_conn_type == CONN_TYPE_INI) { 946 mutex_enter(&ic->ic_state_mutex); 947 ic->ic_state_flags |= CF_ERROR; 948 ic->ic_conn_sm_status = IDM_STATUS_FAIL; 949 cv_signal(&ic->ic_state_cv); 950 mutex_exit(&ic->ic_state_mutex); 951 } 952 } 953 954 /* ARGSUSED */ 955 static void 956 idm_state_s9a_rejected(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 957 { 958 /* Ignore events */ 959 } 960 961 static void 962 idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 963 { 964 idm_pdu_t *pdu; 965 966 /* 967 * Need to cancel the cleanup timeout before leaving this state 968 * if it hasn't already fired. 969 */ 970 switch (event_ctx->iec_event) { 971 case CE_LOGOUT_FAIL_RCV: 972 case CE_LOGOUT_FAIL_SND: 973 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 974 break; 975 case CE_LOGOUT_SUCCESS_SND: 976 case CE_LOGOUT_SUCCESS_RCV: 977 case CE_LOGOUT_SESSION_SUCCESS: 978 (void) untimeout(ic->ic_state_timeout); 979 /*FALLTHROUGH*/ 980 case CE_CLEANUP_TIMEOUT: 981 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 982 break; 983 case CE_LOGOUT_SUCCESS_SND_DONE: 984 case CE_LOGOUT_FAIL_SND_DONE: 985 pdu = (idm_pdu_t *)event_ctx->iec_info; 986 /* restore client callback */ 987 pdu->isp_callback = ic->ic_client_callback; 988 ic->ic_client_callback = NULL; 989 idm_pdu_complete(pdu, pdu->isp_status); 990 break; 991 case CE_TX_PROTOCOL_ERROR: 992 case CE_RX_PROTOCOL_ERROR: 993 case CE_MISC_TX: 994 case CE_MISC_RX: 995 case CE_LOGOUT_TIMEOUT: 996 /* Don't care */ 997 break; 998 default: 999 ASSERT(0); 1000 } 1001 } 1002 1003 /* ARGSUSED */ 1004 static void 1005 idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1006 { 1007 idm_pdu_t *pdu; 1008 1009 /* 1010 * Cleanup logout success/fail completion if it's been delayed 1011 * until now. 1012 */ 1013 switch (event_ctx->iec_event) { 1014 case CE_LOGOUT_SUCCESS_SND_DONE: 1015 case CE_LOGOUT_FAIL_SND_DONE: 1016 pdu = (idm_pdu_t *)event_ctx->iec_info; 1017 /* restore client callback */ 1018 pdu->isp_callback = ic->ic_client_callback; 1019 ic->ic_client_callback = NULL; 1020 idm_pdu_complete(pdu, pdu->isp_status); 1021 break; 1022 } 1023 } 1024 1025 static void 1026 idm_state_s12_enable_dm(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1027 { 1028 switch (event_ctx->iec_event) { 1029 case CE_ENABLE_DM_SUCCESS: 1030 /* T20 */ 1031 idm_update_state(ic, CS_S5_LOGGED_IN, event_ctx); 1032 break; 1033 case CE_ENABLE_DM_FAIL: 1034 /* T21 */ 1035 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 1036 break; 1037 case CE_TRANSPORT_FAIL: 1038 /* 1039 * We expect to always hear back from the transport layer 1040 * once we have an "enable data-mover" request outstanding. 1041 * Therefore we'll ignore other events that may occur even 1042 * when they clearly indicate a problem and wait for 1043 * CE_ENABLE_DM_FAIL. On a related note this means the 1044 * transport must ensure that it eventually completes the 1045 * "enable data-mover" operation with either success or 1046 * failure -- otherwise we'll be stuck here. 1047 */ 1048 break; 1049 default: 1050 ASSERT(0); 1051 break; 1052 } 1053 } 1054 1055 static void 1056 idm_update_state(idm_conn_t *ic, idm_conn_state_t new_state, 1057 idm_conn_event_ctx_t *event_ctx) 1058 { 1059 int rc; 1060 idm_status_t idm_status; 1061 1062 /* 1063 * Validate new state 1064 */ 1065 ASSERT(new_state != CS_S0_UNDEFINED); 1066 ASSERT3U(new_state, <, CS_MAX_STATE); 1067 1068 /* 1069 * Update state in context. We protect this with a mutex 1070 * even though the state machine code is single threaded so that 1071 * other threads can check the state value atomically. 1072 */ 1073 new_state = (new_state < CS_MAX_STATE) ? 1074 new_state : CS_S0_UNDEFINED; 1075 1076 IDM_SM_LOG(CE_NOTE, "idm_update_state: conn %p, evt %s(%d), " 1077 "%s(%d) --> %s(%d)", (void *)ic, 1078 idm_ce_name[event_ctx->iec_event], event_ctx->iec_event, 1079 idm_cs_name[ic->ic_state], ic->ic_state, 1080 idm_cs_name[new_state], new_state); 1081 1082 DTRACE_PROBE2(conn__state__change, 1083 idm_conn_t *, ic, idm_conn_state_t, new_state); 1084 1085 mutex_enter(&ic->ic_state_mutex); 1086 idm_sm_audit_state_change(&ic->ic_state_audit, SAS_IDM_CONN, 1087 (int)ic->ic_state, (int)new_state); 1088 ic->ic_last_state = ic->ic_state; 1089 ic->ic_state = new_state; 1090 cv_signal(&ic->ic_state_cv); 1091 mutex_exit(&ic->ic_state_mutex); 1092 1093 switch (ic->ic_state) { 1094 case CS_S1_FREE: 1095 ASSERT(0); /* Initial state, can't return */ 1096 break; 1097 case CS_S2_XPT_WAIT: 1098 if ((rc = idm_ini_conn_finish(ic)) != 0) { 1099 idm_conn_event(ic, CE_CONNECT_FAIL, NULL); 1100 } else { 1101 idm_conn_event(ic, CE_CONNECT_SUCCESS, NULL); 1102 } 1103 break; 1104 case CS_S3_XPT_UP: 1105 /* 1106 * Finish any connection related setup including 1107 * waking up the idm_tgt_conn_accept thread. 1108 * and starting the login timer. If the function 1109 * fails then we return to "free" state. 1110 */ 1111 if ((rc = idm_tgt_conn_finish(ic)) != IDM_STATUS_SUCCESS) { 1112 switch (rc) { 1113 case IDM_STATUS_REJECT: 1114 idm_conn_event(ic, CE_CONNECT_REJECT, NULL); 1115 break; 1116 default: 1117 idm_conn_event(ic, CE_CONNECT_FAIL, NULL); 1118 break; 1119 } 1120 } 1121 1122 /* 1123 * First login received will cause a transition to 1124 * CS_S4_IN_LOGIN. Start login timer. 1125 */ 1126 ic->ic_state_timeout = timeout(idm_login_timeout, ic, 1127 drv_usectohz(IDM_LOGIN_SECONDS*1000000)); 1128 break; 1129 case CS_S4_IN_LOGIN: 1130 if (ic->ic_conn_type == CONN_TYPE_INI) { 1131 (void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL); 1132 mutex_enter(&ic->ic_state_mutex); 1133 ic->ic_state_flags |= CF_LOGIN_READY; 1134 cv_signal(&ic->ic_state_cv); 1135 mutex_exit(&ic->ic_state_mutex); 1136 } 1137 break; 1138 case CS_S5_LOGGED_IN: 1139 ASSERT(!ic->ic_ffp); 1140 /* 1141 * IDM can go to FFP before the initiator but it 1142 * needs to go to FFP after the target (IDM target should 1143 * go to FFP after notify_ack). 1144 */ 1145 idm_status = idm_ffp_enable(ic); 1146 if (idm_status != IDM_STATUS_SUCCESS) { 1147 idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL); 1148 } 1149 1150 if (ic->ic_reinstate_conn) { 1151 /* Connection reinstatement is complete */ 1152 idm_conn_event_locked(ic->ic_reinstate_conn, 1153 CE_CONN_REINSTATE_SUCCESS, NULL, CT_NONE); 1154 } 1155 break; 1156 case CS_S6_IN_LOGOUT: 1157 break; 1158 case CS_S7_LOGOUT_REQ: 1159 /* Start logout timer for target connections */ 1160 if (IDM_CONN_ISTGT(ic)) { 1161 ic->ic_state_timeout = timeout(idm_logout_req_timeout, 1162 ic, drv_usectohz(IDM_LOGOUT_SECONDS*1000000)); 1163 } 1164 break; 1165 case CS_S8_CLEANUP: 1166 /* Close connection (if it's not already closed) */ 1167 if (IDM_CONN_ISTGT(ic)) { 1168 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 1169 } else { 1170 ic->ic_transport_ops->it_ini_conn_disconnect(ic); 1171 } 1172 1173 /* Stop executing active tasks */ 1174 idm_task_abort(ic, NULL, AT_INTERNAL_SUSPEND); 1175 1176 /* Start logout timer */ 1177 ic->ic_state_timeout = timeout(idm_cleanup_timeout, ic, 1178 drv_usectohz(IDM_CLEANUP_SECONDS*1000000)); 1179 break; 1180 case CS_S10_IN_CLEANUP: 1181 break; 1182 case CS_S9A_REJECTED: 1183 /* 1184 * We never finished establishing the connection so no 1185 * disconnect. No client notificatiosn because the client 1186 * rejected the connection. 1187 */ 1188 idm_refcnt_async_wait_ref(&ic->ic_refcnt, 1189 &idm_conn_reject_unref); 1190 break; 1191 case CS_S9_INIT_ERROR: 1192 if (IDM_CONN_ISTGT(ic)) { 1193 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 1194 } else { 1195 mutex_enter(&ic->ic_state_mutex); 1196 ic->ic_state_flags |= CF_ERROR; 1197 ic->ic_conn_sm_status = IDM_STATUS_FAIL; 1198 cv_signal(&ic->ic_state_cv); 1199 mutex_exit(&ic->ic_state_mutex); 1200 if (ic->ic_last_state != CS_S1_FREE && 1201 ic->ic_last_state != CS_S2_XPT_WAIT) { 1202 ic->ic_transport_ops->it_ini_conn_disconnect( 1203 ic); 1204 } else { 1205 (void) idm_notify_client(ic, CN_CONNECT_FAIL, 1206 NULL); 1207 } 1208 } 1209 /*FALLTHROUGH*/ 1210 case CS_S11_COMPLETE: 1211 /* 1212 * No more traffic on this connection. If this is an 1213 * initiator connection and we weren't connected yet 1214 * then don't send the "connect lost" event. 1215 * It's useful to the initiator to know whether we were 1216 * logging in at the time so send that information in the 1217 * data field. 1218 */ 1219 if (IDM_CONN_ISTGT(ic) || 1220 ((ic->ic_last_state != CS_S1_FREE) && 1221 (ic->ic_last_state != CS_S2_XPT_WAIT))) { 1222 (void) idm_notify_client(ic, CN_CONNECT_LOST, 1223 (uintptr_t)(ic->ic_last_state == CS_S4_IN_LOGIN)); 1224 } 1225 1226 /* Abort all tasks */ 1227 idm_task_abort(ic, NULL, AT_INTERNAL_ABORT); 1228 1229 /* 1230 * Handle terminal state actions on the global taskq so 1231 * we can clean up all the connection resources from 1232 * a separate thread context. 1233 */ 1234 idm_refcnt_async_wait_ref(&ic->ic_refcnt, &idm_conn_unref); 1235 break; 1236 case CS_S12_ENABLE_DM: 1237 1238 /* 1239 * The Enable DM state indicates the initiator to initiate 1240 * the hello sequence and the target to get ready to accept 1241 * the iSER Hello Message. 1242 */ 1243 idm_status = (IDM_CONN_ISINI(ic)) ? 1244 ic->ic_transport_ops->it_ini_enable_datamover(ic) : 1245 ic->ic_transport_ops->it_tgt_enable_datamover(ic); 1246 1247 if (idm_status == IDM_STATUS_SUCCESS) { 1248 idm_conn_event(ic, CE_ENABLE_DM_SUCCESS, NULL); 1249 } else { 1250 idm_conn_event(ic, CE_ENABLE_DM_FAIL, NULL); 1251 } 1252 1253 break; 1254 } 1255 } 1256 1257 1258 static void 1259 idm_conn_unref(void *ic_void) 1260 { 1261 idm_conn_t *ic = ic_void; 1262 1263 /* 1264 * Client should not be notified that the connection is destroyed 1265 * until all references on the idm connection have been removed. 1266 * Otherwise references on the associated client context would need 1267 * to be tracked separately which seems like a waste (at least when 1268 * there is a one for one correspondence with references on the 1269 * IDM connection). 1270 */ 1271 if (IDM_CONN_ISTGT(ic)) { 1272 (void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL); 1273 idm_svc_conn_destroy(ic); 1274 } else { 1275 /* Initiator may destroy connection during this call */ 1276 (void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL); 1277 } 1278 } 1279 1280 static void 1281 idm_conn_reject_unref(void *ic_void) 1282 { 1283 idm_conn_t *ic = ic_void; 1284 1285 ASSERT(IDM_CONN_ISTGT(ic)); 1286 1287 /* Don't notify the client since it rejected the connection */ 1288 idm_svc_conn_destroy(ic); 1289 } 1290 1291 1292 1293 static idm_pdu_event_action_t 1294 idm_conn_sm_validate_pdu(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx, 1295 idm_pdu_t *pdu) 1296 { 1297 char *reason_string; 1298 idm_pdu_event_action_t action; 1299 1300 ASSERT((event_ctx->iec_pdu_event_type == CT_RX_PDU) || 1301 (event_ctx->iec_pdu_event_type == CT_TX_PDU)); 1302 1303 /* 1304 * Let's check the simple stuff first. Make sure if this is a 1305 * target connection that the PDU is appropriate for a target 1306 * and if this is an initiator connection that the PDU is 1307 * appropriate for an initiator. This code is not in the data 1308 * path so organization is more important than performance. 1309 */ 1310 switch (IDM_PDU_OPCODE(pdu)) { 1311 case ISCSI_OP_NOOP_OUT: 1312 case ISCSI_OP_SCSI_CMD: 1313 case ISCSI_OP_SCSI_TASK_MGT_MSG: 1314 case ISCSI_OP_LOGIN_CMD: 1315 case ISCSI_OP_TEXT_CMD: 1316 case ISCSI_OP_SCSI_DATA: 1317 case ISCSI_OP_LOGOUT_CMD: 1318 case ISCSI_OP_SNACK_CMD: 1319 /* 1320 * Only the initiator should send these PDU's and 1321 * only the target should receive them. 1322 */ 1323 if (IDM_CONN_ISINI(ic) && 1324 (event_ctx->iec_pdu_event_type == CT_RX_PDU)) { 1325 reason_string = "Invalid RX PDU for initiator"; 1326 action = CA_RX_PROTOCOL_ERROR; 1327 goto validate_pdu_done; 1328 } 1329 1330 if (IDM_CONN_ISTGT(ic) && 1331 (event_ctx->iec_pdu_event_type == CT_TX_PDU)) { 1332 reason_string = "Invalid TX PDU for target"; 1333 action = CA_TX_PROTOCOL_ERROR; 1334 goto validate_pdu_done; 1335 } 1336 break; 1337 case ISCSI_OP_NOOP_IN: 1338 case ISCSI_OP_SCSI_RSP: 1339 case ISCSI_OP_SCSI_TASK_MGT_RSP: 1340 case ISCSI_OP_LOGIN_RSP: 1341 case ISCSI_OP_TEXT_RSP: 1342 case ISCSI_OP_SCSI_DATA_RSP: 1343 case ISCSI_OP_LOGOUT_RSP: 1344 case ISCSI_OP_RTT_RSP: 1345 case ISCSI_OP_ASYNC_EVENT: 1346 case ISCSI_OP_REJECT_MSG: 1347 /* 1348 * Only the target should send these PDU's and 1349 * only the initiator should receive them. 1350 */ 1351 if (IDM_CONN_ISTGT(ic) && 1352 (event_ctx->iec_pdu_event_type == CT_RX_PDU)) { 1353 reason_string = "Invalid RX PDU for target"; 1354 action = CA_RX_PROTOCOL_ERROR; 1355 goto validate_pdu_done; 1356 } 1357 1358 if (IDM_CONN_ISINI(ic) && 1359 (event_ctx->iec_pdu_event_type == CT_TX_PDU)) { 1360 reason_string = "Invalid TX PDU for initiator"; 1361 action = CA_TX_PROTOCOL_ERROR; 1362 goto validate_pdu_done; 1363 } 1364 break; 1365 default: 1366 reason_string = "Unknown PDU Type"; 1367 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1368 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1369 goto validate_pdu_done; 1370 } 1371 1372 /* 1373 * Now validate the opcodes against the current state. 1374 */ 1375 reason_string = "PDU not allowed in current state"; 1376 switch (IDM_PDU_OPCODE(pdu)) { 1377 case ISCSI_OP_NOOP_OUT: 1378 case ISCSI_OP_NOOP_IN: 1379 /* 1380 * Obviously S1-S3 are not allowed since login hasn't started. 1381 * S8 is probably out as well since the connection has been 1382 * dropped. 1383 */ 1384 switch (ic->ic_state) { 1385 case CS_S4_IN_LOGIN: 1386 case CS_S5_LOGGED_IN: 1387 case CS_S6_IN_LOGOUT: 1388 case CS_S7_LOGOUT_REQ: 1389 action = CA_FORWARD; 1390 goto validate_pdu_done; 1391 case CS_S8_CLEANUP: 1392 case CS_S10_IN_CLEANUP: 1393 action = CA_DROP; 1394 break; 1395 default: 1396 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1397 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1398 goto validate_pdu_done; 1399 } 1400 /*NOTREACHED*/ 1401 case ISCSI_OP_SCSI_CMD: 1402 case ISCSI_OP_SCSI_RSP: 1403 case ISCSI_OP_SCSI_TASK_MGT_MSG: 1404 case ISCSI_OP_SCSI_TASK_MGT_RSP: 1405 case ISCSI_OP_SCSI_DATA: 1406 case ISCSI_OP_SCSI_DATA_RSP: 1407 case ISCSI_OP_RTT_RSP: 1408 case ISCSI_OP_SNACK_CMD: 1409 case ISCSI_OP_TEXT_CMD: 1410 case ISCSI_OP_TEXT_RSP: 1411 switch (ic->ic_state) { 1412 case CS_S5_LOGGED_IN: 1413 case CS_S6_IN_LOGOUT: 1414 case CS_S7_LOGOUT_REQ: 1415 action = CA_FORWARD; 1416 goto validate_pdu_done; 1417 case CS_S8_CLEANUP: 1418 case CS_S10_IN_CLEANUP: 1419 action = CA_DROP; 1420 break; 1421 default: 1422 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1423 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1424 goto validate_pdu_done; 1425 } 1426 /*NOTREACHED*/ 1427 case ISCSI_OP_LOGOUT_CMD: 1428 case ISCSI_OP_LOGOUT_RSP: 1429 case ISCSI_OP_REJECT_MSG: 1430 case ISCSI_OP_ASYNC_EVENT: 1431 switch (ic->ic_state) { 1432 case CS_S5_LOGGED_IN: 1433 case CS_S6_IN_LOGOUT: 1434 case CS_S7_LOGOUT_REQ: 1435 action = CA_FORWARD; 1436 goto validate_pdu_done; 1437 case CS_S8_CLEANUP: 1438 case CS_S10_IN_CLEANUP: 1439 action = CA_DROP; 1440 break; 1441 default: 1442 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1443 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1444 goto validate_pdu_done; 1445 } 1446 /*NOTREACHED*/ 1447 case ISCSI_OP_LOGIN_CMD: 1448 case ISCSI_OP_LOGIN_RSP: 1449 switch (ic->ic_state) { 1450 case CS_S3_XPT_UP: 1451 case CS_S4_IN_LOGIN: 1452 action = CA_FORWARD; 1453 goto validate_pdu_done; 1454 default: 1455 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1456 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1457 goto validate_pdu_done; 1458 } 1459 /*NOTREACHED*/ 1460 default: 1461 /* This should never happen -- we already checked above */ 1462 ASSERT(0); 1463 /*NOTREACHED*/ 1464 } 1465 1466 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1467 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1468 1469 validate_pdu_done: 1470 if (action != CA_FORWARD) { 1471 DTRACE_PROBE2(idm__int__protocol__error, 1472 idm_conn_event_ctx_t *, event_ctx, 1473 char *, reason_string); 1474 } 1475 1476 return (action); 1477 } 1478 1479 /* ARGSUSED */ 1480 void 1481 idm_pdu_tx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu) 1482 { 1483 /* 1484 * Return the PDU to the caller indicating it was a protocol error. 1485 * Caller can take appropriate action. 1486 */ 1487 idm_pdu_complete(pdu, IDM_STATUS_PROTOCOL_ERROR); 1488 } 1489 1490 void 1491 idm_pdu_rx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu) 1492 { 1493 /* 1494 * Forward PDU to caller indicating it is a protocol error. 1495 * Caller should take appropriate action. 1496 */ 1497 (*ic->ic_conn_ops.icb_rx_error)(ic, pdu, IDM_STATUS_PROTOCOL_ERROR); 1498 } 1499 1500 idm_status_t 1501 idm_notify_client(idm_conn_t *ic, idm_client_notify_t cn, uintptr_t data) 1502 { 1503 /* 1504 * We may want to make this more complicated at some point but 1505 * for now lets just call the client's notify function and return 1506 * the status. 1507 */ 1508 ASSERT(!mutex_owned(&ic->ic_state_mutex)); 1509 cn = (cn > CN_MAX) ? CN_MAX : cn; 1510 IDM_SM_LOG(CE_NOTE, "idm_notify_client: ic=%p %s(%d)\n", 1511 (void *)ic, idm_cn_strings[cn], cn); 1512 return ((*ic->ic_conn_ops.icb_client_notify)(ic, cn, data)); 1513 } 1514 1515 static idm_status_t 1516 idm_ffp_enable(idm_conn_t *ic) 1517 { 1518 idm_status_t rc; 1519 1520 /* 1521 * On the initiator side the client will see this notification 1522 * before the actual login succes PDU. This shouldn't be a big 1523 * deal since the initiator drives the connection. It can simply 1524 * wait for the login response then start sending SCSI commands. 1525 * Kind ugly though compared with the way things work on target 1526 * connections. 1527 */ 1528 mutex_enter(&ic->ic_state_mutex); 1529 ic->ic_ffp = B_TRUE; 1530 mutex_exit(&ic->ic_state_mutex); 1531 1532 rc = idm_notify_client(ic, CN_FFP_ENABLED, NULL); 1533 if (rc != IDM_STATUS_SUCCESS) { 1534 mutex_enter(&ic->ic_state_mutex); 1535 ic->ic_ffp = B_FALSE; 1536 mutex_exit(&ic->ic_state_mutex); 1537 } 1538 return (rc); 1539 } 1540 1541 static void 1542 idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type) 1543 { 1544 mutex_enter(&ic->ic_state_mutex); 1545 ic->ic_ffp = B_FALSE; 1546 mutex_exit(&ic->ic_state_mutex); 1547 1548 /* Client can't "fail" CN_FFP_DISABLED */ 1549 (void) idm_notify_client(ic, CN_FFP_DISABLED, 1550 (uintptr_t)disable_type); 1551 } 1552 1553 static void 1554 idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1555 { 1556 ASSERT((event_ctx->iec_event == CE_LOGIN_RCV) || 1557 (event_ctx->iec_event == CE_LOGIN_SND)); 1558 1559 /* 1560 * Currently it's not clear what we would do here -- since 1561 * we went to the trouble of coding an "initial login" hook 1562 * we'll leave it in for now. Remove before integration if 1563 * it's not used for anything. 1564 */ 1565 ic->ic_state_flags |= CF_INITIAL_LOGIN; 1566 } 1567 1568 static void 1569 idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1570 { 1571 idm_pdu_t *pdu = (idm_pdu_t *)event_ctx->iec_info; 1572 iscsi_login_hdr_t *login_req = 1573 (iscsi_login_hdr_t *)pdu->isp_hdr; 1574 1575 ASSERT((event_ctx->iec_event == CE_LOGIN_SUCCESS_RCV) || 1576 (event_ctx->iec_event == CE_LOGIN_SUCCESS_SND)); 1577 1578 /* 1579 * Save off CID 1580 */ 1581 mutex_enter(&ic->ic_state_mutex); 1582 ic->ic_login_cid = ntohs(login_req->cid); 1583 ic->ic_login_info_valid = B_TRUE; 1584 1585 mutex_exit(&ic->ic_state_mutex); 1586 } 1587