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