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