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