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