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