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