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