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