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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/cpuvar.h> 27 #include <sys/types.h> 28 #include <sys/conf.h> 29 #include <sys/file.h> 30 #include <sys/ddi.h> 31 #include <sys/sunddi.h> 32 #include <sys/modctl.h> 33 #include <sys/sysmacros.h> 34 35 #include <sys/socket.h> 36 #include <sys/strsubr.h> 37 #include <sys/note.h> 38 #include <sys/sdt.h> 39 40 #include <sys/stmf.h> 41 #include <sys/stmf_ioctl.h> 42 #include <sys/portif.h> 43 #include <sys/idm/idm.h> 44 45 #define ISCSIT_SESS_SM_STRINGS 46 #include <iscsit.h> 47 48 typedef struct { 49 list_node_t se_ctx_node; 50 iscsit_session_event_t se_ctx_event; 51 iscsit_conn_t *se_event_data; 52 } sess_event_ctx_t; 53 54 static void 55 sess_sm_event_locked(iscsit_sess_t *ist, iscsit_session_event_t event, 56 iscsit_conn_t *ict); 57 58 static void 59 sess_sm_event_dispatch(iscsit_sess_t *ist, sess_event_ctx_t *ctx); 60 61 static void 62 sess_sm_q1_free(iscsit_sess_t *ist, sess_event_ctx_t *ctx); 63 64 static void 65 sess_sm_q2_active(iscsit_sess_t *ist, sess_event_ctx_t *ctx); 66 67 static void 68 sess_sm_q3_logged_in(iscsit_sess_t *ist, sess_event_ctx_t *ctx); 69 70 static void 71 sess_sm_q4_failed(iscsit_sess_t *ist, sess_event_ctx_t *ctx); 72 73 static void 74 sess_sm_q5_continue(iscsit_sess_t *ist, sess_event_ctx_t *ctx); 75 76 static void 77 sess_sm_q6_done(iscsit_sess_t *ist, sess_event_ctx_t *ctx); 78 79 static void 80 sess_sm_q7_error(iscsit_sess_t *ist, sess_event_ctx_t *ctx); 81 82 static void 83 sess_sm_new_state(iscsit_sess_t *ist, sess_event_ctx_t *ctx, 84 iscsit_session_state_t new_state); 85 86 87 static uint16_t 88 iscsit_tsih_alloc(void) 89 { 90 uintptr_t result; 91 92 result = (uintptr_t)vmem_alloc(iscsit_global.global_tsih_pool, 93 1, VM_NOSLEEP | VM_NEXTFIT); 94 95 /* ISCSI_UNSPEC_TSIH (0) indicates failure */ 96 if (result > ISCSI_MAX_TSIH) { 97 vmem_free(iscsit_global.global_tsih_pool, (void *)result, 1); 98 result = ISCSI_UNSPEC_TSIH; 99 } 100 101 return ((uint16_t)result); 102 } 103 104 static void 105 iscsit_tsih_free(uint16_t tsih) 106 { 107 vmem_free(iscsit_global.global_tsih_pool, (void *)(uintptr_t)tsih, 1); 108 } 109 110 111 iscsit_sess_t * 112 iscsit_sess_create(iscsit_tgt_t *tgt, iscsit_conn_t *ict, 113 uint32_t cmdsn, uint8_t *isid, uint16_t tag, 114 char *initiator_name, char *target_name, 115 uint8_t *error_class, uint8_t *error_detail) 116 { 117 iscsit_sess_t *result; 118 119 *error_class = ISCSI_STATUS_CLASS_SUCCESS; 120 121 /* 122 * Even if this session create "fails" for some reason we still need 123 * to return a valid session pointer so that we can send the failed 124 * login response. 125 */ 126 result = kmem_zalloc(sizeof (*result), KM_SLEEP); 127 128 /* Allocate TSIH */ 129 if ((result->ist_tsih = iscsit_tsih_alloc()) == ISCSI_UNSPEC_TSIH) { 130 /* Out of TSIH's */ 131 *error_class = ISCSI_STATUS_CLASS_TARGET_ERR; 132 *error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES; 133 /* 134 * Continue initializing this session so we can use it 135 * to complete the login process. 136 */ 137 } 138 139 idm_sm_audit_init(&result->ist_state_audit); 140 rw_init(&result->ist_sn_rwlock, NULL, RW_DRIVER, NULL); 141 mutex_init(&result->ist_mutex, NULL, MUTEX_DEFAULT, NULL); 142 cv_init(&result->ist_cv, NULL, CV_DEFAULT, NULL); 143 list_create(&result->ist_events, sizeof (sess_event_ctx_t), 144 offsetof(sess_event_ctx_t, se_ctx_node)); 145 list_create(&result->ist_conn_list, sizeof (iscsit_conn_t), 146 offsetof(iscsit_conn_t, ict_sess_ln)); 147 148 result->ist_state = SS_Q1_FREE; 149 result->ist_last_state = SS_Q1_FREE; 150 bcopy(isid, result->ist_isid, ISCSI_ISID_LEN); 151 result->ist_tpgt_tag = tag; 152 153 result->ist_tgt = tgt; 154 /* 155 * cmdsn/expcmdsn do not advance during login phase. 156 */ 157 result->ist_expcmdsn = cmdsn; 158 result->ist_maxcmdsn = result->ist_expcmdsn + 1; 159 160 result->ist_initiator_name = 161 kmem_alloc(strlen(initiator_name) + 1, KM_SLEEP); 162 (void) strcpy(result->ist_initiator_name, initiator_name); 163 if (target_name) { 164 /* A discovery session might not have a target name */ 165 result->ist_target_name = 166 kmem_alloc(strlen(target_name) + 1, KM_SLEEP); 167 (void) strcpy(result->ist_target_name, target_name); 168 } 169 idm_refcnt_init(&result->ist_refcnt, result); 170 171 /* Login code will fill in ist_stmf_sess if necessary */ 172 173 if (*error_class == ISCSI_STATUS_CLASS_SUCCESS) { 174 /* 175 * Make sure the service is still enabled and if so get a global 176 * hold to represent this session. 177 */ 178 ISCSIT_GLOBAL_LOCK(RW_READER); 179 if (iscsit_global.global_svc_state == ISE_ENABLED) { 180 iscsit_global_hold(); 181 ISCSIT_GLOBAL_UNLOCK(); 182 183 /* 184 * Kick session state machine (also binds connection 185 * to session) 186 */ 187 iscsit_sess_sm_event(result, SE_CONN_IN_LOGIN, ict); 188 189 *error_class = ISCSI_STATUS_CLASS_SUCCESS; 190 } else { 191 ISCSIT_GLOBAL_UNLOCK(); 192 *error_class = ISCSI_STATUS_CLASS_TARGET_ERR; 193 *error_detail = ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE; 194 } 195 } 196 197 /* 198 * As noted above we must return a session pointer even if something 199 * failed. The resources will get freed later. 200 */ 201 return (result); 202 } 203 204 static void 205 iscsit_sess_unref(void *ist_void) 206 { 207 iscsit_sess_t *ist = ist_void; 208 209 /* 210 * State machine has run to completion, destroy session 211 * 212 * If we have an associated STMF session we should clean it 213 * up now. 214 * 215 * This session is no longer associated with a target at this 216 * point so don't touch the target. 217 */ 218 mutex_enter(&ist->ist_mutex); 219 ASSERT(ist->ist_conn_count == 0); 220 if (ist->ist_stmf_sess != NULL) { 221 stmf_deregister_scsi_session(ist->ist_lport, 222 ist->ist_stmf_sess); 223 kmem_free(ist->ist_stmf_sess->ss_rport_id, 224 sizeof (scsi_devid_desc_t) + 225 strlen(ist->ist_initiator_name) + 1); 226 stmf_free(ist->ist_stmf_sess); 227 } 228 mutex_exit(&ist->ist_mutex); 229 230 iscsit_sess_destroy(ist); 231 iscsit_global_rele(); 232 } 233 234 void 235 iscsit_sess_destroy(iscsit_sess_t *ist) 236 { 237 idm_refcnt_destroy(&ist->ist_refcnt); 238 if (ist->ist_initiator_name) 239 kmem_free(ist->ist_initiator_name, 240 strlen(ist->ist_initiator_name) + 1); 241 if (ist->ist_initiator_alias) 242 kmem_free(ist->ist_initiator_alias, 243 strlen(ist->ist_initiator_alias) + 1); 244 if (ist->ist_target_name) 245 kmem_free(ist->ist_target_name, 246 strlen(ist->ist_target_name) + 1); 247 if (ist->ist_target_alias) 248 kmem_free(ist->ist_target_alias, 249 strlen(ist->ist_target_alias) + 1); 250 list_destroy(&ist->ist_conn_list); 251 list_destroy(&ist->ist_events); 252 cv_destroy(&ist->ist_cv); 253 mutex_destroy(&ist->ist_mutex); 254 rw_destroy(&ist->ist_sn_rwlock); 255 kmem_free(ist, sizeof (*ist)); 256 } 257 258 void 259 iscsit_sess_close(iscsit_sess_t *ist) 260 { 261 iscsit_conn_t *ict; 262 263 mutex_enter(&ist->ist_mutex); 264 /* 265 * Note in the session state that we are forcing this session 266 * to close so that the session state machine can avoid 267 * pointless delays like transitions to SS_Q4_FAILED state. 268 */ 269 ist->ist_admin_close = B_TRUE; 270 if (ist->ist_state == SS_Q3_LOGGED_IN) { 271 for (ict = list_head(&ist->ist_conn_list); 272 ict != NULL; 273 ict = list_next(&ist->ist_conn_list, ict)) { 274 iscsit_send_async_event(ict, 275 ISCSI_ASYNC_EVENT_REQUEST_LOGOUT); 276 } 277 } 278 mutex_exit(&ist->ist_mutex); 279 } 280 281 282 void 283 iscsit_sess_bind_conn(iscsit_sess_t *ist, iscsit_conn_t *ict) 284 { 285 iscsit_conn_hold(ict); 286 iscsit_sess_hold(ist); 287 ict->ict_sess = ist; 288 mutex_enter(&ist->ist_mutex); 289 ist->ist_conn_count++; 290 list_insert_tail(&ist->ist_conn_list, ict); 291 mutex_exit(&ist->ist_mutex); 292 } 293 294 void 295 iscsit_sess_unbind_conn(iscsit_sess_t *ist, iscsit_conn_t *ict) 296 { 297 mutex_enter(&ist->ist_mutex); 298 list_remove(&ist->ist_conn_list, ict); 299 ist->ist_conn_count--; 300 mutex_exit(&ist->ist_mutex); 301 iscsit_sess_rele(ist); 302 iscsit_conn_rele(ict); 303 } 304 305 void 306 iscsit_sess_hold(iscsit_sess_t *ist) 307 { 308 idm_refcnt_hold(&ist->ist_refcnt); 309 } 310 311 void 312 iscsit_sess_rele(iscsit_sess_t *ist) 313 { 314 idm_refcnt_rele(&ist->ist_refcnt); 315 } 316 317 iscsit_conn_t * 318 iscsit_sess_lookup_conn(iscsit_sess_t *ist, uint16_t cid) 319 { 320 iscsit_conn_t *result; 321 322 mutex_enter(&ist->ist_mutex); 323 for (result = list_head(&ist->ist_conn_list); 324 result != NULL; 325 result = list_next(&ist->ist_conn_list, result)) { 326 if (result->ict_cid == cid) { 327 iscsit_conn_hold(result); 328 mutex_exit(&ist->ist_mutex); 329 return (result); 330 } 331 } 332 mutex_exit(&ist->ist_mutex); 333 334 return (NULL); 335 } 336 337 iscsit_sess_t * 338 iscsit_sess_reinstate(iscsit_tgt_t *tgt, iscsit_sess_t *ist, iscsit_conn_t *ict, 339 uint8_t *error_class, uint8_t *error_detail) 340 { 341 iscsit_sess_t *new_sess; 342 343 mutex_enter(&ist->ist_mutex); 344 345 /* 346 * Session reinstatement replaces a current session with a new session. 347 * The new session will have the same ISID as the existing session. 348 */ 349 new_sess = iscsit_sess_create(tgt, ict, 0, 350 ist->ist_isid, ist->ist_tpgt_tag, 351 ist->ist_initiator_name, ist->ist_target_name, 352 error_class, error_detail); 353 ASSERT(new_sess != NULL); 354 355 /* Copy additional fields from original session */ 356 new_sess->ist_expcmdsn = ist->ist_expcmdsn; 357 new_sess->ist_maxcmdsn = ist->ist_expcmdsn + 1; 358 359 if (ist->ist_state != SS_Q6_DONE && 360 ist->ist_state != SS_Q7_ERROR) { 361 /* 362 * Generate reinstate event 363 */ 364 sess_sm_event_locked(ist, SE_SESSION_REINSTATE, NULL); 365 } 366 mutex_exit(&ist->ist_mutex); 367 368 return (new_sess); 369 } 370 371 int 372 iscsit_sess_avl_compare(const void *void_sess1, const void *void_sess2) 373 { 374 const iscsit_sess_t *sess1 = void_sess1; 375 const iscsit_sess_t *sess2 = void_sess2; 376 int result; 377 378 /* 379 * Sort by initiator name, then ISID then portal group tag 380 */ 381 result = strcmp(sess1->ist_initiator_name, sess2->ist_initiator_name); 382 if (result < 0) { 383 return (-1); 384 } else if (result > 0) { 385 return (1); 386 } 387 388 /* 389 * Initiator names match, compare ISIDs 390 */ 391 result = memcmp(sess1->ist_isid, sess2->ist_isid, ISCSI_ISID_LEN); 392 if (result < 0) { 393 return (-1); 394 } else if (result > 0) { 395 return (1); 396 } 397 398 /* 399 * ISIDs match, compare portal group tags 400 */ 401 if (sess1->ist_tpgt_tag < sess2->ist_tpgt_tag) { 402 return (-1); 403 } else if (sess1->ist_tpgt_tag > sess2->ist_tpgt_tag) { 404 return (1); 405 } 406 407 /* 408 * Portal group tags match, compare TSIHs 409 */ 410 if (sess1->ist_tsih < sess2->ist_tsih) { 411 return (-1); 412 } else if (sess1->ist_tsih > sess2->ist_tsih) { 413 return (1); 414 } 415 416 /* 417 * Sessions match 418 */ 419 return (0); 420 } 421 422 423 /* 424 * State machine 425 */ 426 427 void 428 iscsit_sess_sm_event(iscsit_sess_t *ist, iscsit_session_event_t event, 429 iscsit_conn_t *ict) 430 { 431 mutex_enter(&ist->ist_mutex); 432 sess_sm_event_locked(ist, event, ict); 433 mutex_exit(&ist->ist_mutex); 434 } 435 436 static void 437 sess_sm_event_locked(iscsit_sess_t *ist, iscsit_session_event_t event, 438 iscsit_conn_t *ict) 439 { 440 sess_event_ctx_t *ctx; 441 442 iscsit_sess_hold(ist); 443 444 ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP); 445 446 ctx->se_ctx_event = event; 447 ctx->se_event_data = ict; 448 449 list_insert_tail(&ist->ist_events, ctx); 450 /* 451 * Use the ist_sm_busy to keep the state machine single threaded. 452 * This also serves as recursion avoidance since this flag will 453 * always be set if we call login_sm_event from within the 454 * state machine code. 455 */ 456 if (!ist->ist_sm_busy) { 457 ist->ist_sm_busy = B_TRUE; 458 while (!list_is_empty(&ist->ist_events)) { 459 ctx = list_head(&ist->ist_events); 460 list_remove(&ist->ist_events, ctx); 461 idm_sm_audit_event(&ist->ist_state_audit, 462 SAS_ISCSIT_SESS, (int)ist->ist_state, 463 (int)ctx->se_ctx_event, (uintptr_t)ict); 464 mutex_exit(&ist->ist_mutex); 465 sess_sm_event_dispatch(ist, ctx); 466 mutex_enter(&ist->ist_mutex); 467 } 468 ist->ist_sm_busy = B_FALSE; 469 470 } 471 472 iscsit_sess_rele(ist); 473 } 474 475 static void 476 sess_sm_event_dispatch(iscsit_sess_t *ist, sess_event_ctx_t *ctx) 477 { 478 iscsit_conn_t *ict; 479 480 DTRACE_PROBE2(session__event, iscsit_sess_t *, ist, 481 sess_event_ctx_t *, ctx); 482 483 IDM_SM_LOG(CE_NOTE, "sess_sm_event_dispatch: sess %p event %s(%d)", 484 (void *)ist, iscsit_se_name[ctx->se_ctx_event], ctx->se_ctx_event); 485 486 /* State independent actions */ 487 switch (ctx->se_ctx_event) { 488 case SE_CONN_IN_LOGIN: 489 ict = ctx->se_event_data; 490 iscsit_sess_bind_conn(ist, ict); 491 break; 492 case SE_CONN_FAIL: 493 ict = ctx->se_event_data; 494 iscsit_sess_unbind_conn(ist, ict); 495 break; 496 } 497 498 /* State dependent actions */ 499 switch (ist->ist_state) { 500 case SS_Q1_FREE: 501 sess_sm_q1_free(ist, ctx); 502 break; 503 case SS_Q2_ACTIVE: 504 sess_sm_q2_active(ist, ctx); 505 break; 506 case SS_Q3_LOGGED_IN: 507 sess_sm_q3_logged_in(ist, ctx); 508 break; 509 case SS_Q4_FAILED: 510 sess_sm_q4_failed(ist, ctx); 511 break; 512 case SS_Q5_CONTINUE: 513 sess_sm_q5_continue(ist, ctx); 514 break; 515 case SS_Q6_DONE: 516 sess_sm_q6_done(ist, ctx); 517 break; 518 case SS_Q7_ERROR: 519 sess_sm_q7_error(ist, ctx); 520 break; 521 default: 522 ASSERT(0); 523 break; 524 } 525 526 kmem_free(ctx, sizeof (*ctx)); 527 } 528 529 static void 530 sess_sm_q1_free(iscsit_sess_t *ist, sess_event_ctx_t *ctx) 531 { 532 switch (ctx->se_ctx_event) { 533 case SE_CONN_IN_LOGIN: 534 /* N1 */ 535 sess_sm_new_state(ist, ctx, SS_Q2_ACTIVE); 536 break; 537 default: 538 ASSERT(0); 539 break; 540 } 541 } 542 543 544 static void 545 sess_sm_q2_active(iscsit_sess_t *ist, sess_event_ctx_t *ctx) 546 { 547 switch (ctx->se_ctx_event) { 548 case SE_CONN_LOGGED_IN: 549 /* N2 track FFP connections */ 550 ist->ist_ffp_conn_count++; 551 sess_sm_new_state(ist, ctx, SS_Q3_LOGGED_IN); 552 break; 553 case SE_CONN_IN_LOGIN: 554 /* N2.1, don't care stay in this state */ 555 break; 556 case SE_CONN_FAIL: 557 /* N9 */ 558 sess_sm_new_state(ist, ctx, SS_Q7_ERROR); 559 break; 560 case SE_SESSION_REINSTATE: 561 /* N11 */ 562 sess_sm_new_state(ist, ctx, SS_Q6_DONE); 563 break; 564 default: 565 ASSERT(0); 566 break; 567 } 568 } 569 570 static void 571 sess_sm_q3_logged_in(iscsit_sess_t *ist, sess_event_ctx_t *ctx) 572 { 573 iscsit_conn_t *ict; 574 575 switch (ctx->se_ctx_event) { 576 case SE_CONN_IN_LOGIN: 577 case SE_CONN_FAIL: 578 /* N2.2, don't care */ 579 break; 580 case SE_CONN_LOGGED_IN: 581 /* N2.2, track FFP connections */ 582 ist->ist_ffp_conn_count++; 583 break; 584 case SE_CONN_FFP_FAIL: 585 case SE_CONN_FFP_DISABLE: 586 /* 587 * Event data from event context is the associated connection 588 * which in this case happens to be the last FFP connection 589 * for the session. In certain cases we need to refer 590 * to this last valid connection (i.e. RFC3720 section 12.16) 591 * so we'll save off a pointer here for later use. 592 */ 593 ASSERT(ist->ist_ffp_conn_count >= 1); 594 ist->ist_failed_conn = (iscsit_conn_t *)ctx->se_event_data; 595 ist->ist_ffp_conn_count--; 596 if (ist->ist_ffp_conn_count == 0) { 597 /* 598 * N5(fail) or N3(disable) 599 * 600 * If the event is SE_CONN_FFP_FAIL but we are 601 * in the midst of an administrative session close 602 * because of a service or target offline then 603 * there is no need to go to "failed" state. 604 */ 605 sess_sm_new_state(ist, ctx, 606 ((ctx->se_ctx_event == SE_CONN_FFP_DISABLE) || 607 (ist->ist_admin_close)) ? 608 SS_Q6_DONE : SS_Q4_FAILED); 609 } 610 break; 611 case SE_SESSION_CLOSE: 612 case SE_SESSION_REINSTATE: 613 /* N3 */ 614 mutex_enter(&ist->ist_mutex); 615 if (ctx->se_ctx_event == SE_SESSION_CLOSE) { 616 ASSERT(ist->ist_ffp_conn_count >= 1); 617 ist->ist_ffp_conn_count--; 618 } 619 for (ict = list_head(&ist->ist_conn_list); 620 ict != NULL; 621 ict = list_next(&ist->ist_conn_list, ict)) { 622 if ((ctx->se_ctx_event == SE_SESSION_CLOSE) && 623 ((iscsit_conn_t *)ctx->se_event_data == ict)) { 624 /* 625 * Skip this connection since it will 626 * see the logout response 627 */ 628 continue; 629 } 630 idm_conn_event(ict->ict_ic, CE_LOGOUT_SESSION_SUCCESS, 631 NULL); 632 } 633 mutex_exit(&ist->ist_mutex); 634 635 sess_sm_new_state(ist, ctx, SS_Q6_DONE); 636 break; 637 default: 638 ASSERT(0); 639 break; 640 } 641 } 642 643 static void 644 sess_sm_timeout(void *arg) 645 { 646 iscsit_sess_t *ist = arg; 647 648 iscsit_sess_sm_event(ist, SE_SESSION_TIMEOUT, NULL); 649 } 650 651 static void 652 sess_sm_q4_failed(iscsit_sess_t *ist, sess_event_ctx_t *ctx) 653 { 654 /* Session timer must not be running when we leave this event */ 655 switch (ctx->se_ctx_event) { 656 case SE_CONN_IN_LOGIN: 657 /* N7 */ 658 sess_sm_new_state(ist, ctx, SS_Q5_CONTINUE); 659 break; 660 case SE_SESSION_REINSTATE: 661 /* N6 */ 662 (void) untimeout(ist->ist_state_timeout); 663 /*FALLTHROUGH*/ 664 case SE_SESSION_TIMEOUT: 665 /* N6 */ 666 sess_sm_new_state(ist, ctx, SS_Q6_DONE); 667 break; 668 case SE_CONN_FAIL: 669 /* Don't care */ 670 break; 671 default: 672 ASSERT(0); 673 break; 674 } 675 } 676 677 static void 678 sess_sm_q5_continue(iscsit_sess_t *ist, sess_event_ctx_t *ctx) 679 { 680 switch (ctx->se_ctx_event) { 681 case SE_CONN_FAIL: 682 /* N5 */ 683 sess_sm_new_state(ist, ctx, SS_Q4_FAILED); 684 break; 685 case SE_CONN_LOGGED_IN: 686 /* N10 */ 687 sess_sm_new_state(ist, ctx, SS_Q3_LOGGED_IN); 688 break; 689 case SE_SESSION_REINSTATE: 690 /* N11 */ 691 sess_sm_new_state(ist, ctx, SS_Q6_DONE); 692 break; 693 default: 694 ASSERT(0); 695 break; 696 } 697 } 698 699 static void 700 sess_sm_q6_done(iscsit_sess_t *ist, sess_event_ctx_t *ctx) 701 { 702 /* Terminal state */ 703 switch (ctx->se_ctx_event) { 704 case SE_CONN_LOGGED_IN: 705 /* 706 * It's possible to get this event if we encountered 707 * an SE_SESSION_REINSTATE_EVENT while we were in 708 * SS_Q2_ACTIVE state. If so we want to update 709 * ist->ist_ffp_conn_count because we know an 710 * SE_CONN_FFP_FAIL or SE_CONN_FFP_DISABLE is on the 711 * way. 712 */ 713 ist->ist_ffp_conn_count++; 714 break; 715 case SE_CONN_FFP_FAIL: 716 case SE_CONN_FFP_DISABLE: 717 ASSERT(ist->ist_ffp_conn_count >= 1); 718 ist->ist_ffp_conn_count--; 719 break; 720 case SE_CONN_FAIL: 721 if (ist->ist_conn_count == 0) { 722 idm_refcnt_async_wait_ref(&ist->ist_refcnt, 723 &iscsit_sess_unref); 724 } 725 break; 726 default: 727 break; 728 } 729 } 730 731 static void 732 sess_sm_q7_error(iscsit_sess_t *ist, sess_event_ctx_t *ctx) 733 { 734 /* Terminal state */ 735 switch (ctx->se_ctx_event) { 736 case SE_CONN_FAIL: 737 if (ist->ist_conn_count == 0) { 738 idm_refcnt_async_wait_ref(&ist->ist_refcnt, 739 &iscsit_sess_unref); 740 } 741 break; 742 default: 743 break; 744 } 745 } 746 747 static void 748 sess_sm_new_state(iscsit_sess_t *ist, sess_event_ctx_t *ctx, 749 iscsit_session_state_t new_state) 750 { 751 int t2r_secs; 752 753 /* 754 * Validate new state 755 */ 756 ASSERT(new_state != SS_UNDEFINED); 757 ASSERT3U(new_state, <, SS_MAX_STATE); 758 759 new_state = (new_state < SS_MAX_STATE) ? 760 new_state : SS_UNDEFINED; 761 762 IDM_SM_LOG(CE_NOTE, "sess_sm_new_state: sess %p, evt %s(%d), " 763 "%s(%d) --> %s(%d)\n", (void *) ist, 764 iscsit_se_name[ctx->se_ctx_event], ctx->se_ctx_event, 765 iscsit_ss_name[ist->ist_state], ist->ist_state, 766 iscsit_ss_name[new_state], new_state); 767 768 DTRACE_PROBE3(sess__state__change, 769 iscsit_sess_t *, ist, sess_event_ctx_t *, ctx, 770 iscsit_session_state_t, new_state); 771 772 mutex_enter(&ist->ist_mutex); 773 idm_sm_audit_state_change(&ist->ist_state_audit, SAS_ISCSIT_SESS, 774 (int)ist->ist_state, (int)new_state); 775 ist->ist_last_state = ist->ist_state; 776 ist->ist_state = new_state; 777 mutex_exit(&ist->ist_mutex); 778 779 switch (ist->ist_state) { 780 case SS_Q1_FREE: 781 break; 782 case SS_Q2_ACTIVE: 783 iscsit_tgt_bind_sess(ist->ist_tgt, ist); 784 break; 785 case SS_Q3_LOGGED_IN: 786 break; 787 case SS_Q4_FAILED: 788 t2r_secs = 789 ist->ist_failed_conn->ict_op.op_default_time_2_retain; 790 ist->ist_state_timeout = timeout(sess_sm_timeout, ist, 791 drv_usectohz(t2r_secs*1000000)); 792 break; 793 case SS_Q5_CONTINUE: 794 break; 795 case SS_Q6_DONE: 796 case SS_Q7_ERROR: 797 /* 798 * We won't need our TSIH anymore and it represents an 799 * implicit reference to the global TSIH pool. Get rid 800 * of it. 801 */ 802 if (ist->ist_tsih != ISCSI_UNSPEC_TSIH) { 803 iscsit_tsih_free(ist->ist_tsih); 804 } 805 806 /* 807 * We don't want this session to show up anymore so unbind 808 * it now. After this call this session cannot have any 809 * references outside itself (implicit or explicit). 810 */ 811 iscsit_tgt_unbind_sess(ist->ist_tgt, ist); 812 813 /* 814 * If we have more connections bound then more events 815 * are comming so don't wait for idle yet. 816 */ 817 if (ist->ist_conn_count == 0) { 818 idm_refcnt_async_wait_ref(&ist->ist_refcnt, 819 &iscsit_sess_unref); 820 } 821 break; 822 default: 823 ASSERT(0); 824 /*NOTREACHED*/ 825 } 826 } 827