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