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