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