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, (uintptr_t)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, (uintptr_t)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, (uintptr_t)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, (uintptr_t)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, (uintptr_t)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, (uintptr_t)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, (uintptr_t)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, (uintptr_t)NULL); 1170 } else { 1171 idm_conn_event(ic, CE_CONNECT_SUCCESS, (uintptr_t)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, 1185 (uintptr_t)NULL); 1186 break; 1187 default: 1188 idm_conn_event(ic, CE_CONNECT_FAIL, 1189 (uintptr_t)NULL); 1190 break; 1191 } 1192 } 1193 1194 /* 1195 * First login received will cause a transition to 1196 * CS_S4_IN_LOGIN. Start login timer. 1197 */ 1198 IDM_SM_TIMER_CHECK(ic); 1199 ic->ic_state_timeout = timeout(idm_login_timeout, ic, 1200 drv_usectohz(IDM_LOGIN_SECONDS * 1000000)); 1201 break; 1202 case CS_S4_IN_LOGIN: 1203 if (ic->ic_conn_type == CONN_TYPE_INI) { 1204 (void) idm_notify_client(ic, CN_READY_FOR_LOGIN, 1205 (uintptr_t)NULL); 1206 mutex_enter(&ic->ic_state_mutex); 1207 ic->ic_state_flags |= CF_LOGIN_READY; 1208 cv_signal(&ic->ic_state_cv); 1209 mutex_exit(&ic->ic_state_mutex); 1210 } 1211 break; 1212 case CS_S5_LOGGED_IN: 1213 ASSERT(!ic->ic_ffp); 1214 /* 1215 * IDM can go to FFP before the initiator but it 1216 * needs to go to FFP after the target (IDM target should 1217 * go to FFP after notify_ack). 1218 */ 1219 idm_status = idm_ffp_enable(ic); 1220 if (idm_status != IDM_STATUS_SUCCESS) { 1221 idm_conn_event(ic, CE_TRANSPORT_FAIL, (uintptr_t)NULL); 1222 } 1223 1224 if (ic->ic_reinstate_conn) { 1225 /* Connection reinstatement is complete */ 1226 idm_conn_event(ic->ic_reinstate_conn, 1227 CE_CONN_REINSTATE_SUCCESS, (uintptr_t)NULL); 1228 } 1229 break; 1230 case CS_S6_IN_LOGOUT: 1231 break; 1232 case CS_S7_LOGOUT_REQ: 1233 /* Start logout timer for target connections */ 1234 if (IDM_CONN_ISTGT(ic)) { 1235 IDM_SM_TIMER_CHECK(ic); 1236 ic->ic_state_timeout = timeout(idm_logout_req_timeout, 1237 ic, drv_usectohz(IDM_LOGOUT_SECONDS*1000000)); 1238 } 1239 break; 1240 case CS_S8_CLEANUP: 1241 /* Close connection (if it's not already closed) */ 1242 if (IDM_CONN_ISTGT(ic)) { 1243 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 1244 } else { 1245 ic->ic_transport_ops->it_ini_conn_disconnect(ic); 1246 } 1247 1248 /* Stop executing active tasks */ 1249 idm_task_abort(ic, NULL, AT_INTERNAL_SUSPEND); 1250 1251 /* Start logout timer */ 1252 IDM_SM_TIMER_CHECK(ic); 1253 ic->ic_state_timeout = timeout(idm_cleanup_timeout, ic, 1254 drv_usectohz(IDM_CLEANUP_SECONDS*1000000)); 1255 break; 1256 case CS_S10_IN_CLEANUP: 1257 break; 1258 case CS_S9A_REJECTED: 1259 /* 1260 * We never finished establishing the connection so no 1261 * disconnect. No client notifications because the client 1262 * rejected the connection. 1263 */ 1264 idm_refcnt_async_wait_ref(&ic->ic_refcnt, 1265 &idm_conn_reject_unref); 1266 break; 1267 case CS_S9B_WAIT_SND_DONE: 1268 break; 1269 case CS_S9_INIT_ERROR: 1270 if (IDM_CONN_ISTGT(ic)) { 1271 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 1272 } else { 1273 mutex_enter(&ic->ic_state_mutex); 1274 ic->ic_state_flags |= CF_ERROR; 1275 ic->ic_conn_sm_status = IDM_STATUS_FAIL; 1276 cv_signal(&ic->ic_state_cv); 1277 mutex_exit(&ic->ic_state_mutex); 1278 if (ic->ic_last_state != CS_S1_FREE && 1279 ic->ic_last_state != CS_S2_XPT_WAIT) { 1280 ic->ic_transport_ops->it_ini_conn_disconnect( 1281 ic); 1282 } else { 1283 (void) idm_notify_client(ic, CN_CONNECT_FAIL, 1284 (uintptr_t)NULL); 1285 } 1286 } 1287 /*FALLTHROUGH*/ 1288 case CS_S11_COMPLETE: 1289 /* 1290 * No more traffic on this connection. If this is an 1291 * initiator connection and we weren't connected yet 1292 * then don't send the "connect lost" event. 1293 * It's useful to the initiator to know whether we were 1294 * logging in at the time so send that information in the 1295 * data field. 1296 */ 1297 if (IDM_CONN_ISTGT(ic) || 1298 ((ic->ic_last_state != CS_S1_FREE) && 1299 (ic->ic_last_state != CS_S2_XPT_WAIT))) { 1300 (void) idm_notify_client(ic, CN_CONNECT_LOST, 1301 (uintptr_t)(ic->ic_last_state == CS_S4_IN_LOGIN)); 1302 } 1303 1304 /* Abort all tasks */ 1305 idm_task_abort(ic, NULL, AT_INTERNAL_ABORT); 1306 1307 /* 1308 * Handle terminal state actions on the global taskq so 1309 * we can clean up all the connection resources from 1310 * a separate thread context. 1311 */ 1312 idm_refcnt_async_wait_ref(&ic->ic_refcnt, &idm_conn_unref); 1313 break; 1314 case CS_S12_ENABLE_DM: 1315 1316 /* 1317 * The Enable DM state indicates the initiator to initiate 1318 * the hello sequence and the target to get ready to accept 1319 * the iSER Hello Message. 1320 */ 1321 idm_status = (IDM_CONN_ISINI(ic)) ? 1322 ic->ic_transport_ops->it_ini_enable_datamover(ic) : 1323 ic->ic_transport_ops->it_tgt_enable_datamover(ic); 1324 1325 if (idm_status == IDM_STATUS_SUCCESS) { 1326 idm_conn_event(ic, CE_ENABLE_DM_SUCCESS, 1327 (uintptr_t)NULL); 1328 } else { 1329 idm_conn_event(ic, CE_ENABLE_DM_FAIL, (uintptr_t)NULL); 1330 } 1331 1332 break; 1333 1334 default: 1335 ASSERT(0); 1336 break; 1337 1338 } 1339 } 1340 1341 1342 static void 1343 idm_conn_unref(void *ic_void) 1344 { 1345 idm_conn_t *ic = ic_void; 1346 1347 /* 1348 * Client should not be notified that the connection is destroyed 1349 * until all references on the idm connection have been removed. 1350 * Otherwise references on the associated client context would need 1351 * to be tracked separately which seems like a waste (at least when 1352 * there is a one for one correspondence with references on the 1353 * IDM connection). 1354 */ 1355 if (IDM_CONN_ISTGT(ic)) { 1356 (void) idm_notify_client(ic, CN_CONNECT_DESTROY, 1357 (uintptr_t)NULL); 1358 idm_svc_conn_destroy(ic); 1359 } else { 1360 /* Initiator may destroy connection during this call */ 1361 (void) idm_notify_client(ic, CN_CONNECT_DESTROY, 1362 (uintptr_t)NULL); 1363 } 1364 } 1365 1366 static void 1367 idm_conn_reject_unref(void *ic_void) 1368 { 1369 idm_conn_t *ic = ic_void; 1370 1371 ASSERT(IDM_CONN_ISTGT(ic)); 1372 1373 /* Don't notify the client since it rejected the connection */ 1374 idm_svc_conn_destroy(ic); 1375 } 1376 1377 1378 1379 static idm_pdu_event_action_t 1380 idm_conn_sm_validate_pdu(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx, 1381 idm_pdu_t *pdu) 1382 { 1383 char *reason_string; 1384 idm_pdu_event_action_t action; 1385 1386 ASSERT((event_ctx->iec_pdu_event_type == CT_RX_PDU) || 1387 (event_ctx->iec_pdu_event_type == CT_TX_PDU)); 1388 1389 /* 1390 * Let's check the simple stuff first. Make sure if this is a 1391 * target connection that the PDU is appropriate for a target 1392 * and if this is an initiator connection that the PDU is 1393 * appropriate for an initiator. This code is not in the data 1394 * path so organization is more important than performance. 1395 */ 1396 switch (IDM_PDU_OPCODE(pdu)) { 1397 case ISCSI_OP_NOOP_OUT: 1398 case ISCSI_OP_SCSI_CMD: 1399 case ISCSI_OP_SCSI_TASK_MGT_MSG: 1400 case ISCSI_OP_LOGIN_CMD: 1401 case ISCSI_OP_TEXT_CMD: 1402 case ISCSI_OP_SCSI_DATA: 1403 case ISCSI_OP_LOGOUT_CMD: 1404 case ISCSI_OP_SNACK_CMD: 1405 /* 1406 * Only the initiator should send these PDU's and 1407 * only the target should receive them. 1408 */ 1409 if (IDM_CONN_ISINI(ic) && 1410 (event_ctx->iec_pdu_event_type == CT_RX_PDU)) { 1411 reason_string = "Invalid RX PDU for initiator"; 1412 action = CA_RX_PROTOCOL_ERROR; 1413 goto validate_pdu_done; 1414 } 1415 1416 if (IDM_CONN_ISTGT(ic) && 1417 (event_ctx->iec_pdu_event_type == CT_TX_PDU)) { 1418 reason_string = "Invalid TX PDU for target"; 1419 action = CA_TX_PROTOCOL_ERROR; 1420 goto validate_pdu_done; 1421 } 1422 break; 1423 case ISCSI_OP_NOOP_IN: 1424 case ISCSI_OP_SCSI_RSP: 1425 case ISCSI_OP_SCSI_TASK_MGT_RSP: 1426 case ISCSI_OP_LOGIN_RSP: 1427 case ISCSI_OP_TEXT_RSP: 1428 case ISCSI_OP_SCSI_DATA_RSP: 1429 case ISCSI_OP_LOGOUT_RSP: 1430 case ISCSI_OP_RTT_RSP: 1431 case ISCSI_OP_ASYNC_EVENT: 1432 case ISCSI_OP_REJECT_MSG: 1433 /* 1434 * Only the target should send these PDU's and 1435 * only the initiator should receive them. 1436 */ 1437 if (IDM_CONN_ISTGT(ic) && 1438 (event_ctx->iec_pdu_event_type == CT_RX_PDU)) { 1439 reason_string = "Invalid RX PDU for target"; 1440 action = CA_RX_PROTOCOL_ERROR; 1441 goto validate_pdu_done; 1442 } 1443 1444 if (IDM_CONN_ISINI(ic) && 1445 (event_ctx->iec_pdu_event_type == CT_TX_PDU)) { 1446 reason_string = "Invalid TX PDU for initiator"; 1447 action = CA_TX_PROTOCOL_ERROR; 1448 goto validate_pdu_done; 1449 } 1450 break; 1451 default: 1452 reason_string = "Unknown PDU Type"; 1453 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1454 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1455 goto validate_pdu_done; 1456 } 1457 1458 /* 1459 * Now validate the opcodes against the current state. 1460 */ 1461 reason_string = "PDU not allowed in current state"; 1462 switch (IDM_PDU_OPCODE(pdu)) { 1463 case ISCSI_OP_NOOP_OUT: 1464 case ISCSI_OP_NOOP_IN: 1465 /* 1466 * Obviously S1-S3 are not allowed since login hasn't started. 1467 * S8 is probably out as well since the connection has been 1468 * dropped. 1469 */ 1470 switch (ic->ic_state) { 1471 case CS_S4_IN_LOGIN: 1472 case CS_S5_LOGGED_IN: 1473 case CS_S6_IN_LOGOUT: 1474 case CS_S7_LOGOUT_REQ: 1475 action = CA_FORWARD; 1476 goto validate_pdu_done; 1477 case CS_S8_CLEANUP: 1478 case CS_S10_IN_CLEANUP: 1479 action = CA_DROP; 1480 break; 1481 default: 1482 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1483 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1484 goto validate_pdu_done; 1485 } 1486 /*NOTREACHED*/ 1487 case ISCSI_OP_SCSI_CMD: 1488 case ISCSI_OP_SCSI_RSP: 1489 case ISCSI_OP_SCSI_TASK_MGT_MSG: 1490 case ISCSI_OP_SCSI_TASK_MGT_RSP: 1491 case ISCSI_OP_SCSI_DATA: 1492 case ISCSI_OP_SCSI_DATA_RSP: 1493 case ISCSI_OP_RTT_RSP: 1494 case ISCSI_OP_SNACK_CMD: 1495 case ISCSI_OP_TEXT_CMD: 1496 case ISCSI_OP_TEXT_RSP: 1497 switch (ic->ic_state) { 1498 case CS_S5_LOGGED_IN: 1499 case CS_S6_IN_LOGOUT: 1500 case CS_S7_LOGOUT_REQ: 1501 action = CA_FORWARD; 1502 goto validate_pdu_done; 1503 case CS_S8_CLEANUP: 1504 case CS_S10_IN_CLEANUP: 1505 action = CA_DROP; 1506 break; 1507 default: 1508 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1509 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1510 goto validate_pdu_done; 1511 } 1512 /*NOTREACHED*/ 1513 case ISCSI_OP_LOGOUT_CMD: 1514 case ISCSI_OP_LOGOUT_RSP: 1515 case ISCSI_OP_REJECT_MSG: 1516 case ISCSI_OP_ASYNC_EVENT: 1517 switch (ic->ic_state) { 1518 case CS_S5_LOGGED_IN: 1519 case CS_S6_IN_LOGOUT: 1520 case CS_S7_LOGOUT_REQ: 1521 action = CA_FORWARD; 1522 goto validate_pdu_done; 1523 case CS_S8_CLEANUP: 1524 case CS_S10_IN_CLEANUP: 1525 action = CA_DROP; 1526 break; 1527 default: 1528 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1529 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1530 goto validate_pdu_done; 1531 } 1532 /*NOTREACHED*/ 1533 case ISCSI_OP_LOGIN_CMD: 1534 case ISCSI_OP_LOGIN_RSP: 1535 switch (ic->ic_state) { 1536 case CS_S3_XPT_UP: 1537 case CS_S4_IN_LOGIN: 1538 action = CA_FORWARD; 1539 goto validate_pdu_done; 1540 default: 1541 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1542 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1543 goto validate_pdu_done; 1544 } 1545 /*NOTREACHED*/ 1546 default: 1547 /* This should never happen -- we already checked above */ 1548 ASSERT(0); 1549 /*NOTREACHED*/ 1550 } 1551 1552 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1553 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1554 1555 validate_pdu_done: 1556 if (action != CA_FORWARD) { 1557 DTRACE_PROBE2(idm__int__protocol__error, 1558 idm_conn_event_ctx_t *, event_ctx, 1559 char *, reason_string); 1560 } 1561 1562 return (action); 1563 } 1564 1565 /* ARGSUSED */ 1566 void 1567 idm_pdu_tx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu) 1568 { 1569 /* 1570 * Return the PDU to the caller indicating it was a protocol error. 1571 * Caller can take appropriate action. 1572 */ 1573 idm_pdu_complete(pdu, IDM_STATUS_PROTOCOL_ERROR); 1574 } 1575 1576 void 1577 idm_pdu_rx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu) 1578 { 1579 /* 1580 * Forward PDU to caller indicating it is a protocol error. 1581 * Caller should take appropriate action. 1582 */ 1583 (*ic->ic_conn_ops.icb_rx_error)(ic, pdu, IDM_STATUS_PROTOCOL_ERROR); 1584 } 1585 1586 idm_status_t 1587 idm_notify_client(idm_conn_t *ic, idm_client_notify_t cn, uintptr_t data) 1588 { 1589 /* 1590 * We may want to make this more complicated at some point but 1591 * for now lets just call the client's notify function and return 1592 * the status. 1593 */ 1594 ASSERT(!mutex_owned(&ic->ic_state_mutex)); 1595 cn = (cn > CN_MAX) ? CN_MAX : cn; 1596 IDM_SM_LOG(CE_NOTE, "idm_notify_client: ic=%p %s(%d)\n", 1597 (void *)ic, idm_cn_strings[cn], cn); 1598 return ((*ic->ic_conn_ops.icb_client_notify)(ic, cn, data)); 1599 } 1600 1601 static idm_status_t 1602 idm_ffp_enable(idm_conn_t *ic) 1603 { 1604 idm_status_t rc; 1605 1606 /* 1607 * On the initiator side the client will see this notification 1608 * before the actual login succes PDU. This shouldn't be a big 1609 * deal since the initiator drives the connection. It can simply 1610 * wait for the login response then start sending SCSI commands. 1611 * Kind ugly though compared with the way things work on target 1612 * connections. 1613 */ 1614 mutex_enter(&ic->ic_state_mutex); 1615 ic->ic_ffp = B_TRUE; 1616 mutex_exit(&ic->ic_state_mutex); 1617 1618 rc = idm_notify_client(ic, CN_FFP_ENABLED, (uintptr_t)NULL); 1619 if (rc != IDM_STATUS_SUCCESS) { 1620 mutex_enter(&ic->ic_state_mutex); 1621 ic->ic_ffp = B_FALSE; 1622 mutex_exit(&ic->ic_state_mutex); 1623 } 1624 return (rc); 1625 } 1626 1627 static void 1628 idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type) 1629 { 1630 mutex_enter(&ic->ic_state_mutex); 1631 ic->ic_ffp = B_FALSE; 1632 mutex_exit(&ic->ic_state_mutex); 1633 1634 /* Client can't "fail" CN_FFP_DISABLED */ 1635 (void) idm_notify_client(ic, CN_FFP_DISABLED, 1636 (uintptr_t)disable_type); 1637 } 1638 1639 static void 1640 idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1641 { 1642 ASSERT((event_ctx->iec_event == CE_LOGIN_RCV) || 1643 (event_ctx->iec_event == CE_LOGIN_SND)); 1644 1645 /* 1646 * Currently it's not clear what we would do here -- since 1647 * we went to the trouble of coding an "initial login" hook 1648 * we'll leave it in for now. Remove before integration if 1649 * it's not used for anything. 1650 */ 1651 ic->ic_state_flags |= CF_INITIAL_LOGIN; 1652 } 1653 1654 static void 1655 idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1656 { 1657 idm_pdu_t *pdu = (idm_pdu_t *)event_ctx->iec_info; 1658 iscsi_login_hdr_t *login_req = 1659 (iscsi_login_hdr_t *)pdu->isp_hdr; 1660 1661 ASSERT((event_ctx->iec_event == CE_LOGIN_SUCCESS_RCV) || 1662 (event_ctx->iec_event == CE_LOGIN_SUCCESS_SND)); 1663 1664 /* 1665 * Save off CID 1666 */ 1667 mutex_enter(&ic->ic_state_mutex); 1668 ic->ic_login_cid = ntohs(login_req->cid); 1669 ic->ic_login_info_valid = B_TRUE; 1670 1671 mutex_exit(&ic->ic_state_mutex); 1672 } 1673