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