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