Esempio n. 1
0
/* ARGSUSED */
static void
idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
{
	idm_pdu_t *pdu;

	/*
	 * Cleanup logout success/fail completion if it's been delayed
	 * until now.
	 *
	 * All new events are filtered out before reaching this state, but
	 * there might already be events in the event queue, so handle the
	 * SND_DONE events here. Note that if either of the following
	 * SND_DONE events happens AFTER the change to state S11, then the
	 * event filter inside dm_conn_event_locked does enough cleanup.
	 */
	switch (event_ctx->iec_event) {
	case CE_LOGOUT_SUCCESS_SND_DONE:
	case CE_LOGOUT_FAIL_SND_DONE:
		pdu = (idm_pdu_t *)event_ctx->iec_info;
		/* restore client callback */
		pdu->isp_callback =  ic->ic_client_callback;
		ic->ic_client_callback = NULL;
		idm_pdu_complete(pdu, pdu->isp_status);
		break;
	}

}
Esempio n. 2
0
/* ARGSUSED */
void
idm_pdu_tx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu)
{
	/*
	 * Return the PDU to the caller indicating it was a protocol error.
	 * Caller can take appropriate action.
	 */
	idm_pdu_complete(pdu, IDM_STATUS_PROTOCOL_ERROR);
}
Esempio n. 3
0
void
idm_conn_event_locked(idm_conn_t *ic, idm_conn_event_t event,
    uintptr_t event_info, idm_pdu_event_type_t pdu_event_type)
{
	idm_conn_event_ctx_t	*event_ctx;

	ASSERT(mutex_owned(&ic->ic_state_mutex));

	idm_sm_audit_event(&ic->ic_state_audit, SAS_IDM_CONN,
	    (int)ic->ic_state, (int)event, event_info);

	/*
	 * It's very difficult to prevent a few straggling events
	 * at the end.  For example idm_sorx_thread will generate
	 * a CE_TRANSPORT_FAIL event when it exits.  Rather than
	 * push complicated restrictions all over the code to
	 * prevent this we will simply drop the events (and in
	 * the case of PDU events release them appropriately)
	 * since they are irrelevant once we are in a terminal state.
	 * Of course those threads need to have appropriate holds on
	 * the connection otherwise it might disappear.
	 */
	if ((ic->ic_state == CS_S9_INIT_ERROR) ||
	    (ic->ic_state == CS_S9A_REJECTED) ||
	    (ic->ic_state == CS_S11_COMPLETE)) {
		if ((pdu_event_type == CT_TX_PDU) ||
		    (pdu_event_type == CT_RX_PDU)) {
			ic->ic_pdu_events--;
			idm_pdu_complete((idm_pdu_t *)event_info,
			    IDM_STATUS_SUCCESS);
		}
		IDM_SM_LOG(CE_NOTE, "*** Dropping event %s (%d) because of"
		    "state %s (%d)",
		    idm_ce_name[event], event,
		    idm_cs_name[ic->ic_state], ic->ic_state);
		return;
	}

	/*
	 * Normal event handling
	 */
	idm_conn_hold(ic);

	event_ctx = kmem_zalloc(sizeof (*event_ctx), KM_SLEEP);
	event_ctx->iec_ic = ic;
	event_ctx->iec_event = event;
	event_ctx->iec_info = event_info;
	event_ctx->iec_pdu_event_type = pdu_event_type;

	(void) taskq_dispatch(ic->ic_state_taskq, &idm_conn_event_handler,
	    event_ctx, TQ_SLEEP);
}
Esempio n. 4
0
static void
idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
{
	idm_pdu_t *pdu;

	/*
	 * Need to cancel the cleanup timeout before leaving this state
	 * if it hasn't already fired.
	 */
	switch (event_ctx->iec_event) {
	case CE_LOGOUT_SUCCESS_RCV:
	case CE_LOGOUT_SUCCESS_SND:
	case CE_LOGOUT_SESSION_SUCCESS:
		(void) untimeout(ic->ic_state_timeout);
		/*FALLTHROUGH*/
	case CE_CLEANUP_TIMEOUT:
		/* M1 */
		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
		break;
	case CE_LOGOUT_OTHER_CONN_RCV:
	case CE_LOGOUT_OTHER_CONN_SND:
		/* M2 */
		idm_update_state(ic, CS_S10_IN_CLEANUP, event_ctx);
		break;
	case CE_LOGOUT_SUCCESS_SND_DONE:
	case CE_LOGOUT_FAIL_SND_DONE:
		pdu = (idm_pdu_t *)event_ctx->iec_info;
		/* restore client callback */
		pdu->isp_callback =  ic->ic_client_callback;
		ic->ic_client_callback = NULL;
		idm_pdu_complete(pdu, pdu->isp_status);
		break;
	case CE_LOGOUT_SESSION_RCV:
	case CE_LOGOUT_SESSION_SND:
	case CE_TX_PROTOCOL_ERROR:
	case CE_RX_PROTOCOL_ERROR:
	case CE_MISC_TX:
	case CE_MISC_RX:
	case CE_TRANSPORT_FAIL:
	case CE_LOGIN_TIMEOUT:
	case CE_LOGOUT_TIMEOUT:
		/* Don't care */
		break;
	default:
		ASSERT(0);
	}
}
Esempio n. 5
0
/* ARGSUSED */
static void
idm_state_s9b_wait_snd_done(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
{
	idm_pdu_t *pdu;
	/*
	 * Wait for completion of the login fail sequence and then
	 * go to state S9_INIT_ERROR to clean up the connection.
	 */
	switch (event_ctx->iec_event) {
	case CE_LOGIN_FAIL_SND_DONE:
		pdu = (idm_pdu_t *)event_ctx->iec_info;
		/* restore client callback */
		pdu->isp_callback =  ic->ic_client_callback;
		ic->ic_client_callback = NULL;
		idm_pdu_complete(pdu, pdu->isp_status);
		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
		break;

	/* All other events ignored */
	}
}
Esempio n. 6
0
/*
 * iser_pdu_tx() transmits a Control PDU via the iSER channel. We pull the
 * channel out of the idm_conn_t passed in, and pass it and the pdu to the
 * iser_xfer routine.
 */
static void
iser_pdu_tx(idm_conn_t *ic, idm_pdu_t *pdu)
{
    iser_conn_t	*iser_conn;
    iser_status_t	iser_status;

    iser_conn = (iser_conn_t *)ic->ic_transport_private;

    iser_status = iser_xfer_ctrlpdu(iser_conn->ic_chan, pdu);
    if (iser_status != ISER_STATUS_SUCCESS) {
        ISER_LOG(CE_WARN, "iser_pdu_tx: failed iser_xfer_ctrlpdu: "
                 "ic (0x%p) pdu (0x%p)", (void *) ic, (void *) pdu);
        /* Fail this PDU transmission */
        idm_pdu_complete(pdu, IDM_STATUS_FAIL);
    }

    /*
     * We successfully posted this PDU for transmission.
     * The completion handler will invoke idm_pdu_complete()
     * with the completion status. See iser_cq.c for more
     * information.
     */
}
Esempio n. 7
0
static void
idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
{
	idm_pdu_t *pdu;

	switch (event_ctx->iec_event) {
	case CE_LOGOUT_SUCCESS_SND_DONE:
		pdu = (idm_pdu_t *)event_ctx->iec_info;

		/* Close connection (if it's not already closed) */
		ASSERT(IDM_CONN_ISTGT(ic));
		ic->ic_transport_ops->it_tgt_conn_disconnect(ic);

		/* restore client callback */
		pdu->isp_callback =  ic->ic_client_callback;
		ic->ic_client_callback = NULL;
		idm_pdu_complete(pdu, pdu->isp_status);
		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
		break;
	case CE_LOGOUT_FAIL_SND_DONE:
		pdu = (idm_pdu_t *)event_ctx->iec_info;
		/* restore client callback */
		pdu->isp_callback =  ic->ic_client_callback;
		ic->ic_client_callback = NULL;
		idm_pdu_complete(pdu, pdu->isp_status);
		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
		break;
	case CE_LOGOUT_SUCCESS_SND:
	case CE_LOGOUT_FAIL_SND:
		/*
		 * Allow the logout response pdu to be sent and defer
		 * the state machine update until the completion callback.
		 * Only 1 level or callback interposition is allowed.
		 */
		pdu = (idm_pdu_t *)event_ctx->iec_info;
		ASSERT(ic->ic_client_callback == NULL);
		ic->ic_client_callback = pdu->isp_callback;
		if (event_ctx->iec_event == CE_LOGOUT_SUCCESS_SND) {
			pdu->isp_callback =
			    idm_state_s6_in_logout_success_snd_done;
		} else {
			pdu->isp_callback =
			    idm_state_s6_in_logout_fail_snd_done;
		}
		break;
	case CE_LOGOUT_SUCCESS_RCV:
		/*
		 * Need to deliver this PDU to the initiator now because after
		 * we update the state to CS_S11_COMPLETE the initiator will
		 * no longer be in an appropriate state.
		 */
		event_ctx->iec_pdu_forwarded = B_TRUE;
		pdu = (idm_pdu_t *)event_ctx->iec_info;
		idm_pdu_rx_forward(ic, pdu);
		/* FALLTHROUGH */
	case CE_LOGOUT_SESSION_SUCCESS:
		/* T13 */

		/* Close connection (if it's not already closed) */
		if (IDM_CONN_ISTGT(ic)) {
			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
		} else {
			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
		}

		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
		break;
	case CE_ASYNC_LOGOUT_RCV:
		/* T14 Do nothing */
		break;
	case CE_TRANSPORT_FAIL:
	case CE_ASYNC_DROP_CONN_RCV:
	case CE_ASYNC_DROP_CONN_SND:
	case CE_ASYNC_DROP_ALL_CONN_RCV:
	case CE_ASYNC_DROP_ALL_CONN_SND:
	case CE_LOGOUT_FAIL_RCV:
		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
		break;
	case CE_TX_PROTOCOL_ERROR:
	case CE_RX_PROTOCOL_ERROR:
	case CE_MISC_TX:
	case CE_MISC_RX:
	case CE_LOGIN_TIMEOUT:
		/* Don't care */
		break;
	default:
		ASSERT(0);
	}
}
Esempio n. 8
0
static void
idm_conn_event_handler(void *event_ctx_opaque)
{
	idm_conn_event_ctx_t *event_ctx = event_ctx_opaque;
	idm_conn_t *ic = event_ctx->iec_ic;
	idm_pdu_t *pdu = (idm_pdu_t *)event_ctx->iec_info;
	idm_pdu_event_action_t action;

	IDM_SM_LOG(CE_NOTE, "idm_conn_event_handler: conn %p event %s(%d)",
	    (void *)ic, idm_ce_name[event_ctx->iec_event],
	    event_ctx->iec_event);
	DTRACE_PROBE2(conn__event,
	    idm_conn_t *, ic, idm_conn_event_ctx_t *, event_ctx);

	/*
	 * Validate event
	 */
	ASSERT(event_ctx->iec_event != CE_UNDEFINED);
	ASSERT3U(event_ctx->iec_event, <, CE_MAX_EVENT);

	/*
	 * Validate current state
	 */
	ASSERT(ic->ic_state != CS_S0_UNDEFINED);
	ASSERT3U(ic->ic_state, <, CS_MAX_STATE);

	/*
	 * Validate PDU-related events against the current state.  If a PDU
	 * is not allowed in the current state we change the event to a
	 * protocol error.  This simplifies the state-specific event handlers.
	 * For example the CS_S2_XPT_WAIT state only needs to handle the
	 * CE_TX_PROTOCOL_ERROR and CE_RX_PROTOCOL_ERROR events since
	 * no PDU's can be transmitted or received in that state.
	 */
	event_ctx->iec_pdu_forwarded = B_FALSE;
	if (event_ctx->iec_pdu_event_type != CT_NONE) {
		ASSERT(pdu != NULL);
		action = idm_conn_sm_validate_pdu(ic, event_ctx, pdu);

		switch (action) {
		case CA_TX_PROTOCOL_ERROR:
			/*
			 * Change event and forward the PDU
			 */
			event_ctx->iec_event = CE_TX_PROTOCOL_ERROR;
			break;
		case CA_RX_PROTOCOL_ERROR:
			/*
			 * Change event and forward the PDU.
			 */
			event_ctx->iec_event = CE_RX_PROTOCOL_ERROR;
			break;
		case CA_FORWARD:
			/*
			 * Let the state-specific event handlers take
			 * care of it.
			 */
			break;
		case CA_DROP:
			/*
			 * It never even happened
			 */
			IDM_SM_LOG(CE_NOTE, "*** drop PDU %p", (void *) pdu);
			idm_pdu_complete(pdu, IDM_STATUS_FAIL);
			break;
		default:
			ASSERT(0);
			break;
		}
	}

	switch (ic->ic_state) {
	case CS_S1_FREE:
		idm_state_s1_free(ic, event_ctx);
		break;
	case CS_S2_XPT_WAIT:
		idm_state_s2_xpt_wait(ic, event_ctx);
		break;
	case CS_S3_XPT_UP:
		idm_state_s3_xpt_up(ic, event_ctx);
		break;
	case CS_S4_IN_LOGIN:
		idm_state_s4_in_login(ic, event_ctx);
		break;
	case CS_S5_LOGGED_IN:
		idm_state_s5_logged_in(ic, event_ctx);
		break;
	case CS_S6_IN_LOGOUT:
		idm_state_s6_in_logout(ic, event_ctx);
		break;
	case CS_S7_LOGOUT_REQ:
		idm_state_s7_logout_req(ic, event_ctx);
		break;
	case CS_S8_CLEANUP:
		idm_state_s8_cleanup(ic, event_ctx);
		break;
	case CS_S9A_REJECTED:
		idm_state_s9a_rejected(ic, event_ctx);
		break;
	case CS_S9B_WAIT_SND_DONE:
		idm_state_s9b_wait_snd_done(ic, event_ctx);
		break;
	case CS_S9_INIT_ERROR:
		idm_state_s9_init_error(ic, event_ctx);
		break;
	case CS_S10_IN_CLEANUP:
		idm_state_s10_in_cleanup(ic, event_ctx);
		break;
	case CS_S11_COMPLETE:
		idm_state_s11_complete(ic, event_ctx);
		break;
	case CS_S12_ENABLE_DM:
		idm_state_s12_enable_dm(ic, event_ctx);
		break;
	default:
		ASSERT(0);
		break;
	}

	/*
	 * Now that we've updated the state machine, if this was
	 * a PDU-related event take the appropriate action on the PDU
	 * (transmit it, forward it to the clients RX callback, drop
	 * it, etc).
	 */
	if (event_ctx->iec_pdu_event_type != CT_NONE) {
		switch (action) {
		case CA_TX_PROTOCOL_ERROR:
			idm_pdu_tx_protocol_error(ic, pdu);
			break;
		case CA_RX_PROTOCOL_ERROR:
			idm_pdu_rx_protocol_error(ic, pdu);
			break;
		case CA_FORWARD:
			if (!event_ctx->iec_pdu_forwarded) {
				if (event_ctx->iec_pdu_event_type ==
				    CT_RX_PDU) {
					idm_pdu_rx_forward(ic, pdu);
				} else {
					idm_pdu_tx_forward(ic, pdu);
				}
			}
			break;
		default:
			ASSERT(0);
			break;
		}
	}

	/*
	 * Update outstanding PDU event count (see idm_pdu_tx for
	 * how this is used)
	 */
	if ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ||
	    (event_ctx->iec_pdu_event_type == CT_RX_PDU)) {
		mutex_enter(&ic->ic_state_mutex);
		ic->ic_pdu_events--;
		mutex_exit(&ic->ic_state_mutex);
	}

	idm_conn_rele(ic);
	kmem_free(event_ctx, sizeof (*event_ctx));
}