1*a6d42e7dSPeter Dunlap /*
2*a6d42e7dSPeter Dunlap  * CDDL HEADER START
3*a6d42e7dSPeter Dunlap  *
4*a6d42e7dSPeter Dunlap  * The contents of this file are subject to the terms of the
5*a6d42e7dSPeter Dunlap  * Common Development and Distribution License (the "License").
6*a6d42e7dSPeter Dunlap  * You may not use this file except in compliance with the License.
7*a6d42e7dSPeter Dunlap  *
8*a6d42e7dSPeter Dunlap  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*a6d42e7dSPeter Dunlap  * or http://www.opensolaris.org/os/licensing.
10*a6d42e7dSPeter Dunlap  * See the License for the specific language governing permissions
11*a6d42e7dSPeter Dunlap  * and limitations under the License.
12*a6d42e7dSPeter Dunlap  *
13*a6d42e7dSPeter Dunlap  * When distributing Covered Code, include this CDDL HEADER in each
14*a6d42e7dSPeter Dunlap  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*a6d42e7dSPeter Dunlap  * If applicable, add the following below this CDDL HEADER, with the
16*a6d42e7dSPeter Dunlap  * fields enclosed by brackets "[]" replaced with your own identifying
17*a6d42e7dSPeter Dunlap  * information: Portions Copyright [yyyy] [name of copyright owner]
18*a6d42e7dSPeter Dunlap  *
19*a6d42e7dSPeter Dunlap  * CDDL HEADER END
20*a6d42e7dSPeter Dunlap  */
21*a6d42e7dSPeter Dunlap 
22*a6d42e7dSPeter Dunlap /*
23*a6d42e7dSPeter Dunlap  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24*a6d42e7dSPeter Dunlap  * Use is subject to license terms.
25*a6d42e7dSPeter Dunlap  */
26*a6d42e7dSPeter Dunlap 
27*a6d42e7dSPeter Dunlap #include <sys/cpuvar.h>
28*a6d42e7dSPeter Dunlap #include <sys/ddi.h>
29*a6d42e7dSPeter Dunlap #include <sys/sunddi.h>
30*a6d42e7dSPeter Dunlap #include <sys/modctl.h>
31*a6d42e7dSPeter Dunlap #include <sys/socket.h>
32*a6d42e7dSPeter Dunlap #include <sys/strsubr.h>
33*a6d42e7dSPeter Dunlap #include <sys/note.h>
34*a6d42e7dSPeter Dunlap #include <sys/sdt.h>
35*a6d42e7dSPeter Dunlap 
36*a6d42e7dSPeter Dunlap #define	IDM_CONN_SM_STRINGS
37*a6d42e7dSPeter Dunlap #include <sys/idm/idm.h>
38*a6d42e7dSPeter Dunlap 
39*a6d42e7dSPeter Dunlap boolean_t	idm_sm_logging = B_FALSE;
40*a6d42e7dSPeter Dunlap 
41*a6d42e7dSPeter Dunlap extern idm_global_t	idm; /* Global state */
42*a6d42e7dSPeter Dunlap 
43*a6d42e7dSPeter Dunlap static void
44*a6d42e7dSPeter Dunlap idm_conn_event_locked(idm_conn_t *ic, idm_conn_event_t event,
45*a6d42e7dSPeter Dunlap     uintptr_t event_info, idm_pdu_event_type_t pdu_event_type);
46*a6d42e7dSPeter Dunlap 
47*a6d42e7dSPeter Dunlap static void
48*a6d42e7dSPeter Dunlap idm_conn_event_handler(void *event_ctx_opaque);
49*a6d42e7dSPeter Dunlap 
50*a6d42e7dSPeter Dunlap static void
51*a6d42e7dSPeter Dunlap idm_state_s1_free(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
52*a6d42e7dSPeter Dunlap 
53*a6d42e7dSPeter Dunlap static void
54*a6d42e7dSPeter Dunlap idm_state_s2_xpt_wait(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
55*a6d42e7dSPeter Dunlap 
56*a6d42e7dSPeter Dunlap static void
57*a6d42e7dSPeter Dunlap idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
58*a6d42e7dSPeter Dunlap 
59*a6d42e7dSPeter Dunlap static void
60*a6d42e7dSPeter Dunlap idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
61*a6d42e7dSPeter Dunlap 
62*a6d42e7dSPeter Dunlap static void
63*a6d42e7dSPeter Dunlap idm_state_s5_logged_in(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
64*a6d42e7dSPeter Dunlap 
65*a6d42e7dSPeter Dunlap static void
66*a6d42e7dSPeter Dunlap idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
67*a6d42e7dSPeter Dunlap 
68*a6d42e7dSPeter Dunlap static void
69*a6d42e7dSPeter Dunlap idm_logout_req_timeout(void *arg);
70*a6d42e7dSPeter Dunlap 
71*a6d42e7dSPeter Dunlap static void
72*a6d42e7dSPeter Dunlap idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
73*a6d42e7dSPeter Dunlap 
74*a6d42e7dSPeter Dunlap static void
75*a6d42e7dSPeter Dunlap idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
76*a6d42e7dSPeter Dunlap 
77*a6d42e7dSPeter Dunlap static void
78*a6d42e7dSPeter Dunlap idm_state_s9_init_error(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
79*a6d42e7dSPeter Dunlap 
80*a6d42e7dSPeter Dunlap static void
81*a6d42e7dSPeter Dunlap idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
82*a6d42e7dSPeter Dunlap 
83*a6d42e7dSPeter Dunlap static void
84*a6d42e7dSPeter Dunlap idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
85*a6d42e7dSPeter Dunlap 
86*a6d42e7dSPeter Dunlap static void
87*a6d42e7dSPeter Dunlap idm_state_s12_enable_dm(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
88*a6d42e7dSPeter Dunlap 
89*a6d42e7dSPeter Dunlap static void
90*a6d42e7dSPeter Dunlap idm_update_state(idm_conn_t *ic, idm_conn_state_t new_state,
91*a6d42e7dSPeter Dunlap     idm_conn_event_ctx_t *event_ctx);
92*a6d42e7dSPeter Dunlap 
93*a6d42e7dSPeter Dunlap static void
94*a6d42e7dSPeter Dunlap idm_conn_unref(void *ic_void);
95*a6d42e7dSPeter Dunlap 
96*a6d42e7dSPeter Dunlap static idm_pdu_event_action_t
97*a6d42e7dSPeter Dunlap idm_conn_sm_validate_pdu(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx,
98*a6d42e7dSPeter Dunlap     idm_pdu_t *pdu);
99*a6d42e7dSPeter Dunlap 
100*a6d42e7dSPeter Dunlap static idm_status_t
101*a6d42e7dSPeter Dunlap idm_ffp_enable(idm_conn_t *ic);
102*a6d42e7dSPeter Dunlap 
103*a6d42e7dSPeter Dunlap static void
104*a6d42e7dSPeter Dunlap idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type);
105*a6d42e7dSPeter Dunlap 
106*a6d42e7dSPeter Dunlap static void
107*a6d42e7dSPeter Dunlap idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
108*a6d42e7dSPeter Dunlap 
109*a6d42e7dSPeter Dunlap static void
110*a6d42e7dSPeter Dunlap idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
111*a6d42e7dSPeter Dunlap 
112*a6d42e7dSPeter Dunlap idm_status_t
113*a6d42e7dSPeter Dunlap idm_conn_sm_init(idm_conn_t *ic)
114*a6d42e7dSPeter Dunlap {
115*a6d42e7dSPeter Dunlap 	char taskq_name[32];
116*a6d42e7dSPeter Dunlap 
117*a6d42e7dSPeter Dunlap 	/*
118*a6d42e7dSPeter Dunlap 	 * Caller should have assigned a unique connection ID.  Use this
119*a6d42e7dSPeter Dunlap 	 * connection ID to create a unique connection name string
120*a6d42e7dSPeter Dunlap 	 */
121*a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_internal_cid != 0);
122*a6d42e7dSPeter Dunlap 	(void) snprintf(taskq_name, sizeof (taskq_name) - 1, "conn_sm%08x",
123*a6d42e7dSPeter Dunlap 	    ic->ic_internal_cid);
124*a6d42e7dSPeter Dunlap 
125*a6d42e7dSPeter Dunlap 	ic->ic_state_taskq = taskq_create(taskq_name, 1, minclsyspri, 2, 2,
126*a6d42e7dSPeter Dunlap 	    TASKQ_PREPOPULATE);
127*a6d42e7dSPeter Dunlap 	if (ic->ic_state_taskq == NULL) {
128*a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
129*a6d42e7dSPeter Dunlap 	}
130*a6d42e7dSPeter Dunlap 
131*a6d42e7dSPeter Dunlap 	idm_sm_audit_init(&ic->ic_state_audit);
132*a6d42e7dSPeter Dunlap 	mutex_init(&ic->ic_state_mutex, NULL, MUTEX_DEFAULT, NULL);
133*a6d42e7dSPeter Dunlap 	cv_init(&ic->ic_state_cv, NULL, CV_DEFAULT, NULL);
134*a6d42e7dSPeter Dunlap 
135*a6d42e7dSPeter Dunlap 	ic->ic_state = CS_S1_FREE;
136*a6d42e7dSPeter Dunlap 	ic->ic_last_state = CS_S1_FREE;
137*a6d42e7dSPeter Dunlap 
138*a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
139*a6d42e7dSPeter Dunlap }
140*a6d42e7dSPeter Dunlap 
141*a6d42e7dSPeter Dunlap void
142*a6d42e7dSPeter Dunlap idm_conn_sm_fini(idm_conn_t *ic)
143*a6d42e7dSPeter Dunlap {
144*a6d42e7dSPeter Dunlap 	taskq_destroy(ic->ic_state_taskq);
145*a6d42e7dSPeter Dunlap 
146*a6d42e7dSPeter Dunlap 	cv_destroy(&ic->ic_state_cv);
147*a6d42e7dSPeter Dunlap 	/*
148*a6d42e7dSPeter Dunlap 	 * The thread that generated the event that got us here may still
149*a6d42e7dSPeter Dunlap 	 * hold the ic_state_mutex. Once it is released we can safely
150*a6d42e7dSPeter Dunlap 	 * destroy it since there is no way to locate the object now.
151*a6d42e7dSPeter Dunlap 	 */
152*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
153*a6d42e7dSPeter Dunlap 	mutex_destroy(&ic->ic_state_mutex);
154*a6d42e7dSPeter Dunlap }
155*a6d42e7dSPeter Dunlap 
156*a6d42e7dSPeter Dunlap void
157*a6d42e7dSPeter Dunlap idm_conn_event(idm_conn_t *ic, idm_conn_event_t event, uintptr_t event_info)
158*a6d42e7dSPeter Dunlap {
159*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
160*a6d42e7dSPeter Dunlap 	idm_conn_event_locked(ic, event, event_info, CT_NONE);
161*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
162*a6d42e7dSPeter Dunlap }
163*a6d42e7dSPeter Dunlap 
164*a6d42e7dSPeter Dunlap idm_status_t
165*a6d42e7dSPeter Dunlap idm_conn_reinstate_event(idm_conn_t *old_ic, idm_conn_t *new_ic)
166*a6d42e7dSPeter Dunlap {
167*a6d42e7dSPeter Dunlap 	int result;
168*a6d42e7dSPeter Dunlap 
169*a6d42e7dSPeter Dunlap 	mutex_enter(&old_ic->ic_state_mutex);
170*a6d42e7dSPeter Dunlap 	if (((old_ic->ic_conn_type == CONN_TYPE_INI) &&
171*a6d42e7dSPeter Dunlap 	    (old_ic->ic_state != CS_S8_CLEANUP)) ||
172*a6d42e7dSPeter Dunlap 	    ((old_ic->ic_conn_type == CONN_TYPE_TGT) &&
173*a6d42e7dSPeter Dunlap 	    (old_ic->ic_state < CS_S5_LOGGED_IN))) {
174*a6d42e7dSPeter Dunlap 		result = IDM_STATUS_FAIL;
175*a6d42e7dSPeter Dunlap 	} else {
176*a6d42e7dSPeter Dunlap 		result = IDM_STATUS_SUCCESS;
177*a6d42e7dSPeter Dunlap 		new_ic->ic_reinstate_conn = old_ic;
178*a6d42e7dSPeter Dunlap 		idm_conn_event_locked(new_ic->ic_reinstate_conn,
179*a6d42e7dSPeter Dunlap 		    CE_CONN_REINSTATE, (uintptr_t)new_ic, CT_NONE);
180*a6d42e7dSPeter Dunlap 	}
181*a6d42e7dSPeter Dunlap 	mutex_exit(&old_ic->ic_state_mutex);
182*a6d42e7dSPeter Dunlap 
183*a6d42e7dSPeter Dunlap 	return (result);
184*a6d42e7dSPeter Dunlap }
185*a6d42e7dSPeter Dunlap 
186*a6d42e7dSPeter Dunlap void
187*a6d42e7dSPeter Dunlap idm_conn_tx_pdu_event(idm_conn_t *ic, idm_conn_event_t event,
188*a6d42e7dSPeter Dunlap     uintptr_t event_info)
189*a6d42e7dSPeter Dunlap {
190*a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&ic->ic_state_mutex));
191*a6d42e7dSPeter Dunlap 	ic->ic_pdu_events++;
192*a6d42e7dSPeter Dunlap 	idm_conn_event_locked(ic, event, event_info, CT_TX_PDU);
193*a6d42e7dSPeter Dunlap }
194*a6d42e7dSPeter Dunlap 
195*a6d42e7dSPeter Dunlap void
196*a6d42e7dSPeter Dunlap idm_conn_rx_pdu_event(idm_conn_t *ic, idm_conn_event_t event,
197*a6d42e7dSPeter Dunlap     uintptr_t event_info)
198*a6d42e7dSPeter Dunlap {
199*a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&ic->ic_state_mutex));
200*a6d42e7dSPeter Dunlap 	ic->ic_pdu_events++;
201*a6d42e7dSPeter Dunlap 	idm_conn_event_locked(ic, event, event_info, CT_RX_PDU);
202*a6d42e7dSPeter Dunlap }
203*a6d42e7dSPeter Dunlap 
204*a6d42e7dSPeter Dunlap static void
205*a6d42e7dSPeter Dunlap idm_conn_event_locked(idm_conn_t *ic, idm_conn_event_t event,
206*a6d42e7dSPeter Dunlap     uintptr_t event_info, idm_pdu_event_type_t pdu_event_type)
207*a6d42e7dSPeter Dunlap {
208*a6d42e7dSPeter Dunlap 	idm_conn_event_ctx_t	*event_ctx;
209*a6d42e7dSPeter Dunlap 
210*a6d42e7dSPeter Dunlap 	idm_sm_audit_event(&ic->ic_state_audit, SAS_IDM_CONN,
211*a6d42e7dSPeter Dunlap 	    (int)ic->ic_state, (int)event, event_info);
212*a6d42e7dSPeter Dunlap 
213*a6d42e7dSPeter Dunlap 	/*
214*a6d42e7dSPeter Dunlap 	 * It's very difficult to prevent a few straggling events
215*a6d42e7dSPeter Dunlap 	 * at the end.  For example idm_sorx_thread will generate
216*a6d42e7dSPeter Dunlap 	 * a CE_TRANSPORT_FAIL event when it exits.  Rather than
217*a6d42e7dSPeter Dunlap 	 * push complicated restrictions all over the code to
218*a6d42e7dSPeter Dunlap 	 * prevent this we will simply drop the events (and in
219*a6d42e7dSPeter Dunlap 	 * the case of PDU events release them appropriately)
220*a6d42e7dSPeter Dunlap 	 * since they are irrelevant once we are in a terminal state.
221*a6d42e7dSPeter Dunlap 	 * Of course those threads need to have appropriate holds on
222*a6d42e7dSPeter Dunlap 	 * the connection otherwise it might disappear.
223*a6d42e7dSPeter Dunlap 	 */
224*a6d42e7dSPeter Dunlap 	if ((ic->ic_state == CS_S9_INIT_ERROR) ||
225*a6d42e7dSPeter Dunlap 	    (ic->ic_state == CS_S11_COMPLETE)) {
226*a6d42e7dSPeter Dunlap 		if ((pdu_event_type == CT_TX_PDU) ||
227*a6d42e7dSPeter Dunlap 		    (pdu_event_type == CT_RX_PDU)) {
228*a6d42e7dSPeter Dunlap 			ic->ic_pdu_events--;
229*a6d42e7dSPeter Dunlap 			idm_pdu_complete((idm_pdu_t *)event_info,
230*a6d42e7dSPeter Dunlap 			    IDM_STATUS_SUCCESS);
231*a6d42e7dSPeter Dunlap 		}
232*a6d42e7dSPeter Dunlap 		IDM_SM_LOG(CE_NOTE, "*** Dropping event %s (%d) because of"
233*a6d42e7dSPeter Dunlap 		    "state %s (%d)",
234*a6d42e7dSPeter Dunlap 		    idm_ce_name[event], event,
235*a6d42e7dSPeter Dunlap 		    idm_cs_name[ic->ic_state], ic->ic_state);
236*a6d42e7dSPeter Dunlap 		return;
237*a6d42e7dSPeter Dunlap 	}
238*a6d42e7dSPeter Dunlap 
239*a6d42e7dSPeter Dunlap 	/*
240*a6d42e7dSPeter Dunlap 	 * Normal event handling
241*a6d42e7dSPeter Dunlap 	 */
242*a6d42e7dSPeter Dunlap 	idm_conn_hold(ic);
243*a6d42e7dSPeter Dunlap 
244*a6d42e7dSPeter Dunlap 	event_ctx = kmem_zalloc(sizeof (*event_ctx), KM_SLEEP);
245*a6d42e7dSPeter Dunlap 	event_ctx->iec_ic = ic;
246*a6d42e7dSPeter Dunlap 	event_ctx->iec_event = event;
247*a6d42e7dSPeter Dunlap 	event_ctx->iec_info = event_info;
248*a6d42e7dSPeter Dunlap 	event_ctx->iec_pdu_event_type = pdu_event_type;
249*a6d42e7dSPeter Dunlap 
250*a6d42e7dSPeter Dunlap 	(void) taskq_dispatch(ic->ic_state_taskq, &idm_conn_event_handler,
251*a6d42e7dSPeter Dunlap 	    event_ctx, TQ_SLEEP);
252*a6d42e7dSPeter Dunlap }
253*a6d42e7dSPeter Dunlap 
254*a6d42e7dSPeter Dunlap static void
255*a6d42e7dSPeter Dunlap idm_conn_event_handler(void *event_ctx_opaque)
256*a6d42e7dSPeter Dunlap {
257*a6d42e7dSPeter Dunlap 	idm_conn_event_ctx_t *event_ctx = event_ctx_opaque;
258*a6d42e7dSPeter Dunlap 	idm_conn_t *ic = event_ctx->iec_ic;
259*a6d42e7dSPeter Dunlap 	idm_pdu_t *pdu = (idm_pdu_t *)event_ctx->iec_info;
260*a6d42e7dSPeter Dunlap 	idm_pdu_event_action_t action;
261*a6d42e7dSPeter Dunlap 
262*a6d42e7dSPeter Dunlap 	IDM_SM_LOG(CE_NOTE, "idm_conn_event_handler: conn %p event %s(%d)",
263*a6d42e7dSPeter Dunlap 	    (void *)ic, idm_ce_name[event_ctx->iec_event],
264*a6d42e7dSPeter Dunlap 	    event_ctx->iec_event);
265*a6d42e7dSPeter Dunlap 	DTRACE_PROBE2(conn__event,
266*a6d42e7dSPeter Dunlap 	    idm_conn_t *, ic, smb_event_ctx_t *, event_ctx);
267*a6d42e7dSPeter Dunlap 
268*a6d42e7dSPeter Dunlap 	/*
269*a6d42e7dSPeter Dunlap 	 * Validate event
270*a6d42e7dSPeter Dunlap 	 */
271*a6d42e7dSPeter Dunlap 	ASSERT(event_ctx->iec_event != CE_UNDEFINED);
272*a6d42e7dSPeter Dunlap 	ASSERT3U(event_ctx->iec_event, <, CE_MAX_EVENT);
273*a6d42e7dSPeter Dunlap 
274*a6d42e7dSPeter Dunlap 	/*
275*a6d42e7dSPeter Dunlap 	 * Validate current state
276*a6d42e7dSPeter Dunlap 	 */
277*a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_state != CS_S0_UNDEFINED);
278*a6d42e7dSPeter Dunlap 	ASSERT3U(ic->ic_state, <, CS_MAX_STATE);
279*a6d42e7dSPeter Dunlap 
280*a6d42e7dSPeter Dunlap 	/*
281*a6d42e7dSPeter Dunlap 	 * Validate PDU-related events against the current state.  If a PDU
282*a6d42e7dSPeter Dunlap 	 * is not allowed in the current state we change the event to a
283*a6d42e7dSPeter Dunlap 	 * protocol error.  This simplifies the state-specific event handlers.
284*a6d42e7dSPeter Dunlap 	 * For example the CS_S2_XPT_WAIT state only needs to handle the
285*a6d42e7dSPeter Dunlap 	 * CE_TX_PROTOCOL_ERROR and CE_RX_PROTOCOL_ERROR events since
286*a6d42e7dSPeter Dunlap 	 * no PDU's can be transmitted or received in that state.
287*a6d42e7dSPeter Dunlap 	 */
288*a6d42e7dSPeter Dunlap 	if (event_ctx->iec_pdu_event_type != CT_NONE) {
289*a6d42e7dSPeter Dunlap 		ASSERT(pdu != NULL);
290*a6d42e7dSPeter Dunlap 		action = idm_conn_sm_validate_pdu(ic, event_ctx, pdu);
291*a6d42e7dSPeter Dunlap 
292*a6d42e7dSPeter Dunlap 		switch (action) {
293*a6d42e7dSPeter Dunlap 		case CA_TX_PROTOCOL_ERROR:
294*a6d42e7dSPeter Dunlap 			/*
295*a6d42e7dSPeter Dunlap 			 * Change event and forward the PDU
296*a6d42e7dSPeter Dunlap 			 */
297*a6d42e7dSPeter Dunlap 			event_ctx->iec_event = CE_TX_PROTOCOL_ERROR;
298*a6d42e7dSPeter Dunlap 			break;
299*a6d42e7dSPeter Dunlap 		case CA_RX_PROTOCOL_ERROR:
300*a6d42e7dSPeter Dunlap 			/*
301*a6d42e7dSPeter Dunlap 			 * Change event and forward the PDU.
302*a6d42e7dSPeter Dunlap 			 */
303*a6d42e7dSPeter Dunlap 			event_ctx->iec_event = CE_RX_PROTOCOL_ERROR;
304*a6d42e7dSPeter Dunlap 			break;
305*a6d42e7dSPeter Dunlap 		case CA_FORWARD:
306*a6d42e7dSPeter Dunlap 			/*
307*a6d42e7dSPeter Dunlap 			 * Let the state-specific event handlers take
308*a6d42e7dSPeter Dunlap 			 * care of it.
309*a6d42e7dSPeter Dunlap 			 */
310*a6d42e7dSPeter Dunlap 			break;
311*a6d42e7dSPeter Dunlap 		case CA_DROP:
312*a6d42e7dSPeter Dunlap 			/*
313*a6d42e7dSPeter Dunlap 			 * It never even happened
314*a6d42e7dSPeter Dunlap 			 */
315*a6d42e7dSPeter Dunlap 			IDM_SM_LOG(CE_NOTE, "*** drop PDU %p", (void *) pdu);
316*a6d42e7dSPeter Dunlap 			idm_pdu_complete(pdu, IDM_STATUS_FAIL);
317*a6d42e7dSPeter Dunlap 			break;
318*a6d42e7dSPeter Dunlap 		default:
319*a6d42e7dSPeter Dunlap 			ASSERT(0);
320*a6d42e7dSPeter Dunlap 			break;
321*a6d42e7dSPeter Dunlap 		}
322*a6d42e7dSPeter Dunlap 	}
323*a6d42e7dSPeter Dunlap 
324*a6d42e7dSPeter Dunlap 	switch (ic->ic_state) {
325*a6d42e7dSPeter Dunlap 	case CS_S1_FREE:
326*a6d42e7dSPeter Dunlap 		idm_state_s1_free(ic, event_ctx);
327*a6d42e7dSPeter Dunlap 		break;
328*a6d42e7dSPeter Dunlap 	case CS_S2_XPT_WAIT:
329*a6d42e7dSPeter Dunlap 		idm_state_s2_xpt_wait(ic, event_ctx);
330*a6d42e7dSPeter Dunlap 		break;
331*a6d42e7dSPeter Dunlap 	case CS_S3_XPT_UP:
332*a6d42e7dSPeter Dunlap 		idm_state_s3_xpt_up(ic, event_ctx);
333*a6d42e7dSPeter Dunlap 		break;
334*a6d42e7dSPeter Dunlap 	case CS_S4_IN_LOGIN:
335*a6d42e7dSPeter Dunlap 		idm_state_s4_in_login(ic, event_ctx);
336*a6d42e7dSPeter Dunlap 		break;
337*a6d42e7dSPeter Dunlap 	case CS_S5_LOGGED_IN:
338*a6d42e7dSPeter Dunlap 		idm_state_s5_logged_in(ic, event_ctx);
339*a6d42e7dSPeter Dunlap 		break;
340*a6d42e7dSPeter Dunlap 	case CS_S6_IN_LOGOUT:
341*a6d42e7dSPeter Dunlap 		idm_state_s6_in_logout(ic, event_ctx);
342*a6d42e7dSPeter Dunlap 		break;
343*a6d42e7dSPeter Dunlap 	case CS_S7_LOGOUT_REQ:
344*a6d42e7dSPeter Dunlap 		idm_state_s7_logout_req(ic, event_ctx);
345*a6d42e7dSPeter Dunlap 		break;
346*a6d42e7dSPeter Dunlap 	case CS_S8_CLEANUP:
347*a6d42e7dSPeter Dunlap 		idm_state_s8_cleanup(ic, event_ctx);
348*a6d42e7dSPeter Dunlap 		break;
349*a6d42e7dSPeter Dunlap 	case CS_S9_INIT_ERROR:
350*a6d42e7dSPeter Dunlap 		idm_state_s9_init_error(ic, event_ctx);
351*a6d42e7dSPeter Dunlap 		break;
352*a6d42e7dSPeter Dunlap 	case CS_S10_IN_CLEANUP:
353*a6d42e7dSPeter Dunlap 		idm_state_s10_in_cleanup(ic, event_ctx);
354*a6d42e7dSPeter Dunlap 		break;
355*a6d42e7dSPeter Dunlap 	case CS_S11_COMPLETE:
356*a6d42e7dSPeter Dunlap 		idm_state_s11_complete(ic, event_ctx);
357*a6d42e7dSPeter Dunlap 		break;
358*a6d42e7dSPeter Dunlap 	case CS_S12_ENABLE_DM:
359*a6d42e7dSPeter Dunlap 		idm_state_s12_enable_dm(ic, event_ctx);
360*a6d42e7dSPeter Dunlap 		break;
361*a6d42e7dSPeter Dunlap 	default:
362*a6d42e7dSPeter Dunlap 		ASSERT(0);
363*a6d42e7dSPeter Dunlap 		break;
364*a6d42e7dSPeter Dunlap 	}
365*a6d42e7dSPeter Dunlap 
366*a6d42e7dSPeter Dunlap 	/*
367*a6d42e7dSPeter Dunlap 	 * Now that we've updated the state machine, if this was
368*a6d42e7dSPeter Dunlap 	 * a PDU-related event take the appropriate action on the PDU
369*a6d42e7dSPeter Dunlap 	 * (transmit it, forward it to the clients RX callback, drop
370*a6d42e7dSPeter Dunlap 	 * it, etc).
371*a6d42e7dSPeter Dunlap 	 */
372*a6d42e7dSPeter Dunlap 	if (event_ctx->iec_pdu_event_type != CT_NONE) {
373*a6d42e7dSPeter Dunlap 		switch (action) {
374*a6d42e7dSPeter Dunlap 		case CA_TX_PROTOCOL_ERROR:
375*a6d42e7dSPeter Dunlap 			idm_pdu_tx_protocol_error(ic, pdu);
376*a6d42e7dSPeter Dunlap 			break;
377*a6d42e7dSPeter Dunlap 		case CA_RX_PROTOCOL_ERROR:
378*a6d42e7dSPeter Dunlap 			idm_pdu_rx_protocol_error(ic, pdu);
379*a6d42e7dSPeter Dunlap 			break;
380*a6d42e7dSPeter Dunlap 		case CA_FORWARD:
381*a6d42e7dSPeter Dunlap 			if (event_ctx->iec_pdu_event_type == CT_RX_PDU) {
382*a6d42e7dSPeter Dunlap 				idm_pdu_rx_forward(ic, pdu);
383*a6d42e7dSPeter Dunlap 			} else {
384*a6d42e7dSPeter Dunlap 				idm_pdu_tx_forward(ic, pdu);
385*a6d42e7dSPeter Dunlap 			}
386*a6d42e7dSPeter Dunlap 			break;
387*a6d42e7dSPeter Dunlap 		default:
388*a6d42e7dSPeter Dunlap 			ASSERT(0);
389*a6d42e7dSPeter Dunlap 			break;
390*a6d42e7dSPeter Dunlap 		}
391*a6d42e7dSPeter Dunlap 	}
392*a6d42e7dSPeter Dunlap 
393*a6d42e7dSPeter Dunlap 	/*
394*a6d42e7dSPeter Dunlap 	 * Update outstanding PDU event count (see idm_pdu_tx for
395*a6d42e7dSPeter Dunlap 	 * how this is used)
396*a6d42e7dSPeter Dunlap 	 */
397*a6d42e7dSPeter Dunlap 	if ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ||
398*a6d42e7dSPeter Dunlap 	    (event_ctx->iec_pdu_event_type == CT_RX_PDU)) {
399*a6d42e7dSPeter Dunlap 		mutex_enter(&ic->ic_state_mutex);
400*a6d42e7dSPeter Dunlap 		ic->ic_pdu_events--;
401*a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
402*a6d42e7dSPeter Dunlap 	}
403*a6d42e7dSPeter Dunlap 
404*a6d42e7dSPeter Dunlap 	idm_conn_rele(ic);
405*a6d42e7dSPeter Dunlap 	kmem_free(event_ctx, sizeof (*event_ctx));
406*a6d42e7dSPeter Dunlap }
407*a6d42e7dSPeter Dunlap 
408*a6d42e7dSPeter Dunlap static void
409*a6d42e7dSPeter Dunlap idm_state_s1_free(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
410*a6d42e7dSPeter Dunlap {
411*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
412*a6d42e7dSPeter Dunlap 	case CE_CONNECT_REQ:
413*a6d42e7dSPeter Dunlap 		/* T1 */
414*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S2_XPT_WAIT, event_ctx);
415*a6d42e7dSPeter Dunlap 		break;
416*a6d42e7dSPeter Dunlap 	case CE_CONNECT_ACCEPT:
417*a6d42e7dSPeter Dunlap 		/* T3 */
418*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S3_XPT_UP, event_ctx);
419*a6d42e7dSPeter Dunlap 		break;
420*a6d42e7dSPeter Dunlap 	case CE_TX_PROTOCOL_ERROR:
421*a6d42e7dSPeter Dunlap 	case CE_RX_PROTOCOL_ERROR:
422*a6d42e7dSPeter Dunlap 		/* This should never happen */
423*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
424*a6d42e7dSPeter Dunlap 		break;
425*a6d42e7dSPeter Dunlap 	default:
426*a6d42e7dSPeter Dunlap 		ASSERT(0);
427*a6d42e7dSPeter Dunlap 		/*NOTREACHED*/
428*a6d42e7dSPeter Dunlap 	}
429*a6d42e7dSPeter Dunlap }
430*a6d42e7dSPeter Dunlap 
431*a6d42e7dSPeter Dunlap 
432*a6d42e7dSPeter Dunlap static void
433*a6d42e7dSPeter Dunlap idm_state_s2_xpt_wait(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
434*a6d42e7dSPeter Dunlap {
435*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
436*a6d42e7dSPeter Dunlap 	case CE_CONNECT_SUCCESS:
437*a6d42e7dSPeter Dunlap 		/* T4 */
438*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S4_IN_LOGIN, event_ctx);
439*a6d42e7dSPeter Dunlap 		break;
440*a6d42e7dSPeter Dunlap 	case CE_CONNECT_FAIL:
441*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_RCV:
442*a6d42e7dSPeter Dunlap 	case CE_TX_PROTOCOL_ERROR:
443*a6d42e7dSPeter Dunlap 	case CE_RX_PROTOCOL_ERROR:
444*a6d42e7dSPeter Dunlap 		/* T2 */
445*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
446*a6d42e7dSPeter Dunlap 		break;
447*a6d42e7dSPeter Dunlap 	default:
448*a6d42e7dSPeter Dunlap 		ASSERT(0);
449*a6d42e7dSPeter Dunlap 		/*NOTREACHED*/
450*a6d42e7dSPeter Dunlap 	}
451*a6d42e7dSPeter Dunlap }
452*a6d42e7dSPeter Dunlap 
453*a6d42e7dSPeter Dunlap 
454*a6d42e7dSPeter Dunlap static void
455*a6d42e7dSPeter Dunlap idm_login_timeout(void *arg)
456*a6d42e7dSPeter Dunlap {
457*a6d42e7dSPeter Dunlap 	idm_conn_t *ic = arg;
458*a6d42e7dSPeter Dunlap 
459*a6d42e7dSPeter Dunlap 	idm_conn_event(ic, CE_LOGIN_TIMEOUT, NULL);
460*a6d42e7dSPeter Dunlap }
461*a6d42e7dSPeter Dunlap 
462*a6d42e7dSPeter Dunlap static void
463*a6d42e7dSPeter Dunlap idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
464*a6d42e7dSPeter Dunlap {
465*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
466*a6d42e7dSPeter Dunlap 	case CE_LOGIN_RCV:
467*a6d42e7dSPeter Dunlap 		/* T4 */
468*a6d42e7dSPeter Dunlap 		idm_initial_login_actions(ic, event_ctx);
469*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S4_IN_LOGIN, event_ctx);
470*a6d42e7dSPeter Dunlap 		break;
471*a6d42e7dSPeter Dunlap 	case CE_LOGIN_TIMEOUT:
472*a6d42e7dSPeter Dunlap 		/*
473*a6d42e7dSPeter Dunlap 		 * Don't need to cancel login timer since the timer is
474*a6d42e7dSPeter Dunlap 		 * presumed to be the source of this event.
475*a6d42e7dSPeter Dunlap 		 */
476*a6d42e7dSPeter Dunlap 		(void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
477*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
478*a6d42e7dSPeter Dunlap 		break;
479*a6d42e7dSPeter Dunlap 	case CE_CONNECT_REJECT:
480*a6d42e7dSPeter Dunlap 	case CE_CONNECT_FAIL:
481*a6d42e7dSPeter Dunlap 	case CE_TRANSPORT_FAIL:
482*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_SND:
483*a6d42e7dSPeter Dunlap 		/* T6 */
484*a6d42e7dSPeter Dunlap 		(void) untimeout(ic->ic_state_timeout);
485*a6d42e7dSPeter Dunlap 		(void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
486*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
487*a6d42e7dSPeter Dunlap 		break;
488*a6d42e7dSPeter Dunlap 	case CE_TX_PROTOCOL_ERROR:
489*a6d42e7dSPeter Dunlap 	case CE_RX_PROTOCOL_ERROR:
490*a6d42e7dSPeter Dunlap 		/* Don't care */
491*a6d42e7dSPeter Dunlap 		break;
492*a6d42e7dSPeter Dunlap 	default:
493*a6d42e7dSPeter Dunlap 		ASSERT(0);
494*a6d42e7dSPeter Dunlap 		/*NOTREACHED*/
495*a6d42e7dSPeter Dunlap 	}
496*a6d42e7dSPeter Dunlap }
497*a6d42e7dSPeter Dunlap 
498*a6d42e7dSPeter Dunlap static void
499*a6d42e7dSPeter Dunlap idm_state_s4_in_login_fail_snd_done(idm_pdu_t *pdu, idm_status_t status)
500*a6d42e7dSPeter Dunlap {
501*a6d42e7dSPeter Dunlap 	idm_conn_t		*ic = pdu->isp_ic;
502*a6d42e7dSPeter Dunlap 
503*a6d42e7dSPeter Dunlap 	/*
504*a6d42e7dSPeter Dunlap 	 * This pdu callback can be invoked by the tx thread,
505*a6d42e7dSPeter Dunlap 	 * so run the disconnect code from another thread.
506*a6d42e7dSPeter Dunlap 	 */
507*a6d42e7dSPeter Dunlap 	pdu->isp_status = status;
508*a6d42e7dSPeter Dunlap 	idm_conn_event(ic, CE_LOGIN_FAIL_SND_DONE, (uintptr_t)pdu);
509*a6d42e7dSPeter Dunlap }
510*a6d42e7dSPeter Dunlap 
511*a6d42e7dSPeter Dunlap static void
512*a6d42e7dSPeter Dunlap idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
513*a6d42e7dSPeter Dunlap {
514*a6d42e7dSPeter Dunlap 	idm_pdu_t *pdu;
515*a6d42e7dSPeter Dunlap 
516*a6d42e7dSPeter Dunlap 	/*
517*a6d42e7dSPeter Dunlap 	 * Login timer should no longer be active after leaving this
518*a6d42e7dSPeter Dunlap 	 * state.
519*a6d42e7dSPeter Dunlap 	 */
520*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
521*a6d42e7dSPeter Dunlap 	case CE_LOGIN_SUCCESS_RCV:
522*a6d42e7dSPeter Dunlap 	case CE_LOGIN_SUCCESS_SND:
523*a6d42e7dSPeter Dunlap 		(void) untimeout(ic->ic_state_timeout);
524*a6d42e7dSPeter Dunlap 		idm_login_success_actions(ic, event_ctx);
525*a6d42e7dSPeter Dunlap 		if (ic->ic_rdma_extensions) {
526*a6d42e7dSPeter Dunlap 			/* T19 */
527*a6d42e7dSPeter Dunlap 			idm_update_state(ic, CS_S12_ENABLE_DM, event_ctx);
528*a6d42e7dSPeter Dunlap 		} else {
529*a6d42e7dSPeter Dunlap 			/* T5 */
530*a6d42e7dSPeter Dunlap 			idm_update_state(ic, CS_S5_LOGGED_IN, event_ctx);
531*a6d42e7dSPeter Dunlap 		}
532*a6d42e7dSPeter Dunlap 		break;
533*a6d42e7dSPeter Dunlap 	case CE_LOGIN_TIMEOUT:
534*a6d42e7dSPeter Dunlap 		/* T7 */
535*a6d42e7dSPeter Dunlap 		(void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
536*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
537*a6d42e7dSPeter Dunlap 		break;
538*a6d42e7dSPeter Dunlap 	case CE_LOGIN_FAIL_SND_DONE:
539*a6d42e7dSPeter Dunlap 		(void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
540*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
541*a6d42e7dSPeter Dunlap 		break;
542*a6d42e7dSPeter Dunlap 	case CE_LOGIN_FAIL_SND:
543*a6d42e7dSPeter Dunlap 		/*
544*a6d42e7dSPeter Dunlap 		 * Allow the logout response pdu to be sent and defer
545*a6d42e7dSPeter Dunlap 		 * the state machine update until the completion callback.
546*a6d42e7dSPeter Dunlap 		 * Only 1 level or callback interposition is allowed.
547*a6d42e7dSPeter Dunlap 		 */
548*a6d42e7dSPeter Dunlap 		(void) untimeout(ic->ic_state_timeout);
549*a6d42e7dSPeter Dunlap 		pdu = (idm_pdu_t *)event_ctx->iec_info;
550*a6d42e7dSPeter Dunlap 		ASSERT(ic->ic_client_callback == NULL);
551*a6d42e7dSPeter Dunlap 		ic->ic_client_callback = pdu->isp_callback;
552*a6d42e7dSPeter Dunlap 		pdu->isp_callback =
553*a6d42e7dSPeter Dunlap 		    idm_state_s4_in_login_fail_snd_done;
554*a6d42e7dSPeter Dunlap 		break;
555*a6d42e7dSPeter Dunlap 	case CE_LOGIN_FAIL_RCV:
556*a6d42e7dSPeter Dunlap 	case CE_TRANSPORT_FAIL:
557*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_SND:
558*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_RCV:
559*a6d42e7dSPeter Dunlap 		/* T7 */
560*a6d42e7dSPeter Dunlap 		(void) untimeout(ic->ic_state_timeout);
561*a6d42e7dSPeter Dunlap 		(void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
562*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
563*a6d42e7dSPeter Dunlap 		break;
564*a6d42e7dSPeter Dunlap 	case CE_LOGIN_SND:
565*a6d42e7dSPeter Dunlap 		/*
566*a6d42e7dSPeter Dunlap 		 * Initiator connections will see initial login PDU
567*a6d42e7dSPeter Dunlap 		 * in this state.  Target connections see initial
568*a6d42e7dSPeter Dunlap 		 * login PDU in "xpt up" state.
569*a6d42e7dSPeter Dunlap 		 */
570*a6d42e7dSPeter Dunlap 		mutex_enter(&ic->ic_state_mutex);
571*a6d42e7dSPeter Dunlap 		if (!(ic->ic_state_flags & CF_INITIAL_LOGIN)) {
572*a6d42e7dSPeter Dunlap 			idm_initial_login_actions(ic, event_ctx);
573*a6d42e7dSPeter Dunlap 		}
574*a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
575*a6d42e7dSPeter Dunlap 		break;
576*a6d42e7dSPeter Dunlap 	case CE_MISC_TX:
577*a6d42e7dSPeter Dunlap 	case CE_MISC_RX:
578*a6d42e7dSPeter Dunlap 	case CE_LOGIN_RCV:
579*a6d42e7dSPeter Dunlap 	case CE_TX_PROTOCOL_ERROR:
580*a6d42e7dSPeter Dunlap 	case CE_RX_PROTOCOL_ERROR:
581*a6d42e7dSPeter Dunlap 		/* Don't care */
582*a6d42e7dSPeter Dunlap 		break;
583*a6d42e7dSPeter Dunlap 	default:
584*a6d42e7dSPeter Dunlap 		ASSERT(0);
585*a6d42e7dSPeter Dunlap 		/*NOTREACHED*/
586*a6d42e7dSPeter Dunlap 	}
587*a6d42e7dSPeter Dunlap }
588*a6d42e7dSPeter Dunlap 
589*a6d42e7dSPeter Dunlap 
590*a6d42e7dSPeter Dunlap static void
591*a6d42e7dSPeter Dunlap idm_state_s5_logged_in(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
592*a6d42e7dSPeter Dunlap {
593*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
594*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_THIS_CONN_RCV:
595*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_THIS_CONN_SND:
596*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_RCV:
597*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_SND:
598*a6d42e7dSPeter Dunlap 		/* T9 */
599*a6d42e7dSPeter Dunlap 		idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */
600*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
601*a6d42e7dSPeter Dunlap 		break;
602*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_RCV:
603*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_SND:
604*a6d42e7dSPeter Dunlap 		/* T9 */
605*a6d42e7dSPeter Dunlap 		idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
606*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
607*a6d42e7dSPeter Dunlap 		break;
608*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_SUCCESS:
609*a6d42e7dSPeter Dunlap 		/* T8 */
610*a6d42e7dSPeter Dunlap 		idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
611*a6d42e7dSPeter Dunlap 
612*a6d42e7dSPeter Dunlap 		/* Close connection */
613*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
614*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
615*a6d42e7dSPeter Dunlap 		} else {
616*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
617*a6d42e7dSPeter Dunlap 		}
618*a6d42e7dSPeter Dunlap 
619*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
620*a6d42e7dSPeter Dunlap 		break;
621*a6d42e7dSPeter Dunlap 	case CE_ASYNC_LOGOUT_RCV:
622*a6d42e7dSPeter Dunlap 	case CE_ASYNC_LOGOUT_SND:
623*a6d42e7dSPeter Dunlap 		/* T11 */
624*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S7_LOGOUT_REQ, event_ctx);
625*a6d42e7dSPeter Dunlap 		break;
626*a6d42e7dSPeter Dunlap 	case CE_TRANSPORT_FAIL:
627*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_CONN_RCV:
628*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_CONN_SND:
629*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_ALL_CONN_RCV:
630*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_ALL_CONN_SND:
631*a6d42e7dSPeter Dunlap 		/* T15 */
632*a6d42e7dSPeter Dunlap 		idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */
633*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
634*a6d42e7dSPeter Dunlap 		break;
635*a6d42e7dSPeter Dunlap 	case CE_MISC_TX:
636*a6d42e7dSPeter Dunlap 	case CE_MISC_RX:
637*a6d42e7dSPeter Dunlap 	case CE_TX_PROTOCOL_ERROR:
638*a6d42e7dSPeter Dunlap 	case CE_RX_PROTOCOL_ERROR:
639*a6d42e7dSPeter Dunlap 		/* Don't care */
640*a6d42e7dSPeter Dunlap 		break;
641*a6d42e7dSPeter Dunlap 	default:
642*a6d42e7dSPeter Dunlap 		ASSERT(0);
643*a6d42e7dSPeter Dunlap 	}
644*a6d42e7dSPeter Dunlap }
645*a6d42e7dSPeter Dunlap 
646*a6d42e7dSPeter Dunlap static void
647*a6d42e7dSPeter Dunlap idm_state_s6_in_logout_success_snd_done(idm_pdu_t *pdu, idm_status_t status)
648*a6d42e7dSPeter Dunlap {
649*a6d42e7dSPeter Dunlap 	idm_conn_t		*ic = pdu->isp_ic;
650*a6d42e7dSPeter Dunlap 
651*a6d42e7dSPeter Dunlap 	/*
652*a6d42e7dSPeter Dunlap 	 * This pdu callback can be invoked by the tx thread,
653*a6d42e7dSPeter Dunlap 	 * so run the disconnect code from another thread.
654*a6d42e7dSPeter Dunlap 	 */
655*a6d42e7dSPeter Dunlap 	pdu->isp_status = status;
656*a6d42e7dSPeter Dunlap 	idm_conn_event(ic, CE_LOGOUT_SUCCESS_SND_DONE, (uintptr_t)pdu);
657*a6d42e7dSPeter Dunlap }
658*a6d42e7dSPeter Dunlap 
659*a6d42e7dSPeter Dunlap static void
660*a6d42e7dSPeter Dunlap idm_state_s6_in_logout_fail_snd_done(idm_pdu_t *pdu, idm_status_t status)
661*a6d42e7dSPeter Dunlap {
662*a6d42e7dSPeter Dunlap 	idm_conn_t		*ic = pdu->isp_ic;
663*a6d42e7dSPeter Dunlap 
664*a6d42e7dSPeter Dunlap 	/*
665*a6d42e7dSPeter Dunlap 	 * This pdu callback can be invoked by the tx thread,
666*a6d42e7dSPeter Dunlap 	 * so run the disconnect code from another thread.
667*a6d42e7dSPeter Dunlap 	 */
668*a6d42e7dSPeter Dunlap 	pdu->isp_status = status;
669*a6d42e7dSPeter Dunlap 	idm_conn_event(ic, CE_LOGOUT_FAIL_SND_DONE, (uintptr_t)pdu);
670*a6d42e7dSPeter Dunlap }
671*a6d42e7dSPeter Dunlap 
672*a6d42e7dSPeter Dunlap static void
673*a6d42e7dSPeter Dunlap idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
674*a6d42e7dSPeter Dunlap {
675*a6d42e7dSPeter Dunlap 	idm_pdu_t *pdu;
676*a6d42e7dSPeter Dunlap 
677*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
678*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_SND_DONE:
679*a6d42e7dSPeter Dunlap 		pdu = (idm_pdu_t *)event_ctx->iec_info;
680*a6d42e7dSPeter Dunlap 
681*a6d42e7dSPeter Dunlap 		/* Close connection (if it's not already closed) */
682*a6d42e7dSPeter Dunlap 		ASSERT(IDM_CONN_ISTGT(ic));
683*a6d42e7dSPeter Dunlap 		ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
684*a6d42e7dSPeter Dunlap 
685*a6d42e7dSPeter Dunlap 		/* restore client callback */
686*a6d42e7dSPeter Dunlap 		pdu->isp_callback =  ic->ic_client_callback;
687*a6d42e7dSPeter Dunlap 		ic->ic_client_callback = NULL;
688*a6d42e7dSPeter Dunlap 		idm_pdu_complete(pdu, pdu->isp_status);
689*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
690*a6d42e7dSPeter Dunlap 		break;
691*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_FAIL_SND_DONE:
692*a6d42e7dSPeter Dunlap 		pdu = (idm_pdu_t *)event_ctx->iec_info;
693*a6d42e7dSPeter Dunlap 		/* restore client callback */
694*a6d42e7dSPeter Dunlap 		pdu->isp_callback =  ic->ic_client_callback;
695*a6d42e7dSPeter Dunlap 		ic->ic_client_callback = NULL;
696*a6d42e7dSPeter Dunlap 		idm_pdu_complete(pdu, pdu->isp_status);
697*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
698*a6d42e7dSPeter Dunlap 		break;
699*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_SND:
700*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_FAIL_SND:
701*a6d42e7dSPeter Dunlap 		/*
702*a6d42e7dSPeter Dunlap 		 * Allow the logout response pdu to be sent and defer
703*a6d42e7dSPeter Dunlap 		 * the state machine update until the completion callback.
704*a6d42e7dSPeter Dunlap 		 * Only 1 level or callback interposition is allowed.
705*a6d42e7dSPeter Dunlap 		 */
706*a6d42e7dSPeter Dunlap 		pdu = (idm_pdu_t *)event_ctx->iec_info;
707*a6d42e7dSPeter Dunlap 		ASSERT(ic->ic_client_callback == NULL);
708*a6d42e7dSPeter Dunlap 		ic->ic_client_callback = pdu->isp_callback;
709*a6d42e7dSPeter Dunlap 		if (event_ctx->iec_event == CE_LOGOUT_SUCCESS_SND) {
710*a6d42e7dSPeter Dunlap 			pdu->isp_callback =
711*a6d42e7dSPeter Dunlap 			    idm_state_s6_in_logout_success_snd_done;
712*a6d42e7dSPeter Dunlap 		} else {
713*a6d42e7dSPeter Dunlap 			pdu->isp_callback =
714*a6d42e7dSPeter Dunlap 			    idm_state_s6_in_logout_fail_snd_done;
715*a6d42e7dSPeter Dunlap 		}
716*a6d42e7dSPeter Dunlap 		break;
717*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_RCV:
718*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_SUCCESS:
719*a6d42e7dSPeter Dunlap 		/* T13 */
720*a6d42e7dSPeter Dunlap 
721*a6d42e7dSPeter Dunlap 		/* Close connection (if it's not already closed) */
722*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
723*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
724*a6d42e7dSPeter Dunlap 		} else {
725*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
726*a6d42e7dSPeter Dunlap 		}
727*a6d42e7dSPeter Dunlap 
728*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
729*a6d42e7dSPeter Dunlap 		break;
730*a6d42e7dSPeter Dunlap 	case CE_ASYNC_LOGOUT_RCV:
731*a6d42e7dSPeter Dunlap 		/* T14 Do nothing */
732*a6d42e7dSPeter Dunlap 		break;
733*a6d42e7dSPeter Dunlap 	case CE_TRANSPORT_FAIL:
734*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_CONN_RCV:
735*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_CONN_SND:
736*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_ALL_CONN_RCV:
737*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_ALL_CONN_SND:
738*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_FAIL_RCV:
739*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
740*a6d42e7dSPeter Dunlap 		break;
741*a6d42e7dSPeter Dunlap 	case CE_TX_PROTOCOL_ERROR:
742*a6d42e7dSPeter Dunlap 	case CE_RX_PROTOCOL_ERROR:
743*a6d42e7dSPeter Dunlap 	case CE_MISC_TX:
744*a6d42e7dSPeter Dunlap 	case CE_MISC_RX:
745*a6d42e7dSPeter Dunlap 		/* Don't care */
746*a6d42e7dSPeter Dunlap 		break;
747*a6d42e7dSPeter Dunlap 	default:
748*a6d42e7dSPeter Dunlap 		ASSERT(0);
749*a6d42e7dSPeter Dunlap 	}
750*a6d42e7dSPeter Dunlap }
751*a6d42e7dSPeter Dunlap 
752*a6d42e7dSPeter Dunlap 
753*a6d42e7dSPeter Dunlap static void
754*a6d42e7dSPeter Dunlap idm_logout_req_timeout(void *arg)
755*a6d42e7dSPeter Dunlap {
756*a6d42e7dSPeter Dunlap 	idm_conn_t *ic = arg;
757*a6d42e7dSPeter Dunlap 
758*a6d42e7dSPeter Dunlap 	idm_conn_event(ic, CE_LOGOUT_TIMEOUT, NULL);
759*a6d42e7dSPeter Dunlap }
760*a6d42e7dSPeter Dunlap 
761*a6d42e7dSPeter Dunlap static void
762*a6d42e7dSPeter Dunlap idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
763*a6d42e7dSPeter Dunlap {
764*a6d42e7dSPeter Dunlap 	/* Must cancel logout timer before leaving this state */
765*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
766*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_THIS_CONN_RCV:
767*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_THIS_CONN_SND:
768*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_RCV:
769*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_SND:
770*a6d42e7dSPeter Dunlap 		/* T10 */
771*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
772*a6d42e7dSPeter Dunlap 			(void) untimeout(ic->ic_state_timeout);
773*a6d42e7dSPeter Dunlap 		}
774*a6d42e7dSPeter Dunlap 		idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */
775*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
776*a6d42e7dSPeter Dunlap 		break;
777*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_RCV:
778*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_SND:
779*a6d42e7dSPeter Dunlap 		/* T10 */
780*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
781*a6d42e7dSPeter Dunlap 			(void) untimeout(ic->ic_state_timeout);
782*a6d42e7dSPeter Dunlap 		}
783*a6d42e7dSPeter Dunlap 		idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
784*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
785*a6d42e7dSPeter Dunlap 		break;
786*a6d42e7dSPeter Dunlap 	case CE_ASYNC_LOGOUT_RCV:
787*a6d42e7dSPeter Dunlap 	case CE_ASYNC_LOGOUT_SND:
788*a6d42e7dSPeter Dunlap 		/* T12 Do nothing */
789*a6d42e7dSPeter Dunlap 		break;
790*a6d42e7dSPeter Dunlap 	case CE_TRANSPORT_FAIL:
791*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_CONN_RCV:
792*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_CONN_SND:
793*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_ALL_CONN_RCV:
794*a6d42e7dSPeter Dunlap 	case CE_ASYNC_DROP_ALL_CONN_SND:
795*a6d42e7dSPeter Dunlap 		/* T16 */
796*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
797*a6d42e7dSPeter Dunlap 			(void) untimeout(ic->ic_state_timeout);
798*a6d42e7dSPeter Dunlap 		}
799*a6d42e7dSPeter Dunlap 		/* FALLTHROUGH */
800*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_TIMEOUT:
801*a6d42e7dSPeter Dunlap 		idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */
802*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
803*a6d42e7dSPeter Dunlap 		break;
804*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_SUCCESS:
805*a6d42e7dSPeter Dunlap 		/* T18 */
806*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
807*a6d42e7dSPeter Dunlap 			(void) untimeout(ic->ic_state_timeout);
808*a6d42e7dSPeter Dunlap 		}
809*a6d42e7dSPeter Dunlap 		idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
810*a6d42e7dSPeter Dunlap 
811*a6d42e7dSPeter Dunlap 		/* Close connection (if it's not already closed) */
812*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
813*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
814*a6d42e7dSPeter Dunlap 		} else {
815*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
816*a6d42e7dSPeter Dunlap 		}
817*a6d42e7dSPeter Dunlap 
818*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
819*a6d42e7dSPeter Dunlap 		break;
820*a6d42e7dSPeter Dunlap 	case CE_TX_PROTOCOL_ERROR:
821*a6d42e7dSPeter Dunlap 	case CE_RX_PROTOCOL_ERROR:
822*a6d42e7dSPeter Dunlap 	case CE_MISC_TX:
823*a6d42e7dSPeter Dunlap 	case CE_MISC_RX:
824*a6d42e7dSPeter Dunlap 		/* Don't care */
825*a6d42e7dSPeter Dunlap 		break;
826*a6d42e7dSPeter Dunlap 	default:
827*a6d42e7dSPeter Dunlap 		ASSERT(0);
828*a6d42e7dSPeter Dunlap 	}
829*a6d42e7dSPeter Dunlap }
830*a6d42e7dSPeter Dunlap 
831*a6d42e7dSPeter Dunlap 
832*a6d42e7dSPeter Dunlap static void
833*a6d42e7dSPeter Dunlap idm_cleanup_timeout(void *arg)
834*a6d42e7dSPeter Dunlap {
835*a6d42e7dSPeter Dunlap 	idm_conn_t *ic = arg;
836*a6d42e7dSPeter Dunlap 
837*a6d42e7dSPeter Dunlap 	idm_conn_event(ic, CE_CLEANUP_TIMEOUT, NULL);
838*a6d42e7dSPeter Dunlap }
839*a6d42e7dSPeter Dunlap 
840*a6d42e7dSPeter Dunlap static void
841*a6d42e7dSPeter Dunlap idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
842*a6d42e7dSPeter Dunlap {
843*a6d42e7dSPeter Dunlap 	idm_pdu_t *pdu;
844*a6d42e7dSPeter Dunlap 
845*a6d42e7dSPeter Dunlap 	/*
846*a6d42e7dSPeter Dunlap 	 * Need to cancel the cleanup timeout before leaving this state
847*a6d42e7dSPeter Dunlap 	 * if it hasn't already fired.
848*a6d42e7dSPeter Dunlap 	 */
849*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
850*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_RCV:
851*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_SND:
852*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_SUCCESS:
853*a6d42e7dSPeter Dunlap 		(void) untimeout(ic->ic_state_timeout);
854*a6d42e7dSPeter Dunlap 		/*FALLTHROUGH*/
855*a6d42e7dSPeter Dunlap 	case CE_CLEANUP_TIMEOUT:
856*a6d42e7dSPeter Dunlap 		/* M1 */
857*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
858*a6d42e7dSPeter Dunlap 		break;
859*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_RCV:
860*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_OTHER_CONN_SND:
861*a6d42e7dSPeter Dunlap 		/* M2 */
862*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S10_IN_CLEANUP, event_ctx);
863*a6d42e7dSPeter Dunlap 		break;
864*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_SND_DONE:
865*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_FAIL_SND_DONE:
866*a6d42e7dSPeter Dunlap 		pdu = (idm_pdu_t *)event_ctx->iec_info;
867*a6d42e7dSPeter Dunlap 		/* restore client callback */
868*a6d42e7dSPeter Dunlap 		pdu->isp_callback =  ic->ic_client_callback;
869*a6d42e7dSPeter Dunlap 		ic->ic_client_callback = NULL;
870*a6d42e7dSPeter Dunlap 		idm_pdu_complete(pdu, pdu->isp_status);
871*a6d42e7dSPeter Dunlap 		break;
872*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_RCV:
873*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_SND:
874*a6d42e7dSPeter Dunlap 	case CE_TX_PROTOCOL_ERROR:
875*a6d42e7dSPeter Dunlap 	case CE_RX_PROTOCOL_ERROR:
876*a6d42e7dSPeter Dunlap 	case CE_MISC_TX:
877*a6d42e7dSPeter Dunlap 	case CE_MISC_RX:
878*a6d42e7dSPeter Dunlap 	case CE_TRANSPORT_FAIL:
879*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_TIMEOUT:
880*a6d42e7dSPeter Dunlap 		/* Don't care */
881*a6d42e7dSPeter Dunlap 		break;
882*a6d42e7dSPeter Dunlap 	default:
883*a6d42e7dSPeter Dunlap 		ASSERT(0);
884*a6d42e7dSPeter Dunlap 	}
885*a6d42e7dSPeter Dunlap }
886*a6d42e7dSPeter Dunlap 
887*a6d42e7dSPeter Dunlap /* ARGSUSED */
888*a6d42e7dSPeter Dunlap static void
889*a6d42e7dSPeter Dunlap idm_state_s9_init_error(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
890*a6d42e7dSPeter Dunlap {
891*a6d42e7dSPeter Dunlap 	if (ic->ic_conn_type == CONN_TYPE_INI) {
892*a6d42e7dSPeter Dunlap 		mutex_enter(&ic->ic_state_mutex);
893*a6d42e7dSPeter Dunlap 		ic->ic_state_flags |= CF_ERROR;
894*a6d42e7dSPeter Dunlap 		ic->ic_conn_sm_status = IDM_STATUS_FAIL;
895*a6d42e7dSPeter Dunlap 		cv_signal(&ic->ic_state_cv);
896*a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
897*a6d42e7dSPeter Dunlap 	}
898*a6d42e7dSPeter Dunlap }
899*a6d42e7dSPeter Dunlap 
900*a6d42e7dSPeter Dunlap static void
901*a6d42e7dSPeter Dunlap idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
902*a6d42e7dSPeter Dunlap {
903*a6d42e7dSPeter Dunlap 	idm_pdu_t *pdu;
904*a6d42e7dSPeter Dunlap 
905*a6d42e7dSPeter Dunlap 	/*
906*a6d42e7dSPeter Dunlap 	 * Need to cancel the cleanup timeout before leaving this state
907*a6d42e7dSPeter Dunlap 	 * if it hasn't already fired.
908*a6d42e7dSPeter Dunlap 	 */
909*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
910*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_FAIL_RCV:
911*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_FAIL_SND:
912*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
913*a6d42e7dSPeter Dunlap 		break;
914*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_SND:
915*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_RCV:
916*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SESSION_SUCCESS:
917*a6d42e7dSPeter Dunlap 		(void) untimeout(ic->ic_state_timeout);
918*a6d42e7dSPeter Dunlap 		/*FALLTHROUGH*/
919*a6d42e7dSPeter Dunlap 	case CE_CLEANUP_TIMEOUT:
920*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
921*a6d42e7dSPeter Dunlap 		break;
922*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_SND_DONE:
923*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_FAIL_SND_DONE:
924*a6d42e7dSPeter Dunlap 		pdu = (idm_pdu_t *)event_ctx->iec_info;
925*a6d42e7dSPeter Dunlap 		/* restore client callback */
926*a6d42e7dSPeter Dunlap 		pdu->isp_callback =  ic->ic_client_callback;
927*a6d42e7dSPeter Dunlap 		ic->ic_client_callback = NULL;
928*a6d42e7dSPeter Dunlap 		idm_pdu_complete(pdu, pdu->isp_status);
929*a6d42e7dSPeter Dunlap 		break;
930*a6d42e7dSPeter Dunlap 	case CE_TX_PROTOCOL_ERROR:
931*a6d42e7dSPeter Dunlap 	case CE_RX_PROTOCOL_ERROR:
932*a6d42e7dSPeter Dunlap 	case CE_MISC_TX:
933*a6d42e7dSPeter Dunlap 	case CE_MISC_RX:
934*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_TIMEOUT:
935*a6d42e7dSPeter Dunlap 		/* Don't care */
936*a6d42e7dSPeter Dunlap 		break;
937*a6d42e7dSPeter Dunlap 	default:
938*a6d42e7dSPeter Dunlap 		ASSERT(0);
939*a6d42e7dSPeter Dunlap 	}
940*a6d42e7dSPeter Dunlap }
941*a6d42e7dSPeter Dunlap 
942*a6d42e7dSPeter Dunlap /* ARGSUSED */
943*a6d42e7dSPeter Dunlap static void
944*a6d42e7dSPeter Dunlap idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
945*a6d42e7dSPeter Dunlap {
946*a6d42e7dSPeter Dunlap 	idm_pdu_t *pdu;
947*a6d42e7dSPeter Dunlap 
948*a6d42e7dSPeter Dunlap 	/*
949*a6d42e7dSPeter Dunlap 	 * Cleanup logout success/fail completion if it's been delayed
950*a6d42e7dSPeter Dunlap 	 * until now.
951*a6d42e7dSPeter Dunlap 	 */
952*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
953*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_SUCCESS_SND_DONE:
954*a6d42e7dSPeter Dunlap 	case CE_LOGOUT_FAIL_SND_DONE:
955*a6d42e7dSPeter Dunlap 		pdu = (idm_pdu_t *)event_ctx->iec_info;
956*a6d42e7dSPeter Dunlap 		/* restore client callback */
957*a6d42e7dSPeter Dunlap 		pdu->isp_callback =  ic->ic_client_callback;
958*a6d42e7dSPeter Dunlap 		ic->ic_client_callback = NULL;
959*a6d42e7dSPeter Dunlap 		idm_pdu_complete(pdu, pdu->isp_status);
960*a6d42e7dSPeter Dunlap 		break;
961*a6d42e7dSPeter Dunlap 	}
962*a6d42e7dSPeter Dunlap }
963*a6d42e7dSPeter Dunlap 
964*a6d42e7dSPeter Dunlap static void
965*a6d42e7dSPeter Dunlap idm_state_s12_enable_dm(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
966*a6d42e7dSPeter Dunlap {
967*a6d42e7dSPeter Dunlap 	switch (event_ctx->iec_event) {
968*a6d42e7dSPeter Dunlap 	case CE_ENABLE_DM_SUCCESS:
969*a6d42e7dSPeter Dunlap 		/* T20 */
970*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S5_LOGGED_IN, event_ctx);
971*a6d42e7dSPeter Dunlap 		break;
972*a6d42e7dSPeter Dunlap 	case CE_ENABLE_DM_FAIL:
973*a6d42e7dSPeter Dunlap 		/* T21 */
974*a6d42e7dSPeter Dunlap 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
975*a6d42e7dSPeter Dunlap 		break;
976*a6d42e7dSPeter Dunlap 	case CE_TRANSPORT_FAIL:
977*a6d42e7dSPeter Dunlap 		/*
978*a6d42e7dSPeter Dunlap 		 * We expect to always hear back from the transport layer
979*a6d42e7dSPeter Dunlap 		 * once we have an "enable data-mover" request outstanding.
980*a6d42e7dSPeter Dunlap 		 * Therefore we'll ignore other events that may occur even
981*a6d42e7dSPeter Dunlap 		 * when they clearly indicate a problem and wait for
982*a6d42e7dSPeter Dunlap 		 * CE_ENABLE_DM_FAIL.  On a related note this means the
983*a6d42e7dSPeter Dunlap 		 * transport must ensure that it eventually completes the
984*a6d42e7dSPeter Dunlap 		 * "enable data-mover" operation with either success or
985*a6d42e7dSPeter Dunlap 		 * failure -- otherwise we'll be stuck here.
986*a6d42e7dSPeter Dunlap 		 */
987*a6d42e7dSPeter Dunlap 		break;
988*a6d42e7dSPeter Dunlap 	default:
989*a6d42e7dSPeter Dunlap 		ASSERT(0);
990*a6d42e7dSPeter Dunlap 		break;
991*a6d42e7dSPeter Dunlap 	}
992*a6d42e7dSPeter Dunlap }
993*a6d42e7dSPeter Dunlap 
994*a6d42e7dSPeter Dunlap static void
995*a6d42e7dSPeter Dunlap idm_update_state(idm_conn_t *ic, idm_conn_state_t new_state,
996*a6d42e7dSPeter Dunlap     idm_conn_event_ctx_t *event_ctx)
997*a6d42e7dSPeter Dunlap {
998*a6d42e7dSPeter Dunlap 	int rc;
999*a6d42e7dSPeter Dunlap 	idm_status_t idm_status;
1000*a6d42e7dSPeter Dunlap 
1001*a6d42e7dSPeter Dunlap 	/*
1002*a6d42e7dSPeter Dunlap 	 * Validate new state
1003*a6d42e7dSPeter Dunlap 	 */
1004*a6d42e7dSPeter Dunlap 	ASSERT(new_state != CS_S0_UNDEFINED);
1005*a6d42e7dSPeter Dunlap 	ASSERT3U(new_state, <, CS_MAX_STATE);
1006*a6d42e7dSPeter Dunlap 
1007*a6d42e7dSPeter Dunlap 	/*
1008*a6d42e7dSPeter Dunlap 	 * Update state in context.  We protect this with a mutex
1009*a6d42e7dSPeter Dunlap 	 * even though the state machine code is single threaded so that
1010*a6d42e7dSPeter Dunlap 	 * other threads can check the state value atomically.
1011*a6d42e7dSPeter Dunlap 	 */
1012*a6d42e7dSPeter Dunlap 	new_state = (new_state < CS_MAX_STATE) ?
1013*a6d42e7dSPeter Dunlap 	    new_state : CS_S0_UNDEFINED;
1014*a6d42e7dSPeter Dunlap 
1015*a6d42e7dSPeter Dunlap 	IDM_SM_LOG(CE_NOTE, "idm_update_state: conn %p, evt %s(%d), "
1016*a6d42e7dSPeter Dunlap 	    "%s(%d) --> %s(%d)", (void *)ic,
1017*a6d42e7dSPeter Dunlap 	    idm_ce_name[event_ctx->iec_event], event_ctx->iec_event,
1018*a6d42e7dSPeter Dunlap 	    idm_cs_name[ic->ic_state], ic->ic_state,
1019*a6d42e7dSPeter Dunlap 	    idm_cs_name[new_state], new_state);
1020*a6d42e7dSPeter Dunlap 
1021*a6d42e7dSPeter Dunlap 	DTRACE_PROBE2(conn__state__change,
1022*a6d42e7dSPeter Dunlap 	    idm_conn_t *, ic, idm_conn_state_t, new_state);
1023*a6d42e7dSPeter Dunlap 
1024*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
1025*a6d42e7dSPeter Dunlap 	idm_sm_audit_state_change(&ic->ic_state_audit, SAS_IDM_CONN,
1026*a6d42e7dSPeter Dunlap 	    (int)ic->ic_state, (int)new_state);
1027*a6d42e7dSPeter Dunlap 	ic->ic_last_state = ic->ic_state;
1028*a6d42e7dSPeter Dunlap 	ic->ic_state = new_state;
1029*a6d42e7dSPeter Dunlap 	cv_signal(&ic->ic_state_cv);
1030*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
1031*a6d42e7dSPeter Dunlap 
1032*a6d42e7dSPeter Dunlap 	switch (ic->ic_state) {
1033*a6d42e7dSPeter Dunlap 	case CS_S1_FREE:
1034*a6d42e7dSPeter Dunlap 		ASSERT(0); /* Initial state, can't return */
1035*a6d42e7dSPeter Dunlap 		break;
1036*a6d42e7dSPeter Dunlap 	case CS_S2_XPT_WAIT:
1037*a6d42e7dSPeter Dunlap 		if ((rc = idm_ini_conn_finish(ic)) != 0) {
1038*a6d42e7dSPeter Dunlap 			idm_conn_event(ic, CE_CONNECT_FAIL, NULL);
1039*a6d42e7dSPeter Dunlap 		} else {
1040*a6d42e7dSPeter Dunlap 			idm_conn_event(ic, CE_CONNECT_SUCCESS, NULL);
1041*a6d42e7dSPeter Dunlap 		}
1042*a6d42e7dSPeter Dunlap 		break;
1043*a6d42e7dSPeter Dunlap 	case CS_S3_XPT_UP:
1044*a6d42e7dSPeter Dunlap 		/*
1045*a6d42e7dSPeter Dunlap 		 * Finish any connection related setup including
1046*a6d42e7dSPeter Dunlap 		 * waking up the idm_tgt_conn_accept thread.
1047*a6d42e7dSPeter Dunlap 		 * and starting the login timer.  If the function
1048*a6d42e7dSPeter Dunlap 		 * fails then we return to "free" state.
1049*a6d42e7dSPeter Dunlap 		 */
1050*a6d42e7dSPeter Dunlap 		if ((rc = idm_tgt_conn_finish(ic)) != IDM_STATUS_SUCCESS) {
1051*a6d42e7dSPeter Dunlap 			switch (rc) {
1052*a6d42e7dSPeter Dunlap 			case IDM_STATUS_REJECT:
1053*a6d42e7dSPeter Dunlap 				idm_conn_event(ic, CE_CONNECT_REJECT, NULL);
1054*a6d42e7dSPeter Dunlap 				break;
1055*a6d42e7dSPeter Dunlap 			default:
1056*a6d42e7dSPeter Dunlap 				idm_conn_event(ic, CE_CONNECT_FAIL, NULL);
1057*a6d42e7dSPeter Dunlap 				break;
1058*a6d42e7dSPeter Dunlap 			}
1059*a6d42e7dSPeter Dunlap 		}
1060*a6d42e7dSPeter Dunlap 
1061*a6d42e7dSPeter Dunlap 		/*
1062*a6d42e7dSPeter Dunlap 		 * First login received will cause a transition to
1063*a6d42e7dSPeter Dunlap 		 * CS_S4_IN_LOGIN.  Start login timer.
1064*a6d42e7dSPeter Dunlap 		 */
1065*a6d42e7dSPeter Dunlap 		ic->ic_state_timeout = timeout(idm_login_timeout, ic,
1066*a6d42e7dSPeter Dunlap 		    drv_usectohz(IDM_LOGIN_SECONDS*1000000));
1067*a6d42e7dSPeter Dunlap 		break;
1068*a6d42e7dSPeter Dunlap 	case CS_S4_IN_LOGIN:
1069*a6d42e7dSPeter Dunlap 		if (ic->ic_conn_type == CONN_TYPE_INI) {
1070*a6d42e7dSPeter Dunlap 			mutex_enter(&ic->ic_state_mutex);
1071*a6d42e7dSPeter Dunlap 			ic->ic_state_flags |= CF_LOGIN_READY;
1072*a6d42e7dSPeter Dunlap 			cv_signal(&ic->ic_state_cv);
1073*a6d42e7dSPeter Dunlap 			mutex_exit(&ic->ic_state_mutex);
1074*a6d42e7dSPeter Dunlap 		}
1075*a6d42e7dSPeter Dunlap 		break;
1076*a6d42e7dSPeter Dunlap 	case CS_S5_LOGGED_IN:
1077*a6d42e7dSPeter Dunlap 		ASSERT(!ic->ic_ffp);
1078*a6d42e7dSPeter Dunlap 		/*
1079*a6d42e7dSPeter Dunlap 		 * IDM can go to FFP before the initiator but it
1080*a6d42e7dSPeter Dunlap 		 * needs to go to FFP after the target (IDM target should
1081*a6d42e7dSPeter Dunlap 		 * go to FFP after notify_ack).
1082*a6d42e7dSPeter Dunlap 		 */
1083*a6d42e7dSPeter Dunlap 		idm_status = idm_ffp_enable(ic);
1084*a6d42e7dSPeter Dunlap 		if (idm_status != IDM_STATUS_SUCCESS) {
1085*a6d42e7dSPeter Dunlap 			idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
1086*a6d42e7dSPeter Dunlap 		}
1087*a6d42e7dSPeter Dunlap 
1088*a6d42e7dSPeter Dunlap 		if (ic->ic_reinstate_conn) {
1089*a6d42e7dSPeter Dunlap 			/* Connection reinstatement is complete */
1090*a6d42e7dSPeter Dunlap 			idm_conn_event_locked(ic->ic_reinstate_conn,
1091*a6d42e7dSPeter Dunlap 			    CE_CONN_REINSTATE_SUCCESS, NULL, CT_NONE);
1092*a6d42e7dSPeter Dunlap 		}
1093*a6d42e7dSPeter Dunlap 		break;
1094*a6d42e7dSPeter Dunlap 	case CS_S6_IN_LOGOUT:
1095*a6d42e7dSPeter Dunlap 		break;
1096*a6d42e7dSPeter Dunlap 	case CS_S7_LOGOUT_REQ:
1097*a6d42e7dSPeter Dunlap 		/* Start logout timer for target connections */
1098*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
1099*a6d42e7dSPeter Dunlap 			ic->ic_state_timeout = timeout(idm_logout_req_timeout,
1100*a6d42e7dSPeter Dunlap 			    ic, drv_usectohz(IDM_LOGOUT_SECONDS*1000000));
1101*a6d42e7dSPeter Dunlap 		}
1102*a6d42e7dSPeter Dunlap 		break;
1103*a6d42e7dSPeter Dunlap 	case CS_S8_CLEANUP:
1104*a6d42e7dSPeter Dunlap 		/* Close connection (if it's not already closed) */
1105*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
1106*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1107*a6d42e7dSPeter Dunlap 		} else {
1108*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
1109*a6d42e7dSPeter Dunlap 		}
1110*a6d42e7dSPeter Dunlap 
1111*a6d42e7dSPeter Dunlap 		/* Stop executing active tasks */
1112*a6d42e7dSPeter Dunlap 		idm_task_abort(ic, NULL, AT_INTERNAL_SUSPEND);
1113*a6d42e7dSPeter Dunlap 
1114*a6d42e7dSPeter Dunlap 		/* Start logout timer */
1115*a6d42e7dSPeter Dunlap 		ic->ic_state_timeout = timeout(idm_cleanup_timeout, ic,
1116*a6d42e7dSPeter Dunlap 		    drv_usectohz(IDM_CLEANUP_SECONDS*1000000));
1117*a6d42e7dSPeter Dunlap 		break;
1118*a6d42e7dSPeter Dunlap 	case CS_S10_IN_CLEANUP:
1119*a6d42e7dSPeter Dunlap 		break;
1120*a6d42e7dSPeter Dunlap 	case CS_S9_INIT_ERROR:
1121*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic)) {
1122*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1123*a6d42e7dSPeter Dunlap 		} else {
1124*a6d42e7dSPeter Dunlap 			mutex_enter(&ic->ic_state_mutex);
1125*a6d42e7dSPeter Dunlap 			ic->ic_state_flags |= CF_ERROR;
1126*a6d42e7dSPeter Dunlap 			ic->ic_conn_sm_status = IDM_STATUS_FAIL;
1127*a6d42e7dSPeter Dunlap 			cv_signal(&ic->ic_state_cv);
1128*a6d42e7dSPeter Dunlap 			mutex_exit(&ic->ic_state_mutex);
1129*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
1130*a6d42e7dSPeter Dunlap 		}
1131*a6d42e7dSPeter Dunlap 		/*FALLTHROUGH*/
1132*a6d42e7dSPeter Dunlap 	case CS_S11_COMPLETE:
1133*a6d42e7dSPeter Dunlap 		/* No more traffic on this connection */
1134*a6d42e7dSPeter Dunlap 		(void) idm_notify_client(ic, CN_CONNECT_LOST, NULL);
1135*a6d42e7dSPeter Dunlap 
1136*a6d42e7dSPeter Dunlap 		/* Abort all tasks */
1137*a6d42e7dSPeter Dunlap 		idm_task_abort(ic, NULL, AT_INTERNAL_ABORT);
1138*a6d42e7dSPeter Dunlap 
1139*a6d42e7dSPeter Dunlap 		/*
1140*a6d42e7dSPeter Dunlap 		 * Handle terminal state actions on the global taskq so
1141*a6d42e7dSPeter Dunlap 		 * we can clean up all the connection resources from
1142*a6d42e7dSPeter Dunlap 		 * a separate thread context.
1143*a6d42e7dSPeter Dunlap 		 */
1144*a6d42e7dSPeter Dunlap 		idm_refcnt_async_wait_ref(&ic->ic_refcnt, &idm_conn_unref);
1145*a6d42e7dSPeter Dunlap 		break;
1146*a6d42e7dSPeter Dunlap 	case CS_S12_ENABLE_DM:
1147*a6d42e7dSPeter Dunlap 
1148*a6d42e7dSPeter Dunlap 		/*
1149*a6d42e7dSPeter Dunlap 		 * The Enable DM state indicates the initiator to initiate
1150*a6d42e7dSPeter Dunlap 		 * the hello sequence and the target to get ready to accept
1151*a6d42e7dSPeter Dunlap 		 * the iSER Hello Message.
1152*a6d42e7dSPeter Dunlap 		 */
1153*a6d42e7dSPeter Dunlap 		idm_status = (IDM_CONN_ISINI(ic)) ?
1154*a6d42e7dSPeter Dunlap 		    ic->ic_transport_ops->it_ini_enable_datamover(ic) :
1155*a6d42e7dSPeter Dunlap 		    ic->ic_transport_ops->it_tgt_enable_datamover(ic);
1156*a6d42e7dSPeter Dunlap 
1157*a6d42e7dSPeter Dunlap 		if (idm_status == IDM_STATUS_SUCCESS) {
1158*a6d42e7dSPeter Dunlap 			idm_conn_event(ic, CE_ENABLE_DM_SUCCESS, NULL);
1159*a6d42e7dSPeter Dunlap 		} else {
1160*a6d42e7dSPeter Dunlap 			idm_conn_event(ic, CE_ENABLE_DM_FAIL, NULL);
1161*a6d42e7dSPeter Dunlap 		}
1162*a6d42e7dSPeter Dunlap 
1163*a6d42e7dSPeter Dunlap 		break;
1164*a6d42e7dSPeter Dunlap 	}
1165*a6d42e7dSPeter Dunlap }
1166*a6d42e7dSPeter Dunlap 
1167*a6d42e7dSPeter Dunlap 
1168*a6d42e7dSPeter Dunlap static void
1169*a6d42e7dSPeter Dunlap idm_conn_unref(void *ic_void)
1170*a6d42e7dSPeter Dunlap {
1171*a6d42e7dSPeter Dunlap 	idm_conn_t *ic = ic_void;
1172*a6d42e7dSPeter Dunlap 
1173*a6d42e7dSPeter Dunlap 	/*
1174*a6d42e7dSPeter Dunlap 	 * Client should not be notified that the connection is destroyed
1175*a6d42e7dSPeter Dunlap 	 * until all references on the idm connection have been removed.
1176*a6d42e7dSPeter Dunlap 	 * Otherwise references on the associated client context would need
1177*a6d42e7dSPeter Dunlap 	 * to be tracked separately which seems like a waste (at least when
1178*a6d42e7dSPeter Dunlap 	 * there is a one for one correspondence with references on the
1179*a6d42e7dSPeter Dunlap 	 * IDM connection).
1180*a6d42e7dSPeter Dunlap 	 */
1181*a6d42e7dSPeter Dunlap 	if (IDM_CONN_ISTGT(ic)) {
1182*a6d42e7dSPeter Dunlap 		(void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL);
1183*a6d42e7dSPeter Dunlap 		idm_svc_conn_destroy(ic);
1184*a6d42e7dSPeter Dunlap 	} else {
1185*a6d42e7dSPeter Dunlap 		/* Initiator may destroy connection during this call */
1186*a6d42e7dSPeter Dunlap 		(void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL);
1187*a6d42e7dSPeter Dunlap 	}
1188*a6d42e7dSPeter Dunlap }
1189*a6d42e7dSPeter Dunlap 
1190*a6d42e7dSPeter Dunlap 
1191*a6d42e7dSPeter Dunlap static idm_pdu_event_action_t
1192*a6d42e7dSPeter Dunlap idm_conn_sm_validate_pdu(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx,
1193*a6d42e7dSPeter Dunlap 	idm_pdu_t *pdu)
1194*a6d42e7dSPeter Dunlap {
1195*a6d42e7dSPeter Dunlap 	char			*reason_string;
1196*a6d42e7dSPeter Dunlap 	idm_pdu_event_action_t	action;
1197*a6d42e7dSPeter Dunlap 
1198*a6d42e7dSPeter Dunlap 	ASSERT((event_ctx->iec_pdu_event_type == CT_RX_PDU) ||
1199*a6d42e7dSPeter Dunlap 	    (event_ctx->iec_pdu_event_type == CT_TX_PDU));
1200*a6d42e7dSPeter Dunlap 
1201*a6d42e7dSPeter Dunlap 	/*
1202*a6d42e7dSPeter Dunlap 	 * Let's check the simple stuff first.  Make sure if this is a
1203*a6d42e7dSPeter Dunlap 	 * target connection that the PDU is appropriate for a target
1204*a6d42e7dSPeter Dunlap 	 * and if this is an initiator connection that the PDU is
1205*a6d42e7dSPeter Dunlap 	 * appropriate for an initiator.  This code is not in the data
1206*a6d42e7dSPeter Dunlap 	 * path so organization is more important than performance.
1207*a6d42e7dSPeter Dunlap 	 */
1208*a6d42e7dSPeter Dunlap 	switch (IDM_PDU_OPCODE(pdu)) {
1209*a6d42e7dSPeter Dunlap 	case ISCSI_OP_NOOP_OUT:
1210*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_CMD:
1211*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
1212*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGIN_CMD:
1213*a6d42e7dSPeter Dunlap 	case ISCSI_OP_TEXT_CMD:
1214*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_DATA:
1215*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGOUT_CMD:
1216*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SNACK_CMD:
1217*a6d42e7dSPeter Dunlap 		/*
1218*a6d42e7dSPeter Dunlap 		 * Only the initiator should send these PDU's and
1219*a6d42e7dSPeter Dunlap 		 * only the target should receive them.
1220*a6d42e7dSPeter Dunlap 		 */
1221*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISINI(ic) &&
1222*a6d42e7dSPeter Dunlap 		    (event_ctx->iec_pdu_event_type == CT_RX_PDU)) {
1223*a6d42e7dSPeter Dunlap 			reason_string = "Invalid RX PDU for initiator";
1224*a6d42e7dSPeter Dunlap 			action = CA_RX_PROTOCOL_ERROR;
1225*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1226*a6d42e7dSPeter Dunlap 		}
1227*a6d42e7dSPeter Dunlap 
1228*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic) &&
1229*a6d42e7dSPeter Dunlap 		    (event_ctx->iec_pdu_event_type == CT_TX_PDU)) {
1230*a6d42e7dSPeter Dunlap 			reason_string = "Invalid TX PDU for target";
1231*a6d42e7dSPeter Dunlap 			action = CA_TX_PROTOCOL_ERROR;
1232*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1233*a6d42e7dSPeter Dunlap 		}
1234*a6d42e7dSPeter Dunlap 		break;
1235*a6d42e7dSPeter Dunlap 	case ISCSI_OP_NOOP_IN:
1236*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_RSP:
1237*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
1238*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGIN_RSP:
1239*a6d42e7dSPeter Dunlap 	case ISCSI_OP_TEXT_RSP:
1240*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_DATA_RSP:
1241*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGOUT_RSP:
1242*a6d42e7dSPeter Dunlap 	case ISCSI_OP_RTT_RSP:
1243*a6d42e7dSPeter Dunlap 	case ISCSI_OP_ASYNC_EVENT:
1244*a6d42e7dSPeter Dunlap 	case ISCSI_OP_REJECT_MSG:
1245*a6d42e7dSPeter Dunlap 		/*
1246*a6d42e7dSPeter Dunlap 		 * Only the target should send these PDU's and
1247*a6d42e7dSPeter Dunlap 		 * only the initiator should receive them.
1248*a6d42e7dSPeter Dunlap 		 */
1249*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISTGT(ic) &&
1250*a6d42e7dSPeter Dunlap 		    (event_ctx->iec_pdu_event_type == CT_RX_PDU)) {
1251*a6d42e7dSPeter Dunlap 			reason_string = "Invalid RX PDU for target";
1252*a6d42e7dSPeter Dunlap 			action = CA_RX_PROTOCOL_ERROR;
1253*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1254*a6d42e7dSPeter Dunlap 		}
1255*a6d42e7dSPeter Dunlap 
1256*a6d42e7dSPeter Dunlap 		if (IDM_CONN_ISINI(ic) &&
1257*a6d42e7dSPeter Dunlap 		    (event_ctx->iec_pdu_event_type == CT_TX_PDU)) {
1258*a6d42e7dSPeter Dunlap 			reason_string = "Invalid TX PDU for initiator";
1259*a6d42e7dSPeter Dunlap 			action = CA_TX_PROTOCOL_ERROR;
1260*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1261*a6d42e7dSPeter Dunlap 		}
1262*a6d42e7dSPeter Dunlap 		break;
1263*a6d42e7dSPeter Dunlap 	default:
1264*a6d42e7dSPeter Dunlap 		reason_string = "Unknown PDU Type";
1265*a6d42e7dSPeter Dunlap 		action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1266*a6d42e7dSPeter Dunlap 		    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1267*a6d42e7dSPeter Dunlap 		goto validate_pdu_done;
1268*a6d42e7dSPeter Dunlap 	}
1269*a6d42e7dSPeter Dunlap 
1270*a6d42e7dSPeter Dunlap 	/*
1271*a6d42e7dSPeter Dunlap 	 * Now validate the opcodes against the current state.
1272*a6d42e7dSPeter Dunlap 	 */
1273*a6d42e7dSPeter Dunlap 	reason_string = "PDU not allowed in current state";
1274*a6d42e7dSPeter Dunlap 	switch (IDM_PDU_OPCODE(pdu)) {
1275*a6d42e7dSPeter Dunlap 	case ISCSI_OP_NOOP_OUT:
1276*a6d42e7dSPeter Dunlap 	case ISCSI_OP_NOOP_IN:
1277*a6d42e7dSPeter Dunlap 		/*
1278*a6d42e7dSPeter Dunlap 		 * Obviously S1-S3 are not allowed since login hasn't started.
1279*a6d42e7dSPeter Dunlap 		 * S8 is probably out as well since the connection has been
1280*a6d42e7dSPeter Dunlap 		 * dropped.
1281*a6d42e7dSPeter Dunlap 		 */
1282*a6d42e7dSPeter Dunlap 		switch (ic->ic_state) {
1283*a6d42e7dSPeter Dunlap 		case CS_S4_IN_LOGIN:
1284*a6d42e7dSPeter Dunlap 		case CS_S5_LOGGED_IN:
1285*a6d42e7dSPeter Dunlap 		case CS_S6_IN_LOGOUT:
1286*a6d42e7dSPeter Dunlap 		case CS_S7_LOGOUT_REQ:
1287*a6d42e7dSPeter Dunlap 			action = CA_FORWARD;
1288*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1289*a6d42e7dSPeter Dunlap 		case CS_S8_CLEANUP:
1290*a6d42e7dSPeter Dunlap 		case CS_S10_IN_CLEANUP:
1291*a6d42e7dSPeter Dunlap 			action = CA_DROP;
1292*a6d42e7dSPeter Dunlap 			break;
1293*a6d42e7dSPeter Dunlap 		default:
1294*a6d42e7dSPeter Dunlap 			action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1295*a6d42e7dSPeter Dunlap 			    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1296*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1297*a6d42e7dSPeter Dunlap 		}
1298*a6d42e7dSPeter Dunlap 		/*NOTREACHED*/
1299*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_CMD:
1300*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_RSP:
1301*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
1302*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
1303*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_DATA:
1304*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_DATA_RSP:
1305*a6d42e7dSPeter Dunlap 	case ISCSI_OP_RTT_RSP:
1306*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SNACK_CMD:
1307*a6d42e7dSPeter Dunlap 	case ISCSI_OP_TEXT_CMD:
1308*a6d42e7dSPeter Dunlap 	case ISCSI_OP_TEXT_RSP:
1309*a6d42e7dSPeter Dunlap 		switch (ic->ic_state) {
1310*a6d42e7dSPeter Dunlap 		case CS_S5_LOGGED_IN:
1311*a6d42e7dSPeter Dunlap 		case CS_S6_IN_LOGOUT:
1312*a6d42e7dSPeter Dunlap 		case CS_S7_LOGOUT_REQ:
1313*a6d42e7dSPeter Dunlap 			action = CA_FORWARD;
1314*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1315*a6d42e7dSPeter Dunlap 		case CS_S8_CLEANUP:
1316*a6d42e7dSPeter Dunlap 		case CS_S10_IN_CLEANUP:
1317*a6d42e7dSPeter Dunlap 			action = CA_DROP;
1318*a6d42e7dSPeter Dunlap 			break;
1319*a6d42e7dSPeter Dunlap 		default:
1320*a6d42e7dSPeter Dunlap 			action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1321*a6d42e7dSPeter Dunlap 			    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1322*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1323*a6d42e7dSPeter Dunlap 		}
1324*a6d42e7dSPeter Dunlap 		/*NOTREACHED*/
1325*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGOUT_CMD:
1326*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGOUT_RSP:
1327*a6d42e7dSPeter Dunlap 	case ISCSI_OP_REJECT_MSG:
1328*a6d42e7dSPeter Dunlap 	case ISCSI_OP_ASYNC_EVENT:
1329*a6d42e7dSPeter Dunlap 		switch (ic->ic_state) {
1330*a6d42e7dSPeter Dunlap 		case CS_S5_LOGGED_IN:
1331*a6d42e7dSPeter Dunlap 		case CS_S6_IN_LOGOUT:
1332*a6d42e7dSPeter Dunlap 		case CS_S7_LOGOUT_REQ:
1333*a6d42e7dSPeter Dunlap 			action = CA_FORWARD;
1334*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1335*a6d42e7dSPeter Dunlap 		case CS_S8_CLEANUP:
1336*a6d42e7dSPeter Dunlap 		case CS_S10_IN_CLEANUP:
1337*a6d42e7dSPeter Dunlap 			action = CA_DROP;
1338*a6d42e7dSPeter Dunlap 			break;
1339*a6d42e7dSPeter Dunlap 		default:
1340*a6d42e7dSPeter Dunlap 			action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1341*a6d42e7dSPeter Dunlap 			    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1342*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1343*a6d42e7dSPeter Dunlap 		}
1344*a6d42e7dSPeter Dunlap 		/*NOTREACHED*/
1345*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGIN_CMD:
1346*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGIN_RSP:
1347*a6d42e7dSPeter Dunlap 		switch (ic->ic_state) {
1348*a6d42e7dSPeter Dunlap 		case CS_S3_XPT_UP:
1349*a6d42e7dSPeter Dunlap 		case CS_S4_IN_LOGIN:
1350*a6d42e7dSPeter Dunlap 			action = CA_FORWARD;
1351*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1352*a6d42e7dSPeter Dunlap 		default:
1353*a6d42e7dSPeter Dunlap 			action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1354*a6d42e7dSPeter Dunlap 			    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1355*a6d42e7dSPeter Dunlap 			goto validate_pdu_done;
1356*a6d42e7dSPeter Dunlap 		}
1357*a6d42e7dSPeter Dunlap 		/*NOTREACHED*/
1358*a6d42e7dSPeter Dunlap 	default:
1359*a6d42e7dSPeter Dunlap 		/* This should never happen -- we already checked above */
1360*a6d42e7dSPeter Dunlap 		ASSERT(0);
1361*a6d42e7dSPeter Dunlap 		/*NOTREACHED*/
1362*a6d42e7dSPeter Dunlap 	}
1363*a6d42e7dSPeter Dunlap 
1364*a6d42e7dSPeter Dunlap 	action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1365*a6d42e7dSPeter Dunlap 	    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1366*a6d42e7dSPeter Dunlap 
1367*a6d42e7dSPeter Dunlap validate_pdu_done:
1368*a6d42e7dSPeter Dunlap 	if (action != CA_FORWARD) {
1369*a6d42e7dSPeter Dunlap 		DTRACE_PROBE2(idm__int__protocol__error,
1370*a6d42e7dSPeter Dunlap 		    idm_conn_event_ctx_t *, event_ctx,
1371*a6d42e7dSPeter Dunlap 		    char *, reason_string);
1372*a6d42e7dSPeter Dunlap 	}
1373*a6d42e7dSPeter Dunlap 
1374*a6d42e7dSPeter Dunlap 	return (action);
1375*a6d42e7dSPeter Dunlap }
1376*a6d42e7dSPeter Dunlap 
1377*a6d42e7dSPeter Dunlap /* ARGSUSED */
1378*a6d42e7dSPeter Dunlap void
1379*a6d42e7dSPeter Dunlap idm_pdu_tx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu)
1380*a6d42e7dSPeter Dunlap {
1381*a6d42e7dSPeter Dunlap 	/*
1382*a6d42e7dSPeter Dunlap 	 * Return the PDU to the caller indicating it was a protocol error.
1383*a6d42e7dSPeter Dunlap 	 * Caller can take appropriate action.
1384*a6d42e7dSPeter Dunlap 	 */
1385*a6d42e7dSPeter Dunlap 	idm_pdu_complete(pdu, IDM_STATUS_PROTOCOL_ERROR);
1386*a6d42e7dSPeter Dunlap }
1387*a6d42e7dSPeter Dunlap 
1388*a6d42e7dSPeter Dunlap void
1389*a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu)
1390*a6d42e7dSPeter Dunlap {
1391*a6d42e7dSPeter Dunlap 	/*
1392*a6d42e7dSPeter Dunlap 	 * Forward PDU to caller indicating it is a protocol error.
1393*a6d42e7dSPeter Dunlap 	 * Caller should take appropriate action.
1394*a6d42e7dSPeter Dunlap 	 */
1395*a6d42e7dSPeter Dunlap 	(*ic->ic_conn_ops.icb_rx_error)(ic, pdu, IDM_STATUS_PROTOCOL_ERROR);
1396*a6d42e7dSPeter Dunlap }
1397*a6d42e7dSPeter Dunlap 
1398*a6d42e7dSPeter Dunlap idm_status_t
1399*a6d42e7dSPeter Dunlap idm_notify_client(idm_conn_t *ic, idm_client_notify_t cn, uintptr_t data)
1400*a6d42e7dSPeter Dunlap {
1401*a6d42e7dSPeter Dunlap 	/*
1402*a6d42e7dSPeter Dunlap 	 * We may want to make this more complicated at some point but
1403*a6d42e7dSPeter Dunlap 	 * for now lets just call the client's notify function and return
1404*a6d42e7dSPeter Dunlap 	 * the status.
1405*a6d42e7dSPeter Dunlap 	 */
1406*a6d42e7dSPeter Dunlap 	return ((*ic->ic_conn_ops.icb_client_notify)(ic, cn, data));
1407*a6d42e7dSPeter Dunlap }
1408*a6d42e7dSPeter Dunlap 
1409*a6d42e7dSPeter Dunlap static idm_status_t
1410*a6d42e7dSPeter Dunlap idm_ffp_enable(idm_conn_t *ic)
1411*a6d42e7dSPeter Dunlap {
1412*a6d42e7dSPeter Dunlap 	idm_status_t rc;
1413*a6d42e7dSPeter Dunlap 
1414*a6d42e7dSPeter Dunlap 	/*
1415*a6d42e7dSPeter Dunlap 	 * On the initiator side the client will see this notification
1416*a6d42e7dSPeter Dunlap 	 * before the actual login succes PDU.  This shouldn't be a big
1417*a6d42e7dSPeter Dunlap 	 * deal since the initiator drives the connection.  It can simply
1418*a6d42e7dSPeter Dunlap 	 * wait for the login response then start sending SCSI commands.
1419*a6d42e7dSPeter Dunlap 	 * Kind ugly though compared with the way things work on target
1420*a6d42e7dSPeter Dunlap 	 * connections.
1421*a6d42e7dSPeter Dunlap 	 */
1422*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
1423*a6d42e7dSPeter Dunlap 	ic->ic_ffp = B_TRUE;
1424*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
1425*a6d42e7dSPeter Dunlap 
1426*a6d42e7dSPeter Dunlap 	rc = idm_notify_client(ic, CN_FFP_ENABLED, NULL);
1427*a6d42e7dSPeter Dunlap 	if (rc != IDM_STATUS_SUCCESS) {
1428*a6d42e7dSPeter Dunlap 		mutex_enter(&ic->ic_state_mutex);
1429*a6d42e7dSPeter Dunlap 		ic->ic_ffp = B_FALSE;
1430*a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
1431*a6d42e7dSPeter Dunlap 	}
1432*a6d42e7dSPeter Dunlap 	return (rc);
1433*a6d42e7dSPeter Dunlap }
1434*a6d42e7dSPeter Dunlap 
1435*a6d42e7dSPeter Dunlap static void
1436*a6d42e7dSPeter Dunlap idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type)
1437*a6d42e7dSPeter Dunlap {
1438*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
1439*a6d42e7dSPeter Dunlap 	ic->ic_ffp = B_FALSE;
1440*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
1441*a6d42e7dSPeter Dunlap 
1442*a6d42e7dSPeter Dunlap 	/* Client can't "fail" CN_FFP_DISABLED */
1443*a6d42e7dSPeter Dunlap 	(void) idm_notify_client(ic, CN_FFP_DISABLED,
1444*a6d42e7dSPeter Dunlap 	    (uintptr_t)disable_type);
1445*a6d42e7dSPeter Dunlap }
1446*a6d42e7dSPeter Dunlap 
1447*a6d42e7dSPeter Dunlap static void
1448*a6d42e7dSPeter Dunlap idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1449*a6d42e7dSPeter Dunlap {
1450*a6d42e7dSPeter Dunlap 	ASSERT((event_ctx->iec_event == CE_LOGIN_RCV) ||
1451*a6d42e7dSPeter Dunlap 	    (event_ctx->iec_event == CE_LOGIN_SND));
1452*a6d42e7dSPeter Dunlap 
1453*a6d42e7dSPeter Dunlap 	/*
1454*a6d42e7dSPeter Dunlap 	 * Currently it's not clear what we would do here -- since
1455*a6d42e7dSPeter Dunlap 	 * we went to the trouble of coding an "initial login" hook
1456*a6d42e7dSPeter Dunlap 	 * we'll leave it in for now.  Remove before integration if
1457*a6d42e7dSPeter Dunlap 	 * it's not used for anything.
1458*a6d42e7dSPeter Dunlap 	 */
1459*a6d42e7dSPeter Dunlap 	ic->ic_state_flags |= CF_INITIAL_LOGIN;
1460*a6d42e7dSPeter Dunlap }
1461*a6d42e7dSPeter Dunlap 
1462*a6d42e7dSPeter Dunlap static void
1463*a6d42e7dSPeter Dunlap idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1464*a6d42e7dSPeter Dunlap {
1465*a6d42e7dSPeter Dunlap 	idm_pdu_t		*pdu = (idm_pdu_t *)event_ctx->iec_info;
1466*a6d42e7dSPeter Dunlap 	iscsi_login_hdr_t	*login_req =
1467*a6d42e7dSPeter Dunlap 	    (iscsi_login_hdr_t *)pdu->isp_hdr;
1468*a6d42e7dSPeter Dunlap 
1469*a6d42e7dSPeter Dunlap 	ASSERT((event_ctx->iec_event == CE_LOGIN_SUCCESS_RCV) ||
1470*a6d42e7dSPeter Dunlap 	    (event_ctx->iec_event == CE_LOGIN_SUCCESS_SND));
1471*a6d42e7dSPeter Dunlap 
1472*a6d42e7dSPeter Dunlap 	/*
1473*a6d42e7dSPeter Dunlap 	 * Save off CID
1474*a6d42e7dSPeter Dunlap 	 */
1475*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
1476*a6d42e7dSPeter Dunlap 	ic->ic_login_cid = ntohs(login_req->cid);
1477*a6d42e7dSPeter Dunlap 	ic->ic_login_info_valid =  B_TRUE;
1478*a6d42e7dSPeter Dunlap 
1479*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
1480*a6d42e7dSPeter Dunlap }
1481