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