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