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 src/sun_nws/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 src/sun_nws/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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "@(#)iscsit_sess.c      1.2     08/03/26 SMI"
  27 
  28 #include <sys/cpuvar.h>
  29 #include <sys/types.h>
  30 #include <sys/conf.h>
  31 #include <sys/stat.h>
  32 #include <sys/file.h>
  33 #include <sys/ddi.h>
  34 #include <sys/sunddi.h>
  35 #include <sys/modctl.h>
  36 #include <sys/sysmacros.h>
  37 
  38 #include <sys/socket.h>           /* networking stuff */
  39 #include <sys/strsubr.h>  /* networking stuff */
  40 #include <sys/note.h>             /* ASSERT3U */
  41 #include <sys/sdt.h>
  42 
  43 #include <stmf.h>
  44 #include <stmf_ioctl.h>
  45 #include <portif.h>
  46 #include <idm.h>
  47 #include <iscsit.h>
  48 
  49 
  50 
  51 typedef struct {
  52         list_node_t             se_ctx_node;
  53         iscsit_session_event_t  se_ctx_event;
  54         iscsit_conn_t           *se_event_data;
  55 } sess_event_ctx_t;
  56 
  57 extern char *iscsit_ss_name[];
  58 extern char *iscsit_se_name[];
  59 
  60 static uint16_t
  61 iscsit_get_tsih(void);
  62 
  63 static void
  64 sess_sm_event_locked(iscsit_sess_t *ist, iscsit_session_event_t event,
  65 iscsit_conn_t *ict);
  66 
  67 static void
  68 sess_sm_complete(void *ist_void);
  69 
  70 static void
  71 sess_sm_event_dispatch(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
  72 
  73 static void
  74 sess_sm_q1_free(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
  75 
  76 static void
  77 sess_sm_q2_active(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
  78 
  79 static void
  80 sess_sm_q3_logged_in(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
  81 
  82 static void
  83 sess_sm_q4_failed(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
  84 
  85 static void
  86 sess_sm_q5_continue(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
  87 
  88 static void
  89 sess_sm_q6_done(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
  90 
  91 static void
  92 sess_sm_q7_error(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
  93 
  94 static void
  95 sess_sm_new_state(iscsit_sess_t *ist, sess_event_ctx_t *ctx,
  96     iscsit_session_state_t new_state);
  97 
  98 
  99 static uint16_t
 100 iscsit_tsih_alloc(void)
 101 {
 102         uintptr_t result;
 103 
 104         result = (uintptr_t)vmem_alloc(iscsit_global.global_tsih_pool,
 105             1, VM_NOSLEEP | VM_NEXTFIT);
 106 
 107         /* ISCSI_UNSPEC_TSIH (0) indicates failure */
 108         return (result > ISCSI_MAX_TSIH ? ISCSI_UNSPEC_TSIH : result);
 109 }
 110 
 111 static void
 112 iscsit_tsih_free(uint16_t tsih)
 113 {
 114         vmem_free(iscsit_global.global_tsih_pool, (void *)(uintptr_t)tsih, 1);
 115 }
 116 
 117 
 118 iscsit_sess_t *
 119 iscsit_sess_create(iscsit_tgt_t *tgt, iscsit_conn_t *ict,
 120     uint32_t cmdsn, uint8_t *isid,
 121     char *initiator_name, char *target_name,
 122     uint8_t *error_class, uint8_t *error_detail)
 123 {
 124         iscsit_sess_t *result;
 125 
 126 
 127         /*
 128          * Even if this session create "fails" for some reason we still need
 129          * to return a valid session pointer so that we can send the failed
 130          * login response.
 131          */
 132         result = kmem_zalloc(sizeof (*result), KM_SLEEP);
 133 
 134         /* Allocate TSIH */
 135         if ((result->ist_tsih = iscsit_tsih_alloc()) == ISCSI_UNSPEC_TSIH) {
 136                 /* Out of TSIH's */
 137                 *error_class = ISCSI_STATUS_CLASS_TARGET_ERR;
 138                 *error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
 139                 /*
 140                  * Continue initializing this session so we can use it
 141                  * to complete the login process.
 142                  */
 143         }
 144 
 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 
 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 
 156         result->ist_tgt = tgt;
 157         result->ist_expcmdsn = cmdsn + 1;
 158         result->ist_maxcmdsn = result->ist_expcmdsn + 1;
 159 
 160         result->ist_initiator_name =
 161             kmem_alloc(strlen(initiator_name) + 1, KM_SLEEP);
 162         strcpy(result->ist_initiator_name, initiator_name);
 163         if (target_name) {
 164                 /* A discovery session might not have a target name */
 165                 result->ist_target_name =
 166                     kmem_alloc(strlen(target_name) + 1, KM_SLEEP);
 167                 strcpy(result->ist_target_name, target_name);
 168         }
 169 
 170         /* Login code will fill in ist_stmf_sess if necessary */
 171 
 172         /* XXX set the authentication parameter */
 173         iscsit_sess_set_auth(result);
 174 
 175         /* Kick session state machine (also binds connection to session) */
 176         iscsit_sess_sm_event(result, SE_CONN_IN_LOGIN, ict);
 177 
 178         *error_class = ISCSI_STATUS_CLASS_SUCCESS;
 179 sess_create_fail:
 180         /*
 181          * As noted above we must return a session pointer even if something
 182          * failed.  The resources will get freed later.
 183          */
 184         return (result);
 185 }
 186 
 187 void
 188 iscsit_sess_destroy(iscsit_sess_t *ist)
 189 {
 190         if (ist->ist_initiator_name)
 191                 kmem_free(ist->ist_initiator_name,
 192                     strlen(ist->ist_initiator_name) + 1);
 193         if (ist->ist_target_name)
 194                 kmem_free(ist->ist_target_name,
 195                     strlen(ist->ist_target_name) + 1);
 196         list_destroy(&ist->ist_conn_list);
 197         list_destroy(&ist->ist_events);
 198         cv_destroy(&ist->ist_cv);
 199         mutex_destroy(&ist->ist_mutex);
 200         if (ist->ist_tsih != ISCSI_UNSPEC_TSIH) {
 201                 iscsit_tsih_free(ist->ist_tsih);
 202         }
 203         kmem_free(ist, sizeof (*ist));
 204 }
 205 
 206 
 207 void
 208 iscsit_sess_bind_conn(iscsit_sess_t *ist, iscsit_conn_t *ict)
 209 {
 210         ict->ict_sess = ist;
 211         mutex_enter(&ist->ist_mutex);
 212         ist->ist_conn_count++;
 213         list_insert_tail(&ist->ist_conn_list, ict);
 214         mutex_exit(&ist->ist_mutex);
 215 }
 216 
 217 void
 218 iscsit_sess_unbind_conn(iscsit_sess_t *ist, iscsit_conn_t *ict)
 219 {
 220         mutex_enter(&ist->ist_mutex);
 221         list_remove(&ist->ist_conn_list, ict);
 222         ist->ist_conn_count--;
 223         mutex_exit(&ist->ist_mutex);
 224 }
 225 
 226 iscsit_conn_t *
 227 iscsit_sess_lookup_conn(iscsit_sess_t *ist, uint16_t cid)
 228 {
 229         iscsit_conn_t *result;
 230 
 231         for (result = list_head(&ist->ist_conn_list);
 232             result != NULL;
 233             result = list_next(&ist->ist_conn_list, result)) {
 234                 if (result->ict_cid == cid)
 235                         return (result);
 236         }
 237 
 238         return (NULL);
 239 }
 240 
 241 /*
 242  * Set the authentication parameters for the session.
 243  */
 244 void
 245 iscsit_sess_set_auth(iscsit_sess_t *ist)
 246 {
 247         char    *username, *username_in;
 248         char    *password, *password_in;
 249 
 250         /* XXX  Load target CHAP name */
 251         username = "target";
 252         (void) strcpy(ist->ist_auth.username, username);
 253 
 254         /* XXX  Load target CHAP secret */
 255         password = "987654321098";
 256         (void) strcpy((char *)ist->ist_auth.password, password);
 257         ist->ist_auth.password_length = strlen(password);
 258 
 259         /* XXX  Load initiator CHAP name */
 260         username_in = "initiator";
 261         (void) strcpy(ist->ist_auth.username_in, username_in);
 262 
 263         /* XXX  Load initiator CHAP secret */
 264         password_in = "123456789012";
 265         (void) strcpy((char *)ist->ist_auth.password_in, password_in);
 266         ist->ist_auth.password_length_in = strlen(password_in);
 267 
 268         /* XXX load the applicable authentication methods */
 269         if (ist->ist_auth.password_length_in != 0) {
 270                 ist->ist_auth.authMethodValidList[0] = AM_CHAP;
 271         } else {
 272                 ist->ist_auth.authMethodValidList[0] = AM_NONE;
 273         }
 274         ist->ist_auth.authMethodValidList[1] = 0;    /* end of list */
 275 }
 276 
 277 
 278 iscsit_sess_t *
 279 iscsit_sess_reinstate(iscsit_sess_t *ist, iscsit_conn_t *ict,
 280     uint8_t *error_class, uint8_t *error_detail)
 281 {
 282         iscsit_sess_t *new_sess;
 283         boolean_t error = B_FALSE;
 284 
 285         if (ist->ist_state < SS_Q3_LOGGED_IN) {
 286                 /* Reinstatement is not valid at this point */
 287                 *error_class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
 288                 *error_detail = ISCSI_LOGIN_STATUS_INIT_ERR;
 289                 /*
 290                  * Continue with session initialization since we need to
 291                  * return a session
 292                  */
 293                 error = B_TRUE;
 294         }
 295 
 296         /*
 297          * Session reinstatement replaces a current session in
 298          * SS_Q4_FAILED state with a new session.  The new
 299          * session will have the same ISID as the existing session.
 300          */
 301         new_sess = iscsit_sess_create(ist->ist_tgt, ict, 0,
 302             ist->ist_isid, ist->ist_initiator_name, ist->ist_target_name,
 303             error_class, error_detail);
 304         ASSERT(new_sess != NULL);
 305 
 306         /* Copy additional fields from original session */
 307         new_sess->ist_expcmdsn = ist->ist_expcmdsn;
 308         new_sess->ist_maxcmdsn = ist->ist_expcmdsn + 1;
 309 
 310         if (!error) {
 311                 mutex_enter(&ist->ist_mutex);
 312                 /*
 313                  * Generate reinstate event
 314                  */
 315                 sess_sm_event_locked(ist, SE_SESSION_REINSTATE, NULL);
 316                 mutex_exit(&ist->ist_mutex);
 317         }
 318 
 319         return (new_sess);
 320 }
 321 
 322 int
 323 iscsit_sess_avl_compare(const void *void_sess1, const void *void_sess2)
 324 {
 325         const iscsit_sess_t     *sess1 = void_sess1;
 326         const iscsit_sess_t     *sess2 = void_sess2;
 327         int                     result;
 328 
 329         /*
 330          * Sort by initiator name, then ISID then TSIH
 331          */
 332         result = strcmp(sess1->ist_initiator_name, sess2->ist_initiator_name);
 333         if (result < 0) {
 334                 return (-1);
 335         } else if (result > 0) {
 336                 return (1);
 337         }
 338 
 339         /*
 340          * Initiator names match, compare ISIDs
 341          */
 342         result = memcmp(sess1->ist_isid, sess2->ist_isid, ISCSI_ISID_LEN);
 343         if (result < 0) {
 344                 return (-1);
 345         } else if (result > 0) {
 346                 return (1);
 347         }
 348 
 349         /*
 350          * ISIDs match, compare TSIHs
 351          */
 352         if (sess1->ist_tsih < sess2->ist_tsih) {
 353                 return (-1);
 354         } else if (sess1->ist_tsih > sess2->ist_tsih) {
 355                 return (1);
 356         }
 357 
 358         /*
 359          * Sessions match
 360          */
 361         return (0);
 362 }
 363 
 364 
 365 /*
 366  * State machine
 367  */
 368 
 369 void
 370 iscsit_sess_sm_event(iscsit_sess_t *ist, iscsit_session_event_t event,
 371     iscsit_conn_t *ict)
 372 {
 373         mutex_enter(&ist->ist_mutex);
 374         sess_sm_event_locked(ist, event, ict);
 375         mutex_exit(&ist->ist_mutex);
 376 }
 377 
 378 static void
 379 sess_sm_event_locked(iscsit_sess_t *ist, iscsit_session_event_t event,
 380     iscsit_conn_t *ict)
 381 {
 382         sess_event_ctx_t *ctx;
 383 
 384         ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP);
 385 
 386         ctx->se_ctx_event = event;
 387         ctx->se_event_data = ict;
 388 
 389         list_insert_tail(&ist->ist_events, ctx);
 390         /*
 391          * Use the icl_busy flag to keep the state machine single threaded.
 392          * This also serves as recursion avoidance since this flag will
 393          * always be set if we call login_sm_event from within the
 394          * state machine code.
 395          */
 396         if (!ist->ist_sm_busy) {
 397                 ist->ist_sm_busy = B_TRUE;
 398                 while (!list_is_empty(&ist->ist_events)) {
 399                         ctx = list_head(&ist->ist_events);
 400                         list_remove(&ist->ist_events, ctx);
 401                         mutex_exit(&ist->ist_mutex);
 402                         sess_sm_event_dispatch(ist, ctx);
 403                         mutex_enter(&ist->ist_mutex);
 404                 }
 405                 ist->ist_sm_busy = B_FALSE;
 406 
 407                 /*
 408                  * When the state machine reaches SS_Q6_DONE or SS_Q7_ERROR
 409                  * state the session is over and it's time to cleanup.  The
 410                  * state machine code will mark itself "complete" when this
 411                  * happens.
 412                  *
 413                  * If we get an "spurious" events after we reach SS_Q6_DONE
 414                  * or SS_Q7_ERROR we don't want to destroy again so
 415                  * set ist_sm_busy again (shouldn't happen).
 416                  */
 417                 if (ist->ist_sm_complete) {
 418                         ist->ist_sm_busy = B_TRUE;
 419                         (void) taskq_dispatch(
 420                             iscsit_global.global_dispatch_taskq,
 421                             sess_sm_complete, ist, DDI_SLEEP);
 422                 }
 423         }
 424 }
 425 
 426 static void
 427 sess_sm_complete(void *ist_void)
 428 {
 429         iscsit_sess_t *ist = ist_void;
 430 
 431         /*
 432          * State machine has run to completion, destroy session
 433          *
 434          * If we have an associated STMF session we should clean it
 435          * up now.
 436          */
 437         mutex_enter(&ist->ist_mutex);
 438 
 439         ASSERT(ist->ist_conn_count == 0);
 440         if (ist->ist_stmf_sess != NULL) {
 441                 stmf_deregister_scsi_session(ist->ist_lport,
 442                     ist->ist_stmf_sess);
 443                 kmem_free(ist->ist_stmf_sess->ss_rport_id,
 444                     sizeof (scsi_devid_desc_t) +
 445                     strlen(ist->ist_initiator_name) + 1);
 446                 stmf_free(ist->ist_stmf_sess);
 447         }
 448         mutex_exit(&ist->ist_mutex);
 449 
 450         iscsit_sess_destroy(ist);
 451 }
 452 
 453 static void
 454 sess_sm_event_dispatch(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
 455 {
 456         iscsit_conn_t   *ict;
 457 
 458         /* State independent actions */
 459         switch (ctx->se_ctx_event) {
 460         case SE_CONN_IN_LOGIN:
 461                 ict = ctx->se_event_data;
 462                 iscsit_sess_bind_conn(ist, ict);
 463                 break;
 464         case SE_CONN_FAIL:
 465                 ict = ctx->se_event_data;
 466                 iscsit_sess_unbind_conn(ist, ict);
 467                 iscsit_conn_destroy_done(ict);
 468                 break;
 469         }
 470 
 471         /* State dependent actions */
 472         switch (ist->ist_state) {
 473         case SS_Q1_FREE:
 474                 sess_sm_q1_free(ist, ctx);
 475                 break;
 476         case SS_Q2_ACTIVE:
 477                 sess_sm_q2_active(ist, ctx);
 478                 break;
 479         case SS_Q3_LOGGED_IN:
 480                 sess_sm_q3_logged_in(ist, ctx);
 481                 break;
 482         case SS_Q4_FAILED:
 483                 sess_sm_q4_failed(ist, ctx);
 484                 break;
 485         case SS_Q5_CONTINUE:
 486                 sess_sm_q5_continue(ist, ctx);
 487                 break;
 488         case SS_Q6_DONE:
 489                 sess_sm_q6_done(ist, ctx);
 490                 break;
 491         case SS_Q7_ERROR:
 492                 sess_sm_q7_error(ist, ctx);
 493                 break;
 494         default:
 495                 ASSERT(0);
 496                 break;
 497         }
 498 
 499         kmem_free(ctx, sizeof (*ctx));
 500 }
 501 
 502 static void
 503 sess_sm_q1_free(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
 504 {
 505         switch (ctx->se_ctx_event) {
 506         case SE_CONN_IN_LOGIN:
 507                 /* N1 */
 508                 sess_sm_new_state(ist, ctx, SS_Q2_ACTIVE);
 509                 break;
 510         default:
 511                 ASSERT(0);
 512                 break;
 513         }
 514 }
 515 
 516 
 517 static void
 518 sess_sm_q2_active(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
 519 {
 520         switch (ctx->se_ctx_event) {
 521         case SE_CONN_LOGGED_IN:
 522                 /* N2 track FFP connections */
 523                 ist->ist_ffp_conn_count++;
 524                 sess_sm_new_state(ist, ctx, SS_Q3_LOGGED_IN);
 525                 break;
 526         case SE_CONN_IN_LOGIN:
 527                 /* N2.1, don't care stay in this state */
 528                 break;
 529         case SE_CONN_FAIL:
 530                 /* N9 */
 531                 sess_sm_new_state(ist, ctx, SS_Q7_ERROR);
 532                 break;
 533         default:
 534                 ASSERT(0);
 535                 break;
 536         }
 537 }
 538 
 539 static void
 540 sess_sm_q3_logged_in(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
 541 {
 542         iscsit_conn_t *ict;
 543 
 544         switch (ctx->se_ctx_event) {
 545         case SE_CONN_IN_LOGIN:
 546         case SE_CONN_FAIL:
 547                 /* N2.2, don't care */
 548                 break;
 549         case SE_CONN_LOGGED_IN:
 550                 /* N2.2, track FFP connections */
 551                 ist->ist_ffp_conn_count++;
 552                 break;
 553         case SE_CONN_FFP_FAIL:
 554                 /*
 555                  * Event data from event context is the associated connection
 556                  * which in this case happens to be the last FFP connection
 557                  * for the session.  In certain cases we need to refer
 558                  * to this last valid connection (i.e. RFC3720 section 12.16)
 559                  * so we'll save off a pointer here for later use.
 560                  */
 561                 ASSERT(ist->ist_ffp_conn_count >= 1);
 562                 ist->ist_failed_conn = (iscsit_conn_t *)ctx->se_event_data;
 563                 ist->ist_ffp_conn_count--;
 564                 if (ist->ist_ffp_conn_count == 0) {
 565                         /* N5 */
 566                         sess_sm_new_state(ist, ctx, SS_Q4_FAILED);
 567                 }
 568                 break;
 569         case SE_SESSION_CLOSE:
 570         case SE_SESSION_REINSTATE:
 571                 /* N3 */
 572                 mutex_enter(&ist->ist_mutex);
 573                 for (ict = list_head(&ist->ist_conn_list);
 574                     ict != NULL;
 575                     ict = list_next(&ist->ist_conn_list, ict)) {
 576                         idm_conn_event(ict->ict_ic, CE_LOGOUT_SESSION_SUCCESS,
 577                             NULL);
 578                 }
 579                 mutex_exit(&ist->ist_mutex);
 580 
 581                 sess_sm_new_state(ist, ctx, SS_Q6_DONE);
 582                 break;
 583         default:
 584                 ASSERT(0);
 585                 break;
 586         }
 587 }
 588 
 589 static void
 590 sess_sm_timeout(void *arg)
 591 {
 592         iscsit_sess_t *ist = arg;
 593 
 594         iscsit_sess_sm_event(ist, SE_SESSION_TIMEOUT, NULL);
 595 }
 596 
 597 static void
 598 sess_sm_q4_failed(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
 599 {
 600         /* Session timer must not be running when we leave this event */
 601         switch (ctx->se_ctx_event) {
 602         case SE_CONN_IN_LOGIN:
 603                 /* N7 */
 604                 sess_sm_new_state(ist, ctx, SS_Q5_CONTINUE);
 605                 break;
 606         case SE_SESSION_REINSTATE:
 607                 /* N6 */
 608                 untimeout(ist->ist_state_timeout);
 609                 /*FALLTHROUGH*/
 610         case SE_SESSION_TIMEOUT:
 611                 /* N6 */
 612                 sess_sm_new_state(ist, ctx, SS_Q6_DONE);
 613                 break;
 614         case SE_CONN_FAIL:
 615                 /* Don't care */
 616                 break;
 617         default:
 618                 ASSERT(0);
 619                 break;
 620         }
 621 }
 622 
 623 static void
 624 sess_sm_q5_continue(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
 625 {
 626         switch (ctx->se_ctx_event) {
 627         case SE_CONN_FAIL:
 628                 /* N5 */
 629                 sess_sm_new_state(ist, ctx, SS_Q4_FAILED);
 630                 break;
 631         case SE_CONN_LOGGED_IN:
 632                 /* N10 */
 633                 sess_sm_new_state(ist, ctx, SS_Q3_LOGGED_IN);
 634                 break;
 635         case SE_SESSION_REINSTATE:
 636                 /* N11 */
 637                 sess_sm_new_state(ist, ctx, SS_Q6_DONE);
 638                 break;
 639         default:
 640                 ASSERT(0);
 641                 break;
 642         }
 643 }
 644 
 645 static void
 646 sess_sm_q6_done(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
 647 {
 648         /* Terminal state */
 649         switch (ctx->se_ctx_event) {
 650         case SE_CONN_FAIL:
 651                 if (ist->ist_conn_count == 0) {
 652                         ist->ist_sm_complete = B_TRUE;
 653                 }
 654                 break;
 655         default:
 656                 break;
 657         }
 658 }
 659 
 660 static void
 661 sess_sm_q7_error(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
 662 {
 663         /* Terminal state */
 664         switch (ctx->se_ctx_event) {
 665         case SE_CONN_FAIL:
 666                 if (ist->ist_conn_count == 0) {
 667                         ist->ist_sm_complete = B_TRUE;
 668                 }
 669                 break;
 670         default:
 671                 break;
 672         }
 673 }
 674 
 675 static void
 676 sess_sm_new_state(iscsit_sess_t *ist, sess_event_ctx_t *ctx,
 677     iscsit_session_state_t new_state)
 678 {
 679         int t2r_secs;
 680 
 681         /*
 682          * Validate new state
 683          */
 684         ASSERT(new_state != SS_UNDEFINED);
 685         ASSERT3U(new_state, <, SS_MAX_STATE);
 686 
 687         new_state = (new_state < SS_MAX_STATE) ?
 688             new_state : SS_UNDEFINED;
 689 
 690         cmn_err(CE_NOTE, "sess_sm_new_state: sess %p, evt %s(%d), "
 691             "%s(%d) --> %s(%d)\n",
 692             ist, iscsit_se_name[ctx->se_ctx_event], ctx->se_ctx_event,
 693             iscsit_ss_name[ist->ist_state], ist->ist_state,
 694             iscsit_ss_name[new_state], new_state);
 695         DTRACE_PROBE3(sess__state__change,
 696             iscsit_sess_t *, ist, sess_event_ctx_t *, ctx,
 697             iscsit_session_state_t, new_state);
 698 
 699         mutex_enter(&ist->ist_mutex);
 700         ist->ist_last_state = ist->ist_state;
 701         ist->ist_state = new_state;
 702         mutex_exit(&ist->ist_mutex);
 703 
 704         switch (ist->ist_state) {
 705         case SS_Q1_FREE:
 706                 break;
 707         case SS_Q2_ACTIVE:
 708                 iscsit_tgt_bind_sess(ist->ist_tgt, ist);
 709                 break;
 710         case SS_Q3_LOGGED_IN:
 711                 break;
 712         case SS_Q4_FAILED:
 713                 t2r_secs =
 714                     ist->ist_failed_conn->ict_op.op_default_time_2_retain;
 715                 ist->ist_state_timeout = timeout(sess_sm_timeout, ist,
 716                     drv_usectohz(t2r_secs*1000000));
 717                 break;
 718         case SS_Q5_CONTINUE:
 719                 break;
 720         case SS_Q6_DONE:
 721         case SS_Q7_ERROR:
 722                 iscsit_tgt_unbind_sess(ist->ist_tgt, ist);
 723                 if (ist->ist_conn_count == 0) {
 724                         ist->ist_sm_complete = B_TRUE;
 725                 }
 726                 break;
 727         default:
 728                 ASSERT(0);
 729                 /*NOTREACHED*/
 730         }
 731 }