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