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