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