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 }