/* 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; } }
/* 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); }
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); }
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); } }
/* 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 */ } }
/* * 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. */ }
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); } }
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)); }