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