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 }