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