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