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