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