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