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