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