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