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