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  */
24 
25 #include <sys/cpuvar.h>
26 #include <sys/types.h>
27 #include <sys/conf.h>
28 #include <sys/file.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/modctl.h>
32 #include <sys/sysmacros.h>
33 #include <sys/scsi/generic/persist.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 	mutex_init(&result->ist_sn_mutex, NULL, MUTEX_DEFAULT, 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 	result->ist_rxpdu_queue = kmem_zalloc(sizeof (iscsit_cbuf_t), KM_SLEEP);
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 		mutex_enter(&iscsit_global.global_state_mutex);
183 		if (iscsit_global.global_svc_state == ISE_ENABLED) {
184 			iscsit_global_hold();
185 			mutex_exit(&iscsit_global.global_state_mutex);
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 			mutex_exit(&iscsit_global.global_state_mutex);
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 	stmf_scsi_session_t *iss;
213 
214 	/*
215 	 * State machine has run to completion, destroy session
216 	 *
217 	 * If we have an associated STMF session we should clean it
218 	 * up now.
219 	 *
220 	 * This session is no longer associated with a target at this
221 	 * point so don't touch the target.
222 	 */
223 	mutex_enter(&ist->ist_mutex);
224 	ASSERT(ist->ist_conn_count == 0);
225 	iss = ist->ist_stmf_sess;
226 	if (iss != NULL) {
227 		stmf_deregister_scsi_session(ist->ist_lport, iss);
228 		kmem_free(iss->ss_rport_id, sizeof (scsi_devid_desc_t) +
229 		    strlen(ist->ist_initiator_name) + 1);
230 		stmf_remote_port_free(iss->ss_rport);
231 		stmf_free(iss);
232 	}
233 	mutex_exit(&ist->ist_mutex);
234 
235 	iscsit_sess_destroy(ist);
236 	iscsit_global_rele();
237 }
238 
239 void
240 iscsit_sess_destroy(iscsit_sess_t *ist)
241 {
242 	idm_refcnt_destroy(&ist->ist_refcnt);
243 	if (ist->ist_initiator_name)
244 		kmem_free(ist->ist_initiator_name,
245 		    strlen(ist->ist_initiator_name) + 1);
246 	if (ist->ist_initiator_alias)
247 		kmem_free(ist->ist_initiator_alias,
248 		    strlen(ist->ist_initiator_alias) + 1);
249 	if (ist->ist_target_name)
250 		kmem_free(ist->ist_target_name,
251 		    strlen(ist->ist_target_name) + 1);
252 	if (ist->ist_target_alias)
253 		kmem_free(ist->ist_target_alias,
254 		    strlen(ist->ist_target_alias) + 1);
255 	avl_destroy(&ist->ist_task_list);
256 	kmem_free(ist->ist_rxpdu_queue, sizeof (iscsit_cbuf_t));
257 	list_destroy(&ist->ist_conn_list);
258 	list_destroy(&ist->ist_events);
259 	cv_destroy(&ist->ist_cv);
260 	mutex_destroy(&ist->ist_mutex);
261 	mutex_destroy(&ist->ist_sn_mutex);
262 	kmem_free(ist, sizeof (*ist));
263 }
264 
265 void
266 iscsit_sess_close(iscsit_sess_t *ist)
267 {
268 	iscsit_conn_t *ict;
269 
270 	mutex_enter(&ist->ist_mutex);
271 	/*
272 	 * Note in the session state that we are forcing this session
273 	 * to close so that the session state machine can avoid
274 	 * pointless delays like transitions to SS_Q4_FAILED state.
275 	 */
276 	ist->ist_admin_close = B_TRUE;
277 	if (ist->ist_state == SS_Q3_LOGGED_IN) {
278 		for (ict = list_head(&ist->ist_conn_list);
279 		    ict != NULL;
280 		    ict = list_next(&ist->ist_conn_list, ict)) {
281 			iscsit_send_async_event(ict,
282 			    ISCSI_ASYNC_EVENT_REQUEST_LOGOUT);
283 		}
284 	}
285 	mutex_exit(&ist->ist_mutex);
286 }
287 
288 
289 void
290 iscsit_sess_bind_conn(iscsit_sess_t *ist, iscsit_conn_t *ict)
291 {
292 	iscsit_conn_hold(ict);
293 	iscsit_sess_hold(ist);
294 	ict->ict_sess = ist;
295 	mutex_enter(&ist->ist_mutex);
296 	ist->ist_conn_count++;
297 	list_insert_tail(&ist->ist_conn_list, ict);
298 	mutex_exit(&ist->ist_mutex);
299 }
300 
301 void
302 iscsit_sess_unbind_conn(iscsit_sess_t *ist, iscsit_conn_t *ict)
303 {
304 	mutex_enter(&ist->ist_mutex);
305 	list_remove(&ist->ist_conn_list, ict);
306 	ist->ist_conn_count--;
307 	mutex_exit(&ist->ist_mutex);
308 	iscsit_sess_rele(ist);
309 	iscsit_conn_rele(ict);
310 }
311 
312 void
313 iscsit_sess_hold(iscsit_sess_t *ist)
314 {
315 	idm_refcnt_hold(&ist->ist_refcnt);
316 }
317 
318 void
319 iscsit_sess_rele(iscsit_sess_t *ist)
320 {
321 	idm_refcnt_rele(&ist->ist_refcnt);
322 }
323 
324 idm_status_t
325 iscsit_sess_check_hold(iscsit_sess_t *ist)
326 {
327 	mutex_enter(&ist->ist_mutex);
328 	if (ist->ist_state != SS_Q6_DONE &&
329 	    ist->ist_state != SS_Q7_ERROR) {
330 		idm_refcnt_hold(&ist->ist_refcnt);
331 		mutex_exit(&ist->ist_mutex);
332 		return (IDM_STATUS_SUCCESS);
333 	}
334 	mutex_exit(&ist->ist_mutex);
335 	return (IDM_STATUS_FAIL);
336 }
337 
338 iscsit_conn_t *
339 iscsit_sess_lookup_conn(iscsit_sess_t *ist, uint16_t cid)
340 {
341 	iscsit_conn_t *result;
342 
343 	mutex_enter(&ist->ist_mutex);
344 	for (result = list_head(&ist->ist_conn_list);
345 	    result != NULL;
346 	    result = list_next(&ist->ist_conn_list, result)) {
347 		if (result->ict_cid == cid) {
348 			iscsit_conn_hold(result);
349 			mutex_exit(&ist->ist_mutex);
350 			return (result);
351 		}
352 	}
353 	mutex_exit(&ist->ist_mutex);
354 
355 	return (NULL);
356 }
357 
358 iscsit_sess_t *
359 iscsit_sess_reinstate(iscsit_tgt_t *tgt, iscsit_sess_t *ist, iscsit_conn_t *ict,
360     uint8_t *error_class, uint8_t *error_detail)
361 {
362 	iscsit_sess_t *new_sess;
363 
364 	mutex_enter(&ist->ist_mutex);
365 
366 	/*
367 	 * Session reinstatement replaces a current session with a new session.
368 	 * The new session will have the same ISID as the existing session.
369 	 */
370 	new_sess = iscsit_sess_create(tgt, ict, 0,
371 	    ist->ist_isid, ist->ist_tpgt_tag,
372 	    ist->ist_initiator_name, ist->ist_target_name,
373 	    error_class, error_detail);
374 	ASSERT(new_sess != NULL);
375 
376 	/* Copy additional fields from original session */
377 	new_sess->ist_expcmdsn = ist->ist_expcmdsn;
378 	new_sess->ist_maxcmdsn = ist->ist_expcmdsn + 1;
379 
380 	if (ist->ist_state != SS_Q6_DONE &&
381 	    ist->ist_state != SS_Q7_ERROR) {
382 		/*
383 		 * Generate reinstate event
384 		 */
385 		sess_sm_event_locked(ist, SE_SESSION_REINSTATE, NULL);
386 	}
387 	mutex_exit(&ist->ist_mutex);
388 
389 	return (new_sess);
390 }
391 
392 int
393 iscsit_sess_avl_compare(const void *void_sess1, const void *void_sess2)
394 {
395 	const iscsit_sess_t	*sess1 = void_sess1;
396 	const iscsit_sess_t	*sess2 = void_sess2;
397 	int 			result;
398 
399 	/*
400 	 * Sort by initiator name, then ISID then portal group tag
401 	 */
402 	result = strcmp(sess1->ist_initiator_name, sess2->ist_initiator_name);
403 	if (result < 0) {
404 		return (-1);
405 	} else if (result > 0) {
406 		return (1);
407 	}
408 
409 	/*
410 	 * Initiator names match, compare ISIDs
411 	 */
412 	result = memcmp(sess1->ist_isid, sess2->ist_isid, ISCSI_ISID_LEN);
413 	if (result < 0) {
414 		return (-1);
415 	} else if (result > 0) {
416 		return (1);
417 	}
418 
419 	/*
420 	 * ISIDs match, compare portal group tags
421 	 */
422 	if (sess1->ist_tpgt_tag < sess2->ist_tpgt_tag) {
423 		return (-1);
424 	} else if (sess1->ist_tpgt_tag > sess2->ist_tpgt_tag) {
425 		return (1);
426 	}
427 
428 	/*
429 	 * Portal group tags match, compare TSIHs
430 	 */
431 	if (sess1->ist_tsih < sess2->ist_tsih) {
432 		return (-1);
433 	} else if (sess1->ist_tsih > sess2->ist_tsih) {
434 		return (1);
435 	}
436 
437 	/*
438 	 * Sessions match
439 	 */
440 	return (0);
441 }
442 
443 int
444 iscsit_task_itt_compare(const void *void_task1, const void *void_task2)
445 {
446 	const iscsit_task_t	*task1 = void_task1;
447 	const iscsit_task_t	*task2 = void_task2;
448 
449 	if (task1->it_itt < task2->it_itt)
450 		return (-1);
451 	else if (task1->it_itt > task2->it_itt)
452 		return (1);
453 
454 	return (0);
455 }
456 
457 /*
458  * State machine
459  */
460 
461 void
462 iscsit_sess_sm_event(iscsit_sess_t *ist, iscsit_session_event_t event,
463     iscsit_conn_t *ict)
464 {
465 	mutex_enter(&ist->ist_mutex);
466 	sess_sm_event_locked(ist, event, ict);
467 	mutex_exit(&ist->ist_mutex);
468 }
469 
470 static void
471 sess_sm_event_locked(iscsit_sess_t *ist, iscsit_session_event_t event,
472     iscsit_conn_t *ict)
473 {
474 	sess_event_ctx_t *ctx;
475 
476 	iscsit_sess_hold(ist);
477 
478 	ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP);
479 
480 	ctx->se_ctx_event = event;
481 	ctx->se_event_data = ict;
482 
483 	list_insert_tail(&ist->ist_events, ctx);
484 	/*
485 	 * Use the ist_sm_busy to keep the state machine single threaded.
486 	 * This also serves as recursion avoidance since this flag will
487 	 * always be set if we call login_sm_event from within the
488 	 * state machine code.
489 	 */
490 	if (!ist->ist_sm_busy) {
491 		ist->ist_sm_busy = B_TRUE;
492 		while (!list_is_empty(&ist->ist_events)) {
493 			ctx = list_head(&ist->ist_events);
494 			list_remove(&ist->ist_events, ctx);
495 			idm_sm_audit_event(&ist->ist_state_audit,
496 			    SAS_ISCSIT_SESS, (int)ist->ist_state,
497 			    (int)ctx->se_ctx_event, (uintptr_t)ict);
498 			mutex_exit(&ist->ist_mutex);
499 			sess_sm_event_dispatch(ist, ctx);
500 			mutex_enter(&ist->ist_mutex);
501 		}
502 		ist->ist_sm_busy = B_FALSE;
503 
504 	}
505 
506 	iscsit_sess_rele(ist);
507 }
508 
509 static void
510 sess_sm_event_dispatch(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
511 {
512 	iscsit_conn_t	*ict;
513 
514 	DTRACE_PROBE2(session__event, iscsit_sess_t *, ist,
515 	    sess_event_ctx_t *, ctx);
516 
517 	IDM_SM_LOG(CE_NOTE, "sess_sm_event_dispatch: sess %p event %s(%d)",
518 	    (void *)ist, iscsit_se_name[ctx->se_ctx_event], ctx->se_ctx_event);
519 
520 	/* State independent actions */
521 	switch (ctx->se_ctx_event) {
522 	case SE_CONN_IN_LOGIN:
523 		ict = ctx->se_event_data;
524 		iscsit_sess_bind_conn(ist, ict);
525 		break;
526 	case SE_CONN_FAIL:
527 		ict = ctx->se_event_data;
528 		iscsit_sess_unbind_conn(ist, ict);
529 		break;
530 	}
531 
532 	/* State dependent actions */
533 	switch (ist->ist_state) {
534 	case SS_Q1_FREE:
535 		sess_sm_q1_free(ist, ctx);
536 		break;
537 	case SS_Q2_ACTIVE:
538 		sess_sm_q2_active(ist, ctx);
539 		break;
540 	case SS_Q3_LOGGED_IN:
541 		sess_sm_q3_logged_in(ist, ctx);
542 		break;
543 	case SS_Q4_FAILED:
544 		sess_sm_q4_failed(ist, ctx);
545 		break;
546 	case SS_Q5_CONTINUE:
547 		sess_sm_q5_continue(ist, ctx);
548 		break;
549 	case SS_Q6_DONE:
550 		sess_sm_q6_done(ist, ctx);
551 		break;
552 	case SS_Q7_ERROR:
553 		sess_sm_q7_error(ist, ctx);
554 		break;
555 	default:
556 		ASSERT(0);
557 		break;
558 	}
559 
560 	kmem_free(ctx, sizeof (*ctx));
561 }
562 
563 static void
564 sess_sm_q1_free(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
565 {
566 	switch (ctx->se_ctx_event) {
567 	case SE_CONN_IN_LOGIN:
568 		/* N1 */
569 		sess_sm_new_state(ist, ctx, SS_Q2_ACTIVE);
570 		break;
571 	default:
572 		ASSERT(0);
573 		break;
574 	}
575 }
576 
577 
578 static void
579 sess_sm_q2_active(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
580 {
581 	iscsit_conn_t	*ict;
582 
583 	switch (ctx->se_ctx_event) {
584 	case SE_CONN_LOGGED_IN:
585 		/* N2 track FFP connections */
586 		ist->ist_ffp_conn_count++;
587 		sess_sm_new_state(ist, ctx, SS_Q3_LOGGED_IN);
588 		break;
589 	case SE_CONN_IN_LOGIN:
590 		/* N2.1, don't care stay in this state */
591 		break;
592 	case SE_CONN_FAIL:
593 		/* N9 */
594 		sess_sm_new_state(ist, ctx, SS_Q7_ERROR);
595 		break;
596 	case SE_SESSION_REINSTATE:
597 		/* N11 */
598 		/*
599 		 * Shutdown the iSCSI connections by
600 		 * sending an implicit logout to all
601 		 * the IDM connections and transition
602 		 * the session to SS_Q6_DONE state.
603 		 */
604 		mutex_enter(&ist->ist_mutex);
605 		for (ict = list_head(&ist->ist_conn_list);
606 		    ict != NULL;
607 		    ict = list_next(&ist->ist_conn_list, ict)) {
608 			iscsit_conn_logout(ict);
609 		}
610 		mutex_exit(&ist->ist_mutex);
611 		sess_sm_new_state(ist, ctx, SS_Q6_DONE);
612 		break;
613 	default:
614 		ASSERT(0);
615 		break;
616 	}
617 }
618 
619 static void
620 sess_sm_q3_logged_in(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
621 {
622 	iscsit_conn_t	*ict;
623 
624 	switch (ctx->se_ctx_event) {
625 	case SE_CONN_IN_LOGIN:
626 	case SE_CONN_FAIL:
627 		/* N2.2, don't care */
628 		break;
629 	case SE_CONN_LOGGED_IN:
630 		/* N2.2, track FFP connections */
631 		ist->ist_ffp_conn_count++;
632 		break;
633 	case SE_CONN_FFP_FAIL:
634 	case SE_CONN_FFP_DISABLE:
635 		/*
636 		 * Event data from event context is the associated connection
637 		 * which in this case happens to be the last FFP connection
638 		 * for the session.  In certain cases we need to refer
639 		 * to this last valid connection (i.e. RFC3720 section 12.16)
640 		 * so we'll save off a pointer here for later use.
641 		 */
642 		ASSERT(ist->ist_ffp_conn_count >= 1);
643 		ist->ist_failed_conn = (iscsit_conn_t *)ctx->se_event_data;
644 		ist->ist_ffp_conn_count--;
645 		if (ist->ist_ffp_conn_count == 0) {
646 			/*
647 			 * N5(fail) or N3(disable)
648 			 *
649 			 * If the event is SE_CONN_FFP_FAIL but we are
650 			 * in the midst of an administrative session close
651 			 * because of a service or target offline then
652 			 * there is no need to go to "failed" state.
653 			 */
654 			sess_sm_new_state(ist, ctx,
655 			    ((ctx->se_ctx_event == SE_CONN_FFP_DISABLE) ||
656 			    (ist->ist_admin_close)) ?
657 			    SS_Q6_DONE : SS_Q4_FAILED);
658 		}
659 		break;
660 	case SE_SESSION_CLOSE:
661 	case SE_SESSION_REINSTATE:
662 		/* N3 */
663 		mutex_enter(&ist->ist_mutex);
664 		if (ctx->se_ctx_event == SE_SESSION_CLOSE) {
665 			ASSERT(ist->ist_ffp_conn_count >= 1);
666 			ist->ist_ffp_conn_count--;
667 		}
668 		for (ict = list_head(&ist->ist_conn_list);
669 		    ict != NULL;
670 		    ict = list_next(&ist->ist_conn_list, ict)) {
671 			if ((ctx->se_ctx_event == SE_SESSION_CLOSE) &&
672 			    ((iscsit_conn_t *)ctx->se_event_data == ict)) {
673 				/*
674 				 * Skip this connection since it will
675 				 * see the logout response
676 				 */
677 				continue;
678 			}
679 			iscsit_conn_logout(ict);
680 		}
681 		mutex_exit(&ist->ist_mutex);
682 
683 		sess_sm_new_state(ist, ctx, SS_Q6_DONE);
684 		break;
685 	default:
686 		ASSERT(0);
687 		break;
688 	}
689 }
690 
691 static void
692 sess_sm_timeout(void *arg)
693 {
694 	iscsit_sess_t *ist = arg;
695 
696 	iscsit_sess_sm_event(ist, SE_SESSION_TIMEOUT, NULL);
697 }
698 
699 static void
700 sess_sm_q4_failed(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
701 {
702 	/* Session timer must not be running when we leave this event */
703 	switch (ctx->se_ctx_event) {
704 	case SE_CONN_IN_LOGIN:
705 		/* N7 */
706 		sess_sm_new_state(ist, ctx, SS_Q5_CONTINUE);
707 		break;
708 	case SE_SESSION_REINSTATE:
709 		/* N6 */
710 		(void) untimeout(ist->ist_state_timeout);
711 		/*FALLTHROUGH*/
712 	case SE_SESSION_TIMEOUT:
713 		/* N6 */
714 		sess_sm_new_state(ist, ctx, SS_Q6_DONE);
715 		break;
716 	case SE_CONN_FAIL:
717 		/* Don't care */
718 		break;
719 	default:
720 		ASSERT(0);
721 		break;
722 	}
723 }
724 
725 static void
726 sess_sm_q5_continue(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
727 {
728 	switch (ctx->se_ctx_event) {
729 	case SE_CONN_FAIL:
730 		/* N5 */
731 		sess_sm_new_state(ist, ctx, SS_Q4_FAILED);
732 		break;
733 	case SE_CONN_LOGGED_IN:
734 		/* N10 */
735 		sess_sm_new_state(ist, ctx, SS_Q3_LOGGED_IN);
736 		break;
737 	case SE_SESSION_REINSTATE:
738 		/* N11 */
739 		sess_sm_new_state(ist, ctx, SS_Q6_DONE);
740 		break;
741 	default:
742 		ASSERT(0);
743 		break;
744 	}
745 }
746 
747 static void
748 sess_sm_q6_done(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
749 {
750 	/* Terminal state */
751 	switch (ctx->se_ctx_event) {
752 	case SE_CONN_LOGGED_IN:
753 		/*
754 		 * It's possible to get this event if we encountered
755 		 * an SE_SESSION_REINSTATE_EVENT while we were in
756 		 * SS_Q2_ACTIVE state.  If so we want to update
757 		 * ist->ist_ffp_conn_count because we know an
758 		 * SE_CONN_FFP_FAIL or SE_CONN_FFP_DISABLE is on the
759 		 * way.
760 		 */
761 		ist->ist_ffp_conn_count++;
762 		break;
763 	case SE_CONN_FFP_FAIL:
764 	case SE_CONN_FFP_DISABLE:
765 		ASSERT(ist->ist_ffp_conn_count >= 1);
766 		ist->ist_ffp_conn_count--;
767 		break;
768 	case SE_CONN_FAIL:
769 		if (ist->ist_conn_count == 0) {
770 			idm_refcnt_async_wait_ref(&ist->ist_refcnt,
771 			    &iscsit_sess_unref);
772 		}
773 		break;
774 	default:
775 		break;
776 	}
777 }
778 
779 static void
780 sess_sm_q7_error(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
781 {
782 	/* Terminal state */
783 	switch (ctx->se_ctx_event) {
784 	case SE_CONN_FAIL:
785 		if (ist->ist_conn_count == 0) {
786 			idm_refcnt_async_wait_ref(&ist->ist_refcnt,
787 			    &iscsit_sess_unref);
788 		}
789 		break;
790 	default:
791 		break;
792 	}
793 }
794 
795 static void
796 sess_sm_new_state(iscsit_sess_t *ist, sess_event_ctx_t *ctx,
797     iscsit_session_state_t new_state)
798 {
799 	int t2r_secs;
800 
801 	/*
802 	 * Validate new state
803 	 */
804 	ASSERT(new_state != SS_UNDEFINED);
805 	ASSERT3U(new_state, <, SS_MAX_STATE);
806 
807 	new_state = (new_state < SS_MAX_STATE) ?
808 	    new_state : SS_UNDEFINED;
809 
810 	IDM_SM_LOG(CE_NOTE, "sess_sm_new_state: sess %p, evt %s(%d), "
811 	    "%s(%d) --> %s(%d)\n", (void *) ist,
812 	    iscsit_se_name[ctx->se_ctx_event], ctx->se_ctx_event,
813 	    iscsit_ss_name[ist->ist_state], ist->ist_state,
814 	    iscsit_ss_name[new_state], new_state);
815 
816 	DTRACE_PROBE3(sess__state__change,
817 	    iscsit_sess_t *, ist, sess_event_ctx_t *, ctx,
818 	    iscsit_session_state_t, new_state);
819 
820 	mutex_enter(&ist->ist_mutex);
821 	idm_sm_audit_state_change(&ist->ist_state_audit, SAS_ISCSIT_SESS,
822 	    (int)ist->ist_state, (int)new_state);
823 	ist->ist_last_state = ist->ist_state;
824 	ist->ist_state = new_state;
825 	mutex_exit(&ist->ist_mutex);
826 
827 	switch (ist->ist_state) {
828 	case SS_Q1_FREE:
829 		break;
830 	case SS_Q2_ACTIVE:
831 		iscsit_tgt_bind_sess(ist->ist_tgt, ist);
832 		break;
833 	case SS_Q3_LOGGED_IN:
834 		break;
835 	case SS_Q4_FAILED:
836 		t2r_secs =
837 		    ist->ist_failed_conn->ict_op.op_default_time_2_retain;
838 		ist->ist_state_timeout = timeout(sess_sm_timeout, ist,
839 		    drv_usectohz(t2r_secs*1000000));
840 		break;
841 	case SS_Q5_CONTINUE:
842 		break;
843 	case SS_Q6_DONE:
844 	case SS_Q7_ERROR:
845 		/*
846 		 * We won't need our TSIH anymore and it represents an
847 		 * implicit reference to the global TSIH pool.  Get rid
848 		 * of it.
849 		 */
850 		if (ist->ist_tsih != ISCSI_UNSPEC_TSIH) {
851 			iscsit_tsih_free(ist->ist_tsih);
852 		}
853 
854 		/*
855 		 * We don't want this session to show up anymore so unbind
856 		 * it now.  After this call this session cannot have any
857 		 * references outside itself (implicit or explicit).
858 		 */
859 		iscsit_tgt_unbind_sess(ist->ist_tgt, ist);
860 
861 		/*
862 		 * If we have more connections bound then more events
863 		 * are comming so don't wait for idle yet.
864 		 */
865 		if (ist->ist_conn_count == 0) {
866 			idm_refcnt_async_wait_ref(&ist->ist_refcnt,
867 			    &iscsit_sess_unref);
868 		}
869 		break;
870 	default:
871 		ASSERT(0);
872 		/*NOTREACHED*/
873 	}
874 }
875