#include "ocs.h"
#include "ocs_device.h"
#include "ocs_fabric.h"
#include "ocs_els.h"
static void *__ocs_d_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg);
static void *__ocs_d_wait_del_node(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg);
static void *__ocs_d_wait_del_ini_tgt(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg);
static int32_t ocs_process_abts(ocs_io_t *io, fc_header_t *hdr);
void
ocs_d_send_prli_rsp(ocs_io_t *io, uint16_t ox_id)
{
ocs_t *ocs = io->ocs;
ocs_node_t *node = io->node;
if (ocs->fc_type != node->fc_type) {
node_printf(node, "PRLI rejected by target-server, fc-type not supported\n");
ocs_send_ls_rjt(io, ox_id, FC_REASON_UNABLE_TO_PERFORM,
FC_EXPL_REQUEST_NOT_SUPPORTED, 0, NULL, NULL);
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
}
if (node->sport->enable_tgt && (ocs_scsi_validate_initiator(node) == 0)) {
node_printf(node, "PRLI rejected by target-server\n");
ocs_send_ls_rjt(io, ox_id, FC_REASON_UNABLE_TO_PERFORM,
FC_EXPL_NO_ADDITIONAL, 0, NULL, NULL);
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
} else {
ocs_send_prli_acc(io, ox_id, ocs->fc_type, NULL, NULL);
ocs_node_transition(node, __ocs_d_device_ready, NULL);
}
}
void *
__ocs_d_initiate_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER: {
int32_t rc = OCS_SCSI_CALL_COMPLETE;
ocs_scsi_io_alloc_disable(node);
if (node->init && !node->targ) {
ocs_log_debug(node->ocs,
"[%s] delete (initiator) WWPN %s WWNN %s\n",
node->display_name, node->wwpn, node->wwnn);
ocs_node_transition(node, __ocs_d_wait_del_node, NULL);
if (node->sport->enable_tgt) {
rc = ocs_scsi_del_initiator(node,
OCS_SCSI_INITIATOR_DELETED);
}
if (rc == OCS_SCSI_CALL_COMPLETE) {
ocs_node_post_event(node,
OCS_EVT_NODE_DEL_INI_COMPLETE, NULL);
}
} else if (node->targ && !node->init) {
ocs_log_debug(node->ocs,
"[%s] delete (target) WWPN %s WWNN %s\n",
node->display_name, node->wwpn, node->wwnn);
ocs_node_transition(node, __ocs_d_wait_del_node, NULL);
if (node->sport->enable_ini) {
rc = ocs_scsi_del_target(node,
OCS_SCSI_TARGET_DELETED);
}
if (rc == OCS_SCSI_CALL_COMPLETE) {
ocs_node_post_event(node,
OCS_EVT_NODE_DEL_TGT_COMPLETE, NULL);
}
} else if (node->init && node->targ) {
ocs_log_debug(node->ocs,
"[%s] delete (initiator+target) WWPN %s WWNN %s\n",
node->display_name, node->wwpn, node->wwnn);
ocs_node_transition(node, __ocs_d_wait_del_ini_tgt, NULL);
if (node->sport->enable_tgt) {
rc = ocs_scsi_del_initiator(node,
OCS_SCSI_INITIATOR_DELETED);
}
if (rc == OCS_SCSI_CALL_COMPLETE) {
ocs_node_post_event(node,
OCS_EVT_NODE_DEL_INI_COMPLETE, NULL);
}
rc = OCS_SCSI_CALL_COMPLETE;
if (node->sport->enable_ini) {
rc = ocs_scsi_del_target(node,
OCS_SCSI_TARGET_DELETED);
}
if (rc == OCS_SCSI_CALL_COMPLETE) {
ocs_node_post_event(node,
OCS_EVT_NODE_DEL_TGT_COMPLETE, NULL);
}
}
if (node->attached) {
rc = ocs_hw_node_detach(&ocs->hw, &node->rnode);
if (node->rnode.free_group) {
ocs_remote_node_group_free(node->node_group);
node->node_group = NULL;
node->rnode.free_group = FALSE;
}
if (rc != OCS_HW_RTN_SUCCESS &&
rc != OCS_HW_RTN_SUCCESS_SYNC) {
node_printf(node,
"Failed freeing HW node, rc=%d\n", rc);
}
}
if (!node->init && !node->targ){
ocs_node_initiate_cleanup(node);
}
break;
}
case OCS_EVT_ALL_CHILD_NODES_FREE:
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
static void *
__ocs_d_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_node_t *node = NULL;
ocs_t *ocs = NULL;
ocs_assert(ctx, NULL);
node = ctx->app;
ocs_assert(node, NULL);
ocs = node->ocs;
ocs_assert(ocs, NULL);
switch(evt) {
case OCS_EVT_SHUTDOWN:
ocs_log_debug(ocs, "[%s] %-20s %-20s\n", node->display_name, funcname, ocs_sm_event_name(evt));
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
break;
case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO:
ocs_log_debug(ocs, "[%s] %-20s %-20s\n", node->display_name, funcname, ocs_sm_event_name(evt));
node->shutdown_reason = OCS_NODE_SHUTDOWN_EXPLICIT_LOGO;
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
break;
case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO:
ocs_log_debug(ocs, "[%s] %-20s %-20s\n", node->display_name, funcname, ocs_sm_event_name(evt));
node->shutdown_reason = OCS_NODE_SHUTDOWN_IMPLICIT_LOGO;
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
break;
default:
__ocs_node_common(funcname, ctx, evt, arg);
break;
}
return NULL;
}
void *
__ocs_d_wait_loop(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_DOMAIN_ATTACH_OK: {
ocs_node_init_device(node, TRUE);
break;
}
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_del_ini_tgt(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
case OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
case OCS_EVT_ALL_CHILD_NODES_FREE:
break;
case OCS_EVT_NODE_DEL_INI_COMPLETE:
case OCS_EVT_NODE_DEL_TGT_COMPLETE:
ocs_node_transition(node, __ocs_d_wait_del_node, NULL);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_REQ_FAIL:
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
break;
case OCS_EVT_SHUTDOWN:
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO:
case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO:
node_printf(node, "%s received\n", ocs_sm_event_name(evt));
break;
case OCS_EVT_DOMAIN_ATTACH_OK:
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_del_node(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
case OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
case OCS_EVT_ALL_CHILD_NODES_FREE:
break;
case OCS_EVT_NODE_DEL_INI_COMPLETE:
case OCS_EVT_NODE_DEL_TGT_COMPLETE:
ocs_node_initiate_cleanup(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_REQ_FAIL:
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
break;
case OCS_EVT_SHUTDOWN:
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO:
case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO:
node_printf(node, "%s received\n", ocs_sm_event_name(evt));
break;
case OCS_EVT_DOMAIN_ATTACH_OK:
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void
ocs_send_ls_acc_after_attach(ocs_io_t *io, fc_header_t *hdr, ocs_node_send_ls_acc_e ls)
{
ocs_node_t *node = io->node;
uint16_t ox_id = ocs_be16toh(hdr->ox_id);
ocs_assert(node->send_ls_acc == OCS_NODE_SEND_LS_ACC_NONE);
node->ls_acc_oxid = ox_id;
node->send_ls_acc = ls;
node->ls_acc_io = io;
node->ls_acc_did = fc_be24toh(hdr->d_id);
}
void
ocs_process_prli_payload(ocs_node_t *node, fc_prli_payload_t *prli)
{
node->init = (ocs_be16toh(prli->service_params) & FC_PRLI_INITIATOR_FUNCTION) != 0;
node->targ = (ocs_be16toh(prli->service_params) & FC_PRLI_TARGET_FUNCTION) != 0;
node->fcp2device = (ocs_be16toh(prli->service_params) & FC_PRLI_RETRY) != 0;
node->fc_type = prli->type;
}
static int32_t
ocs_process_abts(ocs_io_t *io, fc_header_t *hdr)
{
ocs_node_t *node = io->node;
ocs_t *ocs = node->ocs;
uint16_t ox_id = ocs_be16toh(hdr->ox_id);
uint16_t rx_id = ocs_be16toh(hdr->rx_id);
ocs_io_t *abortio;
abortio = ocs_io_find_tgt_io(ocs, node, ox_id, rx_id);
if (abortio != NULL && (ocs_ref_get_unless_zero(&abortio->ref) != 0)) {
node_printf(node, "Abort request: ox_id [%04x] rx_id [%04x]\n",
ox_id, rx_id);
io->display_name = "abts";
io->init_task_tag = ox_id;
io->abort_rx_id = rx_id;
io->tmf_cmd = OCS_SCSI_TMF_ABORT_TASK;
ocs_scsi_recv_tmf(io, abortio->tgt_io.lun, OCS_SCSI_TMF_ABORT_TASK, abortio, 0);
ocs_ref_put(&abortio->ref);
} else {
node_printf(node, "Abort request: ox_id [%04x], IO not found (exists=%d)\n",
ox_id, (abortio != NULL));
ocs_bls_send_acc_hdr(io, hdr);
}
return 0;
}
void *
__ocs_d_wait_plogi_acc_cmpl(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_CMPL_FAIL:
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
break;
case OCS_EVT_SRRS_ELS_CMPL_OK:
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
ocs_node_transition(node, __ocs_d_port_logged_in, NULL);
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_logo_rsp(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_REQ_OK:
case OCS_EVT_SRRS_ELS_REQ_RJT:
case OCS_EVT_SRRS_ELS_REQ_FAIL:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_LOGO, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
node_printf(node, "LOGO sent (evt=%s), shutdown node\n", ocs_sm_event_name(evt));
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_prlo_rsp(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_REQ_OK:
case OCS_EVT_SRRS_ELS_REQ_RJT:
case OCS_EVT_SRRS_ELS_REQ_FAIL:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PRLO, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
node_printf(node, "PRLO sent (evt=%s)\n", ocs_sm_event_name(evt));
ocs_node_transition(node, __ocs_d_port_logged_in, NULL);
break;
default:
__ocs_node_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void
ocs_node_init_device(ocs_node_t *node, int send_plogi)
{
node->send_plogi = send_plogi;
if ((node->ocs->nodedb_mask & OCS_NODEDB_PAUSE_NEW_NODES) && !FC_ADDR_IS_DOMAIN_CTRL(node->rnode.fc_id)) {
node->nodedb_state = __ocs_d_init;
ocs_node_transition(node, __ocs_node_paused, NULL);
} else {
ocs_node_transition(node, __ocs_d_init, NULL);
}
}
void *
__ocs_d_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
int32_t rc;
ocs_node_cb_t *cbdata = arg;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
if (node->send_plogi) {
if (node->sport->enable_ini && node->sport->domain->attached) {
ocs_send_plogi(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT,
OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_d_wait_plogi_rsp, NULL);
} else {
node_printf(node, "not sending plogi sport.ini=%d, domain attached=%d\n",
node->sport->enable_ini, node->sport->domain->attached);
}
}
break;
case OCS_EVT_PLOGI_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
uint32_t d_id = fc_be24toh(hdr->d_id);
ocs_node_save_sparms(node, cbdata->payload->dma.virt);
ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI);
if (node->sport->domain->attached) {
rc = ocs_node_attach(node);
ocs_node_transition(node, __ocs_d_wait_node_attach, NULL);
if (rc == OCS_HW_RTN_SUCCESS_SYNC) {
ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL);
}
break;
}
switch (node->sport->topology) {
case OCS_SPORT_TOPOLOGY_P2P:
ocs_domain_attach(node->sport->domain, d_id);
ocs_node_transition(node, __ocs_d_wait_domain_attach, NULL);
break;
case OCS_SPORT_TOPOLOGY_FABRIC:
ocs_node_transition(node, __ocs_d_wait_domain_attach, NULL);
break;
case OCS_SPORT_TOPOLOGY_UNKNOWN:
node_printf(node, "received PLOGI, with unknown topology did=0x%x\n", d_id);
ocs_node_transition(node, __ocs_d_wait_topology_notify, NULL);
break;
default:
node_printf(node, "received PLOGI, with unexpected topology %d\n",
node->sport->topology);
ocs_assert(FALSE, NULL);
break;
}
break;
}
case OCS_EVT_FDISC_RCVD: {
__ocs_d_common(__func__, ctx, evt, arg);
break;
}
case OCS_EVT_FLOGI_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_assert(ocs_rnode_is_nport(cbdata->payload->dma.virt), NULL);
ocs_domain_save_sparms(node->sport->domain, cbdata->payload->dma.virt);
ocs_fabric_set_topology(node, OCS_SPORT_TOPOLOGY_P2P);
ocs_send_flogi_p2p_acc(cbdata->io, ocs_be16toh(hdr->ox_id), fc_be24toh(hdr->d_id), NULL, NULL);
if (ocs_p2p_setup(node->sport)) {
node_printf(node, "p2p setup failed, shutting down node\n");
ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL);
} else {
ocs_node_transition(node, __ocs_p2p_wait_flogi_acc_cmpl, NULL);
}
break;
}
case OCS_EVT_LOGO_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
if (!node->sport->domain->attached) {
node_printf(node, "%s domain not attached, dropping\n", ocs_sm_event_name(evt));
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
break;
}
ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL);
break;
}
case OCS_EVT_PRLI_RCVD:
case OCS_EVT_PRLO_RCVD:
case OCS_EVT_PDISC_RCVD:
case OCS_EVT_ADISC_RCVD:
case OCS_EVT_RSCN_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
if (!node->sport->domain->attached) {
node_printf(node, "%s domain not attached, dropping\n", ocs_sm_event_name(evt));
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
break;
}
node_printf(node, "%s received, sending reject\n", ocs_sm_event_name(evt));
ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id),
FC_REASON_UNABLE_TO_PERFORM, FC_EXPL_NPORT_LOGIN_REQUIRED, 0,
NULL, NULL);
break;
}
case OCS_EVT_FCP_CMD_RCVD: {
if (!node->sport->domain->attached) {
node_printf(node, "%s domain not attached, dropping\n", ocs_sm_event_name(evt));
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
break;
}
node_printf(node, "FCP_CMND received, send LOGO\n");
if (ocs_send_logo(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, 0, NULL, NULL) == NULL) {
node_printf(node, "Failed to send LOGO\n");
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
} else {
ocs_node_transition(node, __ocs_d_wait_logo_rsp, NULL);
}
break;
}
case OCS_EVT_DOMAIN_ATTACH_OK:
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_plogi_rsp(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
int32_t rc;
ocs_node_cb_t *cbdata = arg;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_PLOGI_RCVD: {
ocs_node_save_sparms(node, cbdata->payload->dma.virt);
ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI);
rc = ocs_node_attach(node);
ocs_node_transition(node, __ocs_d_wait_node_attach, NULL);
if (rc == OCS_HW_RTN_SUCCESS_SYNC) {
ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL);
}
break;
}
case OCS_EVT_PRLI_RCVD:
ocs_process_prli_payload(node, cbdata->payload->dma.virt);
if (ocs->fc_type == node->fc_type) {
ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PRLI);
ocs_node_transition(node, __ocs_d_wait_plogi_rsp_recvd_prli, NULL);
} else {
}
break;
case OCS_EVT_LOGO_RCVD:
case OCS_EVT_PRLO_RCVD:
case OCS_EVT_PDISC_RCVD:
case OCS_EVT_FDISC_RCVD:
case OCS_EVT_ADISC_RCVD:
case OCS_EVT_RSCN_RCVD:
case OCS_EVT_SCR_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
node_printf(node, "%s received, sending reject\n", ocs_sm_event_name(evt));
ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id),
FC_REASON_UNABLE_TO_PERFORM, FC_EXPL_NPORT_LOGIN_REQUIRED, 0,
NULL, NULL);
break;
}
case OCS_EVT_SRRS_ELS_REQ_OK:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_node_save_sparms(node, cbdata->els->els_rsp.virt);
ocs_display_sparams(node->display_name, "plogi rcvd resp", 0, NULL,
((uint8_t*)cbdata->els->els_rsp.virt) + 4);
rc = ocs_node_attach(node);
ocs_node_transition(node, __ocs_d_wait_node_attach, NULL);
if (rc == OCS_HW_RTN_SUCCESS_SYNC) {
ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL);
}
break;
case OCS_EVT_SRRS_ELS_REQ_FAIL:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL);
break;
case OCS_EVT_SRRS_ELS_REQ_RJT:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
break;
case OCS_EVT_FCP_CMD_RCVD: {
node_printf(node, "FCP_CMND received, drop\n");
break;
}
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_plogi_rsp_recvd_prli(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
int32_t rc;
ocs_node_cb_t *cbdata = arg;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_REQ_OK:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_node_save_sparms(node, cbdata->els->els_rsp.virt);
ocs_display_sparams(node->display_name, "plogi rcvd resp", 0, NULL,
((uint8_t*)cbdata->els->els_rsp.virt) + 4);
rc = ocs_node_attach(node);
ocs_node_transition(node, __ocs_d_wait_node_attach, NULL);
if (rc == OCS_HW_RTN_SUCCESS_SYNC) {
ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL);
}
break;
case OCS_EVT_SRRS_ELS_REQ_FAIL:
case OCS_EVT_SRRS_ELS_REQ_RJT:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL);
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_domain_attach(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
int32_t rc;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_DOMAIN_ATTACH_OK:
ocs_assert(node->sport->domain->attached, NULL);
rc = ocs_node_attach(node);
ocs_node_transition(node, __ocs_d_wait_node_attach, NULL);
if (rc == OCS_HW_RTN_SUCCESS_SYNC) {
ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL);
}
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_topology_notify(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
int32_t rc;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SPORT_TOPOLOGY_NOTIFY: {
ocs_sport_topology_e topology = (ocs_sport_topology_e)(uintptr_t)arg;
ocs_assert(!node->sport->domain->attached, NULL);
ocs_assert(node->send_ls_acc == OCS_NODE_SEND_LS_ACC_PLOGI, NULL);
node_printf(node, "topology notification, topology=%d\n", topology);
if (topology == OCS_SPORT_TOPOLOGY_P2P) {
ocs_domain_attach(node->sport->domain, node->ls_acc_did);
}
ocs_node_transition(node, __ocs_d_wait_domain_attach, NULL);
break;
}
case OCS_EVT_DOMAIN_ATTACH_OK:
ocs_assert(node->sport->domain->attached, NULL);
node_printf(node, "domain attach ok\n");
rc = ocs_node_attach(node);
ocs_node_transition(node, __ocs_d_wait_node_attach, NULL);
if (rc == OCS_HW_RTN_SUCCESS_SYNC) {
ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL);
}
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_node_attach(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_NODE_ATTACH_OK:
node->attached = TRUE;
switch (node->send_ls_acc) {
case OCS_NODE_SEND_LS_ACC_PLOGI: {
ocs_send_plogi_acc(node->ls_acc_io, node->ls_acc_oxid, NULL, NULL);
ocs_node_transition(node, __ocs_d_wait_plogi_acc_cmpl, NULL);
node->send_ls_acc = OCS_NODE_SEND_LS_ACC_NONE;
node->ls_acc_io = NULL;
break;
}
case OCS_NODE_SEND_LS_ACC_PRLI: {
ocs_d_send_prli_rsp(node->ls_acc_io, node->ls_acc_oxid);
node->send_ls_acc = OCS_NODE_SEND_LS_ACC_NONE;
node->ls_acc_io = NULL;
break;
}
case OCS_NODE_SEND_LS_ACC_NONE:
default:
ocs_node_transition(node, __ocs_d_port_logged_in, NULL);
break;
}
break;
case OCS_EVT_NODE_ATTACH_FAIL:
node->attached = FALSE;
node_printf(node, "node attach failed\n");
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
break;
case OCS_EVT_SHUTDOWN:
node_printf(node, "%s received\n", ocs_sm_event_name(evt));
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_node_transition(node, __ocs_d_wait_attach_evt_shutdown, NULL);
break;
case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO:
node_printf(node, "%s received\n", ocs_sm_event_name(evt));
node->shutdown_reason = OCS_NODE_SHUTDOWN_EXPLICIT_LOGO;
ocs_node_transition(node, __ocs_d_wait_attach_evt_shutdown, NULL);
break;
case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO:
node_printf(node, "%s received\n", ocs_sm_event_name(evt));
node->shutdown_reason = OCS_NODE_SHUTDOWN_IMPLICIT_LOGO;
ocs_node_transition(node, __ocs_d_wait_attach_evt_shutdown, NULL);
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_attach_evt_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_NODE_ATTACH_OK:
node->attached = TRUE;
node_printf(node, "Attach evt=%s, proceed to shutdown\n", ocs_sm_event_name(evt));
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
break;
case OCS_EVT_NODE_ATTACH_FAIL:
node->attached = FALSE;
node_printf(node, "Attach evt=%s, proceed to shutdown\n", ocs_sm_event_name(evt));
ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL);
break;
case OCS_EVT_SHUTDOWN:
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO:
case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO:
node_printf(node, "%s received\n", ocs_sm_event_name(evt));
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_port_logged_in(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_node_cb_t *cbdata = arg;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
if (node->sport->enable_ini && !FC_ADDR_IS_DOMAIN_CTRL(node->rnode.fc_id)
&& !node->sent_prli) {
ocs_send_prli(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
node->sent_prli = TRUE;
}
break;
case OCS_EVT_FCP_CMD_RCVD: {
if (node->sport->enable_tgt) {
if (ocs_send_prlo(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT,
OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL)) {
ocs_node_transition(node, __ocs_d_wait_prlo_rsp, NULL);
}
}
break;
}
case OCS_EVT_PRLI_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_process_prli_payload(node, cbdata->payload->dma.virt);
ocs_d_send_prli_rsp(cbdata->io, ocs_be16toh(hdr->ox_id));
break;
}
case OCS_EVT_SRRS_ELS_REQ_OK: {
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PRLI, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_process_prli_payload(node, cbdata->els->els_rsp.virt);
ocs_node_transition(node, __ocs_d_device_ready, NULL);
break;
}
case OCS_EVT_SRRS_ELS_REQ_FAIL: {
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PRLI, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL);
break;
}
case OCS_EVT_SRRS_ELS_REQ_RJT: {
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PRLI, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
break;
}
case OCS_EVT_SRRS_ELS_CMPL_OK: {
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
break;
}
case OCS_EVT_PLOGI_RCVD: {
ocs_node_save_sparms(node, cbdata->payload->dma.virt);
ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI);
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_IMPLICIT_LOGO, NULL);
break;
}
case OCS_EVT_LOGO_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
node_printf(node, "%s received attached=%d\n", ocs_sm_event_name(evt), node->attached);
ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL);
break;
}
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_logo_acc_cmpl(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_CMPL_OK:
case OCS_EVT_SRRS_ELS_CMPL_FAIL:
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_device_ready(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_node_cb_t *cbdata = arg;
std_node_state_decl();
if (evt != OCS_EVT_FCP_CMD_RCVD) {
node_sm_trace();
}
switch(evt) {
case OCS_EVT_ENTER:
node->fcp_enabled = TRUE;
if (node->init) {
device_printf(ocs->dev, "[%s] found (initiator) WWPN %s WWNN %s\n", node->display_name,
node->wwpn, node->wwnn);
if (node->sport->enable_tgt)
ocs_scsi_new_initiator(node);
}
if (node->targ) {
device_printf(ocs->dev, "[%s] found (target) WWPN %s WWNN %s\n", node->display_name,
node->wwpn, node->wwnn);
if (node->sport->enable_ini)
ocs_scsi_new_target(node);
}
break;
case OCS_EVT_EXIT:
node->fcp_enabled = FALSE;
break;
case OCS_EVT_PLOGI_RCVD: {
ocs_node_save_sparms(node, cbdata->payload->dma.virt);
ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI);
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_IMPLICIT_LOGO, NULL);
break;
}
case OCS_EVT_PDISC_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_send_plogi_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
break;
}
case OCS_EVT_PRLI_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_process_prli_payload(node, cbdata->payload->dma.virt);
if (ocs->fc_type == node->fc_type)
ocs_send_prli_acc(cbdata->io, ocs_be16toh(hdr->ox_id), ocs->fc_type, NULL, NULL);
else
ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id), FC_REASON_UNABLE_TO_PERFORM,
FC_EXPL_REQUEST_NOT_SUPPORTED, 0, NULL, NULL);
break;
}
case OCS_EVT_PRLO_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
fc_prlo_payload_t *prlo = cbdata->payload->dma.virt;
if (ocs->fc_type == prlo->type)
ocs_send_prlo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), ocs->fc_type, NULL, NULL);
else
ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id), FC_REASON_UNABLE_TO_PERFORM,
FC_EXPL_REQUEST_NOT_SUPPORTED, 0, NULL, NULL);
break;
}
case OCS_EVT_LOGO_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
node_printf(node, "%s received attached=%d\n", ocs_sm_event_name(evt), node->attached);
ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL);
break;
}
case OCS_EVT_ADISC_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_send_adisc_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
break;
}
case OCS_EVT_RRQ_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_send_ls_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
break;
}
case OCS_EVT_ABTS_RCVD:
ocs_process_abts(cbdata->io, cbdata->header->dma.virt);
break;
case OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
break;
case OCS_EVT_NODE_REFOUND:
break;
case OCS_EVT_NODE_MISSING:
if (node->sport->enable_rscn) {
ocs_node_transition(node, __ocs_d_device_gone, NULL);
}
break;
case OCS_EVT_SRRS_ELS_CMPL_OK:
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
break;
case OCS_EVT_SRRS_ELS_CMPL_FAIL:
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
node_printf(node, "Failed to send PRLI LS_ACC\n");
break;
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_device_gone(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
int32_t rc = OCS_SCSI_CALL_COMPLETE;
int32_t rc_2 = OCS_SCSI_CALL_COMPLETE;
ocs_node_cb_t *cbdata = arg;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER: {
const char *labels[] = {"none", "initiator", "target", "initiator+target"};
device_printf(ocs->dev, "[%s] missing (%s) WWPN %s WWNN %s\n", node->display_name,
labels[(node->targ << 1) | (node->init)], node->wwpn, node->wwnn);
switch(ocs_node_get_enable(node)) {
case OCS_NODE_ENABLE_T_TO_T:
case OCS_NODE_ENABLE_I_TO_T:
case OCS_NODE_ENABLE_IT_TO_T:
rc = ocs_scsi_del_target(node, OCS_SCSI_TARGET_MISSING);
break;
case OCS_NODE_ENABLE_T_TO_I:
case OCS_NODE_ENABLE_I_TO_I:
case OCS_NODE_ENABLE_IT_TO_I:
rc = ocs_scsi_del_initiator(node, OCS_SCSI_INITIATOR_MISSING);
break;
case OCS_NODE_ENABLE_T_TO_IT:
rc = ocs_scsi_del_initiator(node, OCS_SCSI_INITIATOR_MISSING);
break;
case OCS_NODE_ENABLE_I_TO_IT:
rc = ocs_scsi_del_target(node, OCS_SCSI_TARGET_MISSING);
break;
case OCS_NODE_ENABLE_IT_TO_IT:
rc = ocs_scsi_del_initiator(node, OCS_SCSI_INITIATOR_MISSING);
rc_2 = ocs_scsi_del_target(node, OCS_SCSI_TARGET_MISSING);
break;
default:
rc = OCS_SCSI_CALL_COMPLETE;
break;
}
if ((rc == OCS_SCSI_CALL_COMPLETE) && (rc_2 == OCS_SCSI_CALL_COMPLETE)) {
ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL);
}
break;
}
case OCS_EVT_NODE_REFOUND:
ocs_send_adisc(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_d_wait_adisc_rsp, NULL);
break;
case OCS_EVT_PLOGI_RCVD: {
ocs_node_save_sparms(node, cbdata->payload->dma.virt);
ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI);
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_IMPLICIT_LOGO, NULL);
break;
}
case OCS_EVT_FCP_CMD_RCVD: {
node_printf(node, "FCP_CMND received, drop\n");
break;
}
case OCS_EVT_LOGO_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
node_printf(node, "%s received attached=%d\n", ocs_sm_event_name(evt), node->attached);
ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL);
break;
}
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_d_wait_adisc_rsp(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_node_cb_t *cbdata = arg;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_SRRS_ELS_REQ_OK:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_ADISC, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_node_transition(node, __ocs_d_device_ready, NULL);
break;
case OCS_EVT_SRRS_ELS_REQ_RJT:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_ADISC, __ocs_d_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
break;
case OCS_EVT_LOGO_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
node_printf(node, "%s received attached=%d\n", ocs_sm_event_name(evt), node->attached);
ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL);
break;
}
default:
__ocs_d_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}