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