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