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 pdu = (idm_pdu_t *)event_ctx->iec_info; 570 /* restore client callback */ 571 pdu->isp_callback = ic->ic_client_callback; 572 ic->ic_client_callback = NULL; 573 idm_pdu_complete(pdu, pdu->isp_status); 574 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL); 575 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 576 break; 577 case CE_LOGIN_FAIL_SND: 578 /* 579 * Allow the logout response pdu to be sent and defer 580 * the state machine update until the completion callback. 581 * Only 1 level or callback interposition is allowed. 582 */ 583 (void) untimeout(ic->ic_state_timeout); 584 pdu = (idm_pdu_t *)event_ctx->iec_info; 585 ASSERT(ic->ic_client_callback == NULL); 586 ic->ic_client_callback = pdu->isp_callback; 587 pdu->isp_callback = 588 idm_state_s4_in_login_fail_snd_done; 589 break; 590 case CE_LOGIN_FAIL_RCV: 591 /* 592 * Need to deliver this PDU to the initiator now because after 593 * we update the state to CS_S9_INIT_ERROR the initiator will 594 * no longer be in an appropriate state. 595 */ 596 event_ctx->iec_pdu_forwarded = B_TRUE; 597 pdu = (idm_pdu_t *)event_ctx->iec_info; 598 idm_pdu_rx_forward(ic, pdu); 599 /* FALLTHROUGH */ 600 case CE_TRANSPORT_FAIL: 601 case CE_LOGOUT_OTHER_CONN_SND: 602 case CE_LOGOUT_OTHER_CONN_RCV: 603 /* T7 */ 604 (void) untimeout(ic->ic_state_timeout); 605 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL); 606 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 607 break; 608 case CE_LOGIN_SND: 609 /* 610 * Initiator connections will see initial login PDU 611 * in this state. Target connections see initial 612 * login PDU in "xpt up" state. 613 */ 614 mutex_enter(&ic->ic_state_mutex); 615 if (!(ic->ic_state_flags & CF_INITIAL_LOGIN)) { 616 idm_initial_login_actions(ic, event_ctx); 617 } 618 mutex_exit(&ic->ic_state_mutex); 619 break; 620 case CE_MISC_TX: 621 case CE_MISC_RX: 622 case CE_LOGIN_RCV: 623 case CE_TX_PROTOCOL_ERROR: 624 case CE_RX_PROTOCOL_ERROR: 625 /* Don't care */ 626 break; 627 default: 628 ASSERT(0); 629 /*NOTREACHED*/ 630 } 631 } 632 633 634 static void 635 idm_state_s5_logged_in(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 636 { 637 switch (event_ctx->iec_event) { 638 case CE_LOGOUT_THIS_CONN_RCV: 639 case CE_LOGOUT_THIS_CONN_SND: 640 case CE_LOGOUT_OTHER_CONN_RCV: 641 case CE_LOGOUT_OTHER_CONN_SND: 642 /* T9 */ 643 idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */ 644 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx); 645 break; 646 case CE_LOGOUT_SESSION_RCV: 647 case CE_LOGOUT_SESSION_SND: 648 /* T9 */ 649 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */ 650 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx); 651 break; 652 case CE_LOGOUT_SESSION_SUCCESS: 653 /* T8 */ 654 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */ 655 656 /* Close connection */ 657 if (IDM_CONN_ISTGT(ic)) { 658 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 659 } else { 660 ic->ic_transport_ops->it_ini_conn_disconnect(ic); 661 } 662 663 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 664 break; 665 case CE_ASYNC_LOGOUT_RCV: 666 case CE_ASYNC_LOGOUT_SND: 667 /* T11 */ 668 idm_update_state(ic, CS_S7_LOGOUT_REQ, event_ctx); 669 break; 670 case CE_TRANSPORT_FAIL: 671 case CE_ASYNC_DROP_CONN_RCV: 672 case CE_ASYNC_DROP_CONN_SND: 673 case CE_ASYNC_DROP_ALL_CONN_RCV: 674 case CE_ASYNC_DROP_ALL_CONN_SND: 675 /* T15 */ 676 idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */ 677 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 678 break; 679 case CE_MISC_TX: 680 case CE_MISC_RX: 681 case CE_TX_PROTOCOL_ERROR: 682 case CE_RX_PROTOCOL_ERROR: 683 /* Don't care */ 684 break; 685 default: 686 ASSERT(0); 687 } 688 } 689 690 static void 691 idm_state_s6_in_logout_success_snd_done(idm_pdu_t *pdu, idm_status_t status) 692 { 693 idm_conn_t *ic = pdu->isp_ic; 694 695 /* 696 * This pdu callback can be invoked by the tx thread, 697 * so run the disconnect code from another thread. 698 */ 699 pdu->isp_status = status; 700 idm_conn_event(ic, CE_LOGOUT_SUCCESS_SND_DONE, (uintptr_t)pdu); 701 } 702 703 static void 704 idm_state_s6_in_logout_fail_snd_done(idm_pdu_t *pdu, idm_status_t status) 705 { 706 idm_conn_t *ic = pdu->isp_ic; 707 708 /* 709 * This pdu callback can be invoked by the tx thread, 710 * so run the disconnect code from another thread. 711 */ 712 pdu->isp_status = status; 713 idm_conn_event(ic, CE_LOGOUT_FAIL_SND_DONE, (uintptr_t)pdu); 714 } 715 716 static void 717 idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 718 { 719 idm_pdu_t *pdu; 720 721 switch (event_ctx->iec_event) { 722 case CE_LOGOUT_SUCCESS_SND_DONE: 723 pdu = (idm_pdu_t *)event_ctx->iec_info; 724 725 /* Close connection (if it's not already closed) */ 726 ASSERT(IDM_CONN_ISTGT(ic)); 727 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 728 729 /* restore client callback */ 730 pdu->isp_callback = ic->ic_client_callback; 731 ic->ic_client_callback = NULL; 732 idm_pdu_complete(pdu, pdu->isp_status); 733 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 734 break; 735 case CE_LOGOUT_FAIL_SND_DONE: 736 pdu = (idm_pdu_t *)event_ctx->iec_info; 737 /* restore client callback */ 738 pdu->isp_callback = ic->ic_client_callback; 739 ic->ic_client_callback = NULL; 740 idm_pdu_complete(pdu, pdu->isp_status); 741 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 742 break; 743 case CE_LOGOUT_SUCCESS_SND: 744 case CE_LOGOUT_FAIL_SND: 745 /* 746 * Allow the logout response pdu to be sent and defer 747 * the state machine update until the completion callback. 748 * Only 1 level or callback interposition is allowed. 749 */ 750 pdu = (idm_pdu_t *)event_ctx->iec_info; 751 ASSERT(ic->ic_client_callback == NULL); 752 ic->ic_client_callback = pdu->isp_callback; 753 if (event_ctx->iec_event == CE_LOGOUT_SUCCESS_SND) { 754 pdu->isp_callback = 755 idm_state_s6_in_logout_success_snd_done; 756 } else { 757 pdu->isp_callback = 758 idm_state_s6_in_logout_fail_snd_done; 759 } 760 break; 761 case CE_LOGOUT_SUCCESS_RCV: 762 /* 763 * Need to deliver this PDU to the initiator now because after 764 * we update the state to CS_S11_COMPLETE the initiator will 765 * no longer be in an appropriate state. 766 */ 767 event_ctx->iec_pdu_forwarded = B_TRUE; 768 pdu = (idm_pdu_t *)event_ctx->iec_info; 769 idm_pdu_rx_forward(ic, pdu); 770 /* FALLTHROUGH */ 771 case CE_LOGOUT_SESSION_SUCCESS: 772 /* T13 */ 773 774 /* Close connection (if it's not already closed) */ 775 if (IDM_CONN_ISTGT(ic)) { 776 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 777 } else { 778 ic->ic_transport_ops->it_ini_conn_disconnect(ic); 779 } 780 781 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 782 break; 783 case CE_ASYNC_LOGOUT_RCV: 784 /* T14 Do nothing */ 785 break; 786 case CE_TRANSPORT_FAIL: 787 case CE_ASYNC_DROP_CONN_RCV: 788 case CE_ASYNC_DROP_CONN_SND: 789 case CE_ASYNC_DROP_ALL_CONN_RCV: 790 case CE_ASYNC_DROP_ALL_CONN_SND: 791 case CE_LOGOUT_FAIL_RCV: 792 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 793 break; 794 case CE_TX_PROTOCOL_ERROR: 795 case CE_RX_PROTOCOL_ERROR: 796 case CE_MISC_TX: 797 case CE_MISC_RX: 798 /* Don't care */ 799 break; 800 default: 801 ASSERT(0); 802 } 803 } 804 805 806 static void 807 idm_logout_req_timeout(void *arg) 808 { 809 idm_conn_t *ic = arg; 810 811 idm_conn_event(ic, CE_LOGOUT_TIMEOUT, NULL); 812 } 813 814 static void 815 idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 816 { 817 /* Must cancel logout timer before leaving this state */ 818 switch (event_ctx->iec_event) { 819 case CE_LOGOUT_THIS_CONN_RCV: 820 case CE_LOGOUT_THIS_CONN_SND: 821 case CE_LOGOUT_OTHER_CONN_RCV: 822 case CE_LOGOUT_OTHER_CONN_SND: 823 /* T10 */ 824 if (IDM_CONN_ISTGT(ic)) { 825 (void) untimeout(ic->ic_state_timeout); 826 } 827 idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */ 828 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx); 829 break; 830 case CE_LOGOUT_SESSION_RCV: 831 case CE_LOGOUT_SESSION_SND: 832 /* T10 */ 833 if (IDM_CONN_ISTGT(ic)) { 834 (void) untimeout(ic->ic_state_timeout); 835 } 836 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */ 837 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx); 838 break; 839 case CE_ASYNC_LOGOUT_RCV: 840 case CE_ASYNC_LOGOUT_SND: 841 /* T12 Do nothing */ 842 break; 843 case CE_TRANSPORT_FAIL: 844 case CE_ASYNC_DROP_CONN_RCV: 845 case CE_ASYNC_DROP_CONN_SND: 846 case CE_ASYNC_DROP_ALL_CONN_RCV: 847 case CE_ASYNC_DROP_ALL_CONN_SND: 848 /* T16 */ 849 if (IDM_CONN_ISTGT(ic)) { 850 (void) untimeout(ic->ic_state_timeout); 851 } 852 /* FALLTHROUGH */ 853 case CE_LOGOUT_TIMEOUT: 854 idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */ 855 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 856 break; 857 case CE_LOGOUT_SESSION_SUCCESS: 858 /* T18 */ 859 if (IDM_CONN_ISTGT(ic)) { 860 (void) untimeout(ic->ic_state_timeout); 861 } 862 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */ 863 864 /* Close connection (if it's not already closed) */ 865 if (IDM_CONN_ISTGT(ic)) { 866 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 867 } else { 868 ic->ic_transport_ops->it_ini_conn_disconnect(ic); 869 } 870 871 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 872 break; 873 case CE_TX_PROTOCOL_ERROR: 874 case CE_RX_PROTOCOL_ERROR: 875 case CE_MISC_TX: 876 case CE_MISC_RX: 877 /* Don't care */ 878 break; 879 default: 880 ASSERT(0); 881 } 882 } 883 884 885 static void 886 idm_cleanup_timeout(void *arg) 887 { 888 idm_conn_t *ic = arg; 889 890 idm_conn_event(ic, CE_CLEANUP_TIMEOUT, NULL); 891 } 892 893 static void 894 idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 895 { 896 idm_pdu_t *pdu; 897 898 /* 899 * Need to cancel the cleanup timeout before leaving this state 900 * if it hasn't already fired. 901 */ 902 switch (event_ctx->iec_event) { 903 case CE_LOGOUT_SUCCESS_RCV: 904 case CE_LOGOUT_SUCCESS_SND: 905 case CE_LOGOUT_SESSION_SUCCESS: 906 (void) untimeout(ic->ic_state_timeout); 907 /*FALLTHROUGH*/ 908 case CE_CLEANUP_TIMEOUT: 909 /* M1 */ 910 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 911 break; 912 case CE_LOGOUT_OTHER_CONN_RCV: 913 case CE_LOGOUT_OTHER_CONN_SND: 914 /* M2 */ 915 idm_update_state(ic, CS_S10_IN_CLEANUP, event_ctx); 916 break; 917 case CE_LOGOUT_SUCCESS_SND_DONE: 918 case CE_LOGOUT_FAIL_SND_DONE: 919 pdu = (idm_pdu_t *)event_ctx->iec_info; 920 /* restore client callback */ 921 pdu->isp_callback = ic->ic_client_callback; 922 ic->ic_client_callback = NULL; 923 idm_pdu_complete(pdu, pdu->isp_status); 924 break; 925 case CE_LOGOUT_SESSION_RCV: 926 case CE_LOGOUT_SESSION_SND: 927 case CE_TX_PROTOCOL_ERROR: 928 case CE_RX_PROTOCOL_ERROR: 929 case CE_MISC_TX: 930 case CE_MISC_RX: 931 case CE_TRANSPORT_FAIL: 932 case CE_LOGOUT_TIMEOUT: 933 /* Don't care */ 934 break; 935 default: 936 ASSERT(0); 937 } 938 } 939 940 /* ARGSUSED */ 941 static void 942 idm_state_s9_init_error(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 943 { 944 if (ic->ic_conn_type == CONN_TYPE_INI) { 945 mutex_enter(&ic->ic_state_mutex); 946 ic->ic_state_flags |= CF_ERROR; 947 ic->ic_conn_sm_status = IDM_STATUS_FAIL; 948 cv_signal(&ic->ic_state_cv); 949 mutex_exit(&ic->ic_state_mutex); 950 } 951 } 952 953 /* ARGSUSED */ 954 static void 955 idm_state_s9a_rejected(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 956 { 957 /* Ignore events */ 958 } 959 960 static void 961 idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 962 { 963 idm_pdu_t *pdu; 964 965 /* 966 * Need to cancel the cleanup timeout before leaving this state 967 * if it hasn't already fired. 968 */ 969 switch (event_ctx->iec_event) { 970 case CE_LOGOUT_FAIL_RCV: 971 case CE_LOGOUT_FAIL_SND: 972 idm_update_state(ic, CS_S8_CLEANUP, event_ctx); 973 break; 974 case CE_LOGOUT_SUCCESS_SND: 975 case CE_LOGOUT_SUCCESS_RCV: 976 case CE_LOGOUT_SESSION_SUCCESS: 977 (void) untimeout(ic->ic_state_timeout); 978 /*FALLTHROUGH*/ 979 case CE_CLEANUP_TIMEOUT: 980 idm_update_state(ic, CS_S11_COMPLETE, event_ctx); 981 break; 982 case CE_LOGOUT_SUCCESS_SND_DONE: 983 case CE_LOGOUT_FAIL_SND_DONE: 984 pdu = (idm_pdu_t *)event_ctx->iec_info; 985 /* restore client callback */ 986 pdu->isp_callback = ic->ic_client_callback; 987 ic->ic_client_callback = NULL; 988 idm_pdu_complete(pdu, pdu->isp_status); 989 break; 990 case CE_TX_PROTOCOL_ERROR: 991 case CE_RX_PROTOCOL_ERROR: 992 case CE_MISC_TX: 993 case CE_MISC_RX: 994 case CE_LOGOUT_TIMEOUT: 995 /* Don't care */ 996 break; 997 default: 998 ASSERT(0); 999 } 1000 } 1001 1002 /* ARGSUSED */ 1003 static void 1004 idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1005 { 1006 idm_pdu_t *pdu; 1007 1008 /* 1009 * Cleanup logout success/fail completion if it's been delayed 1010 * until now. 1011 */ 1012 switch (event_ctx->iec_event) { 1013 case CE_LOGOUT_SUCCESS_SND_DONE: 1014 case CE_LOGOUT_FAIL_SND_DONE: 1015 pdu = (idm_pdu_t *)event_ctx->iec_info; 1016 /* restore client callback */ 1017 pdu->isp_callback = ic->ic_client_callback; 1018 ic->ic_client_callback = NULL; 1019 idm_pdu_complete(pdu, pdu->isp_status); 1020 break; 1021 } 1022 } 1023 1024 static void 1025 idm_state_s12_enable_dm(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1026 { 1027 switch (event_ctx->iec_event) { 1028 case CE_ENABLE_DM_SUCCESS: 1029 /* T20 */ 1030 idm_update_state(ic, CS_S5_LOGGED_IN, event_ctx); 1031 break; 1032 case CE_ENABLE_DM_FAIL: 1033 /* T21 */ 1034 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx); 1035 break; 1036 case CE_TRANSPORT_FAIL: 1037 /* 1038 * We expect to always hear back from the transport layer 1039 * once we have an "enable data-mover" request outstanding. 1040 * Therefore we'll ignore other events that may occur even 1041 * when they clearly indicate a problem and wait for 1042 * CE_ENABLE_DM_FAIL. On a related note this means the 1043 * transport must ensure that it eventually completes the 1044 * "enable data-mover" operation with either success or 1045 * failure -- otherwise we'll be stuck here. 1046 */ 1047 break; 1048 default: 1049 ASSERT(0); 1050 break; 1051 } 1052 } 1053 1054 static void 1055 idm_update_state(idm_conn_t *ic, idm_conn_state_t new_state, 1056 idm_conn_event_ctx_t *event_ctx) 1057 { 1058 int rc; 1059 idm_status_t idm_status; 1060 1061 /* 1062 * Validate new state 1063 */ 1064 ASSERT(new_state != CS_S0_UNDEFINED); 1065 ASSERT3U(new_state, <, CS_MAX_STATE); 1066 1067 /* 1068 * Update state in context. We protect this with a mutex 1069 * even though the state machine code is single threaded so that 1070 * other threads can check the state value atomically. 1071 */ 1072 new_state = (new_state < CS_MAX_STATE) ? 1073 new_state : CS_S0_UNDEFINED; 1074 1075 IDM_SM_LOG(CE_NOTE, "idm_update_state: conn %p, evt %s(%d), " 1076 "%s(%d) --> %s(%d)", (void *)ic, 1077 idm_ce_name[event_ctx->iec_event], event_ctx->iec_event, 1078 idm_cs_name[ic->ic_state], ic->ic_state, 1079 idm_cs_name[new_state], new_state); 1080 1081 DTRACE_PROBE2(conn__state__change, 1082 idm_conn_t *, ic, idm_conn_state_t, new_state); 1083 1084 mutex_enter(&ic->ic_state_mutex); 1085 idm_sm_audit_state_change(&ic->ic_state_audit, SAS_IDM_CONN, 1086 (int)ic->ic_state, (int)new_state); 1087 ic->ic_last_state = ic->ic_state; 1088 ic->ic_state = new_state; 1089 cv_signal(&ic->ic_state_cv); 1090 mutex_exit(&ic->ic_state_mutex); 1091 1092 switch (ic->ic_state) { 1093 case CS_S1_FREE: 1094 ASSERT(0); /* Initial state, can't return */ 1095 break; 1096 case CS_S2_XPT_WAIT: 1097 if ((rc = idm_ini_conn_finish(ic)) != 0) { 1098 idm_conn_event(ic, CE_CONNECT_FAIL, NULL); 1099 } else { 1100 idm_conn_event(ic, CE_CONNECT_SUCCESS, NULL); 1101 } 1102 break; 1103 case CS_S3_XPT_UP: 1104 /* 1105 * Finish any connection related setup including 1106 * waking up the idm_tgt_conn_accept thread. 1107 * and starting the login timer. If the function 1108 * fails then we return to "free" state. 1109 */ 1110 if ((rc = idm_tgt_conn_finish(ic)) != IDM_STATUS_SUCCESS) { 1111 switch (rc) { 1112 case IDM_STATUS_REJECT: 1113 idm_conn_event(ic, CE_CONNECT_REJECT, NULL); 1114 break; 1115 default: 1116 idm_conn_event(ic, CE_CONNECT_FAIL, NULL); 1117 break; 1118 } 1119 } 1120 1121 /* 1122 * First login received will cause a transition to 1123 * CS_S4_IN_LOGIN. Start login timer. 1124 */ 1125 ic->ic_state_timeout = timeout(idm_login_timeout, ic, 1126 drv_usectohz(IDM_LOGIN_SECONDS*1000000)); 1127 break; 1128 case CS_S4_IN_LOGIN: 1129 if (ic->ic_conn_type == CONN_TYPE_INI) { 1130 mutex_enter(&ic->ic_state_mutex); 1131 ic->ic_state_flags |= CF_LOGIN_READY; 1132 cv_signal(&ic->ic_state_cv); 1133 mutex_exit(&ic->ic_state_mutex); 1134 } 1135 break; 1136 case CS_S5_LOGGED_IN: 1137 ASSERT(!ic->ic_ffp); 1138 /* 1139 * IDM can go to FFP before the initiator but it 1140 * needs to go to FFP after the target (IDM target should 1141 * go to FFP after notify_ack). 1142 */ 1143 idm_status = idm_ffp_enable(ic); 1144 if (idm_status != IDM_STATUS_SUCCESS) { 1145 idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL); 1146 } 1147 1148 if (ic->ic_reinstate_conn) { 1149 /* Connection reinstatement is complete */ 1150 idm_conn_event_locked(ic->ic_reinstate_conn, 1151 CE_CONN_REINSTATE_SUCCESS, NULL, CT_NONE); 1152 } 1153 break; 1154 case CS_S6_IN_LOGOUT: 1155 break; 1156 case CS_S7_LOGOUT_REQ: 1157 /* Start logout timer for target connections */ 1158 if (IDM_CONN_ISTGT(ic)) { 1159 ic->ic_state_timeout = timeout(idm_logout_req_timeout, 1160 ic, drv_usectohz(IDM_LOGOUT_SECONDS*1000000)); 1161 } 1162 break; 1163 case CS_S8_CLEANUP: 1164 /* Close connection (if it's not already closed) */ 1165 if (IDM_CONN_ISTGT(ic)) { 1166 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 1167 } else { 1168 ic->ic_transport_ops->it_ini_conn_disconnect(ic); 1169 } 1170 1171 /* Stop executing active tasks */ 1172 idm_task_abort(ic, NULL, AT_INTERNAL_SUSPEND); 1173 1174 /* Start logout timer */ 1175 ic->ic_state_timeout = timeout(idm_cleanup_timeout, ic, 1176 drv_usectohz(IDM_CLEANUP_SECONDS*1000000)); 1177 break; 1178 case CS_S10_IN_CLEANUP: 1179 break; 1180 case CS_S9A_REJECTED: 1181 /* 1182 * We never finished establishing the connection so no 1183 * disconnect. No client notificatiosn because the client 1184 * rejected the connection. 1185 */ 1186 idm_refcnt_async_wait_ref(&ic->ic_refcnt, 1187 &idm_conn_reject_unref); 1188 break; 1189 case CS_S9_INIT_ERROR: 1190 if (IDM_CONN_ISTGT(ic)) { 1191 ic->ic_transport_ops->it_tgt_conn_disconnect(ic); 1192 } else { 1193 mutex_enter(&ic->ic_state_mutex); 1194 ic->ic_state_flags |= CF_ERROR; 1195 ic->ic_conn_sm_status = IDM_STATUS_FAIL; 1196 cv_signal(&ic->ic_state_cv); 1197 mutex_exit(&ic->ic_state_mutex); 1198 if (ic->ic_last_state != CS_S1_FREE && 1199 ic->ic_last_state != CS_S2_XPT_WAIT) { 1200 ic->ic_transport_ops->it_ini_conn_disconnect( 1201 ic); 1202 } else { 1203 (void) idm_notify_client(ic, CN_CONNECT_FAIL, 1204 NULL); 1205 } 1206 } 1207 /*FALLTHROUGH*/ 1208 case CS_S11_COMPLETE: 1209 /* 1210 * No more traffic on this connection. If this is an 1211 * initiator connection and we weren't connected yet 1212 * then don't send the "connect lost" event. 1213 * It's useful to the initiator to know whether we were 1214 * logging in at the time so send that information in the 1215 * data field. 1216 */ 1217 if (IDM_CONN_ISTGT(ic) || 1218 ((ic->ic_last_state != CS_S1_FREE) && 1219 (ic->ic_last_state != CS_S2_XPT_WAIT))) { 1220 (void) idm_notify_client(ic, CN_CONNECT_LOST, 1221 (uintptr_t)(ic->ic_last_state == CS_S4_IN_LOGIN)); 1222 } 1223 1224 /* Abort all tasks */ 1225 idm_task_abort(ic, NULL, AT_INTERNAL_ABORT); 1226 1227 /* 1228 * Handle terminal state actions on the global taskq so 1229 * we can clean up all the connection resources from 1230 * a separate thread context. 1231 */ 1232 idm_refcnt_async_wait_ref(&ic->ic_refcnt, &idm_conn_unref); 1233 break; 1234 case CS_S12_ENABLE_DM: 1235 1236 /* 1237 * The Enable DM state indicates the initiator to initiate 1238 * the hello sequence and the target to get ready to accept 1239 * the iSER Hello Message. 1240 */ 1241 idm_status = (IDM_CONN_ISINI(ic)) ? 1242 ic->ic_transport_ops->it_ini_enable_datamover(ic) : 1243 ic->ic_transport_ops->it_tgt_enable_datamover(ic); 1244 1245 if (idm_status == IDM_STATUS_SUCCESS) { 1246 idm_conn_event(ic, CE_ENABLE_DM_SUCCESS, NULL); 1247 } else { 1248 idm_conn_event(ic, CE_ENABLE_DM_FAIL, NULL); 1249 } 1250 1251 break; 1252 } 1253 } 1254 1255 1256 static void 1257 idm_conn_unref(void *ic_void) 1258 { 1259 idm_conn_t *ic = ic_void; 1260 1261 /* 1262 * Client should not be notified that the connection is destroyed 1263 * until all references on the idm connection have been removed. 1264 * Otherwise references on the associated client context would need 1265 * to be tracked separately which seems like a waste (at least when 1266 * there is a one for one correspondence with references on the 1267 * IDM connection). 1268 */ 1269 if (IDM_CONN_ISTGT(ic)) { 1270 (void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL); 1271 idm_svc_conn_destroy(ic); 1272 } else { 1273 /* Initiator may destroy connection during this call */ 1274 (void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL); 1275 } 1276 } 1277 1278 static void 1279 idm_conn_reject_unref(void *ic_void) 1280 { 1281 idm_conn_t *ic = ic_void; 1282 1283 ASSERT(IDM_CONN_ISTGT(ic)); 1284 1285 /* Don't notify the client since it rejected the connection */ 1286 idm_svc_conn_destroy(ic); 1287 } 1288 1289 1290 1291 static idm_pdu_event_action_t 1292 idm_conn_sm_validate_pdu(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx, 1293 idm_pdu_t *pdu) 1294 { 1295 char *reason_string; 1296 idm_pdu_event_action_t action; 1297 1298 ASSERT((event_ctx->iec_pdu_event_type == CT_RX_PDU) || 1299 (event_ctx->iec_pdu_event_type == CT_TX_PDU)); 1300 1301 /* 1302 * Let's check the simple stuff first. Make sure if this is a 1303 * target connection that the PDU is appropriate for a target 1304 * and if this is an initiator connection that the PDU is 1305 * appropriate for an initiator. This code is not in the data 1306 * path so organization is more important than performance. 1307 */ 1308 switch (IDM_PDU_OPCODE(pdu)) { 1309 case ISCSI_OP_NOOP_OUT: 1310 case ISCSI_OP_SCSI_CMD: 1311 case ISCSI_OP_SCSI_TASK_MGT_MSG: 1312 case ISCSI_OP_LOGIN_CMD: 1313 case ISCSI_OP_TEXT_CMD: 1314 case ISCSI_OP_SCSI_DATA: 1315 case ISCSI_OP_LOGOUT_CMD: 1316 case ISCSI_OP_SNACK_CMD: 1317 /* 1318 * Only the initiator should send these PDU's and 1319 * only the target should receive them. 1320 */ 1321 if (IDM_CONN_ISINI(ic) && 1322 (event_ctx->iec_pdu_event_type == CT_RX_PDU)) { 1323 reason_string = "Invalid RX PDU for initiator"; 1324 action = CA_RX_PROTOCOL_ERROR; 1325 goto validate_pdu_done; 1326 } 1327 1328 if (IDM_CONN_ISTGT(ic) && 1329 (event_ctx->iec_pdu_event_type == CT_TX_PDU)) { 1330 reason_string = "Invalid TX PDU for target"; 1331 action = CA_TX_PROTOCOL_ERROR; 1332 goto validate_pdu_done; 1333 } 1334 break; 1335 case ISCSI_OP_NOOP_IN: 1336 case ISCSI_OP_SCSI_RSP: 1337 case ISCSI_OP_SCSI_TASK_MGT_RSP: 1338 case ISCSI_OP_LOGIN_RSP: 1339 case ISCSI_OP_TEXT_RSP: 1340 case ISCSI_OP_SCSI_DATA_RSP: 1341 case ISCSI_OP_LOGOUT_RSP: 1342 case ISCSI_OP_RTT_RSP: 1343 case ISCSI_OP_ASYNC_EVENT: 1344 case ISCSI_OP_REJECT_MSG: 1345 /* 1346 * Only the target should send these PDU's and 1347 * only the initiator should receive them. 1348 */ 1349 if (IDM_CONN_ISTGT(ic) && 1350 (event_ctx->iec_pdu_event_type == CT_RX_PDU)) { 1351 reason_string = "Invalid RX PDU for target"; 1352 action = CA_RX_PROTOCOL_ERROR; 1353 goto validate_pdu_done; 1354 } 1355 1356 if (IDM_CONN_ISINI(ic) && 1357 (event_ctx->iec_pdu_event_type == CT_TX_PDU)) { 1358 reason_string = "Invalid TX PDU for initiator"; 1359 action = CA_TX_PROTOCOL_ERROR; 1360 goto validate_pdu_done; 1361 } 1362 break; 1363 default: 1364 reason_string = "Unknown PDU Type"; 1365 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1366 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1367 goto validate_pdu_done; 1368 } 1369 1370 /* 1371 * Now validate the opcodes against the current state. 1372 */ 1373 reason_string = "PDU not allowed in current state"; 1374 switch (IDM_PDU_OPCODE(pdu)) { 1375 case ISCSI_OP_NOOP_OUT: 1376 case ISCSI_OP_NOOP_IN: 1377 /* 1378 * Obviously S1-S3 are not allowed since login hasn't started. 1379 * S8 is probably out as well since the connection has been 1380 * dropped. 1381 */ 1382 switch (ic->ic_state) { 1383 case CS_S4_IN_LOGIN: 1384 case CS_S5_LOGGED_IN: 1385 case CS_S6_IN_LOGOUT: 1386 case CS_S7_LOGOUT_REQ: 1387 action = CA_FORWARD; 1388 goto validate_pdu_done; 1389 case CS_S8_CLEANUP: 1390 case CS_S10_IN_CLEANUP: 1391 action = CA_DROP; 1392 break; 1393 default: 1394 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1395 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1396 goto validate_pdu_done; 1397 } 1398 /*NOTREACHED*/ 1399 case ISCSI_OP_SCSI_CMD: 1400 case ISCSI_OP_SCSI_RSP: 1401 case ISCSI_OP_SCSI_TASK_MGT_MSG: 1402 case ISCSI_OP_SCSI_TASK_MGT_RSP: 1403 case ISCSI_OP_SCSI_DATA: 1404 case ISCSI_OP_SCSI_DATA_RSP: 1405 case ISCSI_OP_RTT_RSP: 1406 case ISCSI_OP_SNACK_CMD: 1407 case ISCSI_OP_TEXT_CMD: 1408 case ISCSI_OP_TEXT_RSP: 1409 switch (ic->ic_state) { 1410 case CS_S5_LOGGED_IN: 1411 case CS_S6_IN_LOGOUT: 1412 case CS_S7_LOGOUT_REQ: 1413 action = CA_FORWARD; 1414 goto validate_pdu_done; 1415 case CS_S8_CLEANUP: 1416 case CS_S10_IN_CLEANUP: 1417 action = CA_DROP; 1418 break; 1419 default: 1420 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1421 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1422 goto validate_pdu_done; 1423 } 1424 /*NOTREACHED*/ 1425 case ISCSI_OP_LOGOUT_CMD: 1426 case ISCSI_OP_LOGOUT_RSP: 1427 case ISCSI_OP_REJECT_MSG: 1428 case ISCSI_OP_ASYNC_EVENT: 1429 switch (ic->ic_state) { 1430 case CS_S5_LOGGED_IN: 1431 case CS_S6_IN_LOGOUT: 1432 case CS_S7_LOGOUT_REQ: 1433 action = CA_FORWARD; 1434 goto validate_pdu_done; 1435 case CS_S8_CLEANUP: 1436 case CS_S10_IN_CLEANUP: 1437 action = CA_DROP; 1438 break; 1439 default: 1440 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1441 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1442 goto validate_pdu_done; 1443 } 1444 /*NOTREACHED*/ 1445 case ISCSI_OP_LOGIN_CMD: 1446 case ISCSI_OP_LOGIN_RSP: 1447 switch (ic->ic_state) { 1448 case CS_S3_XPT_UP: 1449 case CS_S4_IN_LOGIN: 1450 action = CA_FORWARD; 1451 goto validate_pdu_done; 1452 default: 1453 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1454 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1455 goto validate_pdu_done; 1456 } 1457 /*NOTREACHED*/ 1458 default: 1459 /* This should never happen -- we already checked above */ 1460 ASSERT(0); 1461 /*NOTREACHED*/ 1462 } 1463 1464 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ? 1465 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR); 1466 1467 validate_pdu_done: 1468 if (action != CA_FORWARD) { 1469 DTRACE_PROBE2(idm__int__protocol__error, 1470 idm_conn_event_ctx_t *, event_ctx, 1471 char *, reason_string); 1472 } 1473 1474 return (action); 1475 } 1476 1477 /* ARGSUSED */ 1478 void 1479 idm_pdu_tx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu) 1480 { 1481 /* 1482 * Return the PDU to the caller indicating it was a protocol error. 1483 * Caller can take appropriate action. 1484 */ 1485 idm_pdu_complete(pdu, IDM_STATUS_PROTOCOL_ERROR); 1486 } 1487 1488 void 1489 idm_pdu_rx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu) 1490 { 1491 /* 1492 * Forward PDU to caller indicating it is a protocol error. 1493 * Caller should take appropriate action. 1494 */ 1495 (*ic->ic_conn_ops.icb_rx_error)(ic, pdu, IDM_STATUS_PROTOCOL_ERROR); 1496 } 1497 1498 idm_status_t 1499 idm_notify_client(idm_conn_t *ic, idm_client_notify_t cn, uintptr_t data) 1500 { 1501 /* 1502 * We may want to make this more complicated at some point but 1503 * for now lets just call the client's notify function and return 1504 * the status. 1505 */ 1506 cn = (cn > CN_MAX) ? CN_MAX : cn; 1507 IDM_SM_LOG(CE_NOTE, "idm_notify_client: ic=%p %s(%d)\n", 1508 (void *)ic, idm_cn_strings[cn], cn); 1509 return ((*ic->ic_conn_ops.icb_client_notify)(ic, cn, data)); 1510 } 1511 1512 static idm_status_t 1513 idm_ffp_enable(idm_conn_t *ic) 1514 { 1515 idm_status_t rc; 1516 1517 /* 1518 * On the initiator side the client will see this notification 1519 * before the actual login succes PDU. This shouldn't be a big 1520 * deal since the initiator drives the connection. It can simply 1521 * wait for the login response then start sending SCSI commands. 1522 * Kind ugly though compared with the way things work on target 1523 * connections. 1524 */ 1525 mutex_enter(&ic->ic_state_mutex); 1526 ic->ic_ffp = B_TRUE; 1527 mutex_exit(&ic->ic_state_mutex); 1528 1529 rc = idm_notify_client(ic, CN_FFP_ENABLED, NULL); 1530 if (rc != IDM_STATUS_SUCCESS) { 1531 mutex_enter(&ic->ic_state_mutex); 1532 ic->ic_ffp = B_FALSE; 1533 mutex_exit(&ic->ic_state_mutex); 1534 } 1535 return (rc); 1536 } 1537 1538 static void 1539 idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type) 1540 { 1541 mutex_enter(&ic->ic_state_mutex); 1542 ic->ic_ffp = B_FALSE; 1543 mutex_exit(&ic->ic_state_mutex); 1544 1545 /* Client can't "fail" CN_FFP_DISABLED */ 1546 (void) idm_notify_client(ic, CN_FFP_DISABLED, 1547 (uintptr_t)disable_type); 1548 } 1549 1550 static void 1551 idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1552 { 1553 ASSERT((event_ctx->iec_event == CE_LOGIN_RCV) || 1554 (event_ctx->iec_event == CE_LOGIN_SND)); 1555 1556 /* 1557 * Currently it's not clear what we would do here -- since 1558 * we went to the trouble of coding an "initial login" hook 1559 * we'll leave it in for now. Remove before integration if 1560 * it's not used for anything. 1561 */ 1562 ic->ic_state_flags |= CF_INITIAL_LOGIN; 1563 } 1564 1565 static void 1566 idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx) 1567 { 1568 idm_pdu_t *pdu = (idm_pdu_t *)event_ctx->iec_info; 1569 iscsi_login_hdr_t *login_req = 1570 (iscsi_login_hdr_t *)pdu->isp_hdr; 1571 1572 ASSERT((event_ctx->iec_event == CE_LOGIN_SUCCESS_RCV) || 1573 (event_ctx->iec_event == CE_LOGIN_SUCCESS_SND)); 1574 1575 /* 1576 * Save off CID 1577 */ 1578 mutex_enter(&ic->ic_state_mutex); 1579 ic->ic_login_cid = ntohs(login_req->cid); 1580 ic->ic_login_info_valid = B_TRUE; 1581 1582 mutex_exit(&ic->ic_state_mutex); 1583 } 1584