#include "ocs.h"
#include "ocs_fabric.h"
#include "ocs_els.h"
#include "ocs_device.h"
static void ocs_fabric_initiate_shutdown(ocs_node_t *node);
static void * __ocs_fabric_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg);
static int32_t ocs_start_ns_node(ocs_sport_t *sport);
static int32_t ocs_start_fabctl_node(ocs_sport_t *sport);
static int32_t ocs_process_gidpt_payload(ocs_node_t *node, fcct_gidpt_acc_t *gidpt, uint32_t gidpt_len);
static void ocs_process_rscn(ocs_node_t *node, ocs_node_cb_t *cbdata);
static uint64_t ocs_get_wwpn(fc_plogi_payload_t *sp);
static void gidpt_delay_timer_cb(void *arg);
void *
__ocs_fabric_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_REENTER:
ocs_log_debug(node->ocs, ">>> reenter !!\n");
case OCS_EVT_ENTER:
ocs_send_flogi(node, OCS_FC_FLOGI_TIMEOUT_SEC, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_fabric_flogi_wait_rsp, NULL);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
break;
}
return NULL;
}
void
ocs_fabric_set_topology(ocs_node_t *node, ocs_sport_topology_e topology)
{
node->sport->topology = topology;
}
void
ocs_fabric_notify_topology(ocs_node_t *node)
{
ocs_node_t *tmp_node;
ocs_node_t *next;
ocs_sport_topology_e topology = node->sport->topology;
ocs_sport_lock(node->sport);
ocs_list_foreach_safe(&node->sport->node_list, tmp_node, next) {
if (tmp_node != node) {
ocs_node_post_event(tmp_node, OCS_EVT_SPORT_TOPOLOGY_NOTIFY, (void *)topology);
}
}
ocs_sport_unlock(node->sport);
}
void *
__ocs_fabric_flogi_wait_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_FLOGI, __ocs_fabric_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_domain_save_sparms(node->sport->domain, cbdata->els->els_rsp.virt);
ocs_display_sparams(node->display_name, "flogi rcvd resp", 0, NULL,
((uint8_t*)cbdata->els->els_rsp.virt) + 4);
if (ocs_rnode_is_nport(cbdata->els->els_rsp.virt)) {
ocs_fabric_set_topology(node, OCS_SPORT_TOPOLOGY_P2P);
if (ocs_p2p_setup(node->sport)) {
node_printf(node, "p2p setup failed, shutting down node\n");
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_fabric_initiate_shutdown(node);
} else {
if (node->sport->p2p_winner) {
ocs_node_transition(node, __ocs_p2p_wait_domain_attach, NULL);
if (!node->sport->domain->attached) {
node_printf(node, "p2p winner, domain not attached\n");
ocs_domain_attach(node->sport->domain, node->sport->p2p_port_id);
} else {
node_printf(node, "p2p winner, domain already attached\n");
ocs_node_post_event(node, OCS_EVT_DOMAIN_ATTACH_OK, NULL);
}
} else {
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_fabric_initiate_shutdown(node);
}
}
} else {
ocs_fabric_set_topology(node, OCS_SPORT_TOPOLOGY_FABRIC);
ocs_fabric_notify_topology(node);
ocs_assert(!node->sport->domain->attached, NULL);
ocs_domain_attach(node->sport->domain, cbdata->ext_status);
ocs_node_transition(node, __ocs_fabric_wait_domain_attach, NULL);
}
break;
}
case OCS_EVT_ELS_REQ_ABORTED:
case OCS_EVT_SRRS_ELS_REQ_RJT:
case OCS_EVT_SRRS_ELS_REQ_FAIL: {
ocs_sport_t *sport = node->sport;
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_FLOGI, __ocs_fabric_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
if (node->sport->topology == OCS_SPORT_TOPOLOGY_P2P && !node->sport->p2p_winner) {
node_printf(node, "FLOGI failed, peer p2p winner, shutdown node\n");
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_fabric_initiate_shutdown(node);
break;
}
node_printf(node, "FLOGI failed evt=%s, shutting down sport [%s]\n", ocs_sm_event_name(evt),
sport->display_name);
ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
break;
}
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
break;
}
return NULL;
}
void *
__ocs_vport_fabric_init(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_send_fdisc(node, OCS_FC_FLOGI_TIMEOUT_SEC, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_fabric_fdisc_wait_rsp, NULL);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
break;
}
return NULL;
}
void *
__ocs_fabric_fdisc_wait_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_FDISC, __ocs_fabric_common, __func__)) {
return NULL;
}
ocs_display_sparams(node->display_name, "fdisc rcvd resp", 0, NULL,
((uint8_t*)cbdata->els->els_rsp.virt) + 4);
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_sport_attach(node->sport, cbdata->ext_status);
ocs_node_transition(node, __ocs_fabric_wait_domain_attach, NULL);
break;
}
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_FDISC, __ocs_fabric_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_log_err(ocs, "FDISC failed, shutting down sport\n");
ocs_sm_post_event(&node->sport->sm, OCS_EVT_SHUTDOWN, NULL);
break;
}
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
break;
}
return NULL;
}
void *
__ocs_fabric_wait_domain_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_DOMAIN_ATTACH_OK:
case OCS_EVT_SPORT_ATTACH_OK: {
int rc;
rc = ocs_start_ns_node(node->sport);
if (rc)
return NULL;
if (node->sport->enable_rscn) {
rc = ocs_start_fabctl_node(node->sport);
if (rc)
return NULL;
}
ocs_node_transition(node, __ocs_fabric_idle, NULL);
break;
}
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_fabric_idle(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_DOMAIN_ATTACH_OK:
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_ns_init(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_send_plogi(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_ns_plogi_wait_rsp, NULL);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
break;
}
return NULL;
}
void *
__ocs_ns_plogi_wait_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_SRRS_ELS_REQ_OK: {
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_fabric_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_ns_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_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_ns_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;
ocs_ns_send_rftid(node, OCS_FC_ELS_CT_SEND_DEFAULT_TIMEOUT,
OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_ns_rftid_wait_rsp, NULL);
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_fabric_initiate_shutdown(node);
break;
case OCS_EVT_SHUTDOWN:
node_printf(node, "Shutdown event received\n");
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_node_transition(node, __ocs_fabric_wait_attach_evt_shutdown, NULL);
break;
case OCS_EVT_RSCN_RCVD:
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_fabric_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_fabric_initiate_shutdown(node);
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_fabric_initiate_shutdown(node);
break;
case OCS_EVT_SHUTDOWN:
node_printf(node, "Shutdown event received\n");
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_ns_rftid_wait_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_SRRS_ELS_REQ_OK:
if (node_check_ns_req(ctx, evt, arg, FC_GS_NAMESERVER_RFT_ID, __ocs_fabric_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_ns_send_rffid(node, OCS_FC_ELS_CT_SEND_DEFAULT_TIMEOUT,
OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_ns_rffid_wait_rsp, NULL);
break;
case OCS_EVT_RSCN_RCVD:
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_ns_rffid_wait_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_SRRS_ELS_REQ_OK: {
if (node_check_ns_req(ctx, evt, arg, FC_GS_NAMESERVER_RFF_ID, __ocs_fabric_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
if (node->sport->enable_rscn) {
ocs_ns_send_gidpt(node, OCS_FC_ELS_CT_SEND_DEFAULT_TIMEOUT,
OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_ns_gidpt_wait_rsp, NULL);
} else {
ocs_node_transition(node, __ocs_ns_idle, NULL);
}
break;
}
case OCS_EVT_RSCN_RCVD:
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_ns_gidpt_wait_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_ns_req(ctx, evt, arg, FC_GS_NAMESERVER_GID_PT, __ocs_fabric_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_process_gidpt_payload(node, cbdata->els->els_rsp.virt, cbdata->els->els_rsp.len);
ocs_node_transition(node, __ocs_ns_idle, NULL);
break;
}
case OCS_EVT_SRRS_ELS_REQ_FAIL: {
node_printf(node, "GID_PT failed to complete\n");
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_node_transition(node, __ocs_ns_idle, NULL);
break;
}
case OCS_EVT_RSCN_RCVD: {
node_printf(node, "RSCN received during GID_PT processing\n");
node->rscn_pending = 1;
break;
}
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_ns_idle(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:
if (!node->rscn_pending) {
break;
}
node_printf(node, "RSCN pending, restart discovery\n");
node->rscn_pending = 0;
case OCS_EVT_RSCN_RCVD: {
if ((ocs->tgt_rscn_delay_msec != 0) && !node->sport->enable_ini && node->sport->enable_tgt &&
enable_target_rscn(ocs)) {
ocs_node_transition(node, __ocs_ns_gidpt_delay, NULL);
} else {
ocs_ns_send_gidpt(node, OCS_FC_ELS_CT_SEND_DEFAULT_TIMEOUT,
OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_ns_gidpt_wait_rsp, NULL);
}
break;
}
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
break;
}
return NULL;
}
static void
gidpt_delay_timer_cb(void *arg)
{
ocs_node_t *node = arg;
int32_t rc;
ocs_del_timer(&node->gidpt_delay_timer);
rc = ocs_xport_control(node->ocs->xport, OCS_XPORT_POST_NODE_EVENT, node, OCS_EVT_GIDPT_DELAY_EXPIRED, NULL);
if (rc) {
ocs_log_err(node->ocs, "ocs_xport_control(OCS_XPORT_POST_NODE_EVENT) failed: %d\n", rc);
}
}
void *
__ocs_ns_gidpt_delay(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: {
time_t delay_msec;
ocs_assert(ocs->tgt_rscn_delay_msec != 0, NULL);
delay_msec = ocs->tgt_rscn_delay_msec;
if ((ocs_msectime() - node->time_last_gidpt_msec) < ocs->tgt_rscn_period_msec) {
delay_msec = ocs->tgt_rscn_period_msec;
}
ocs_setup_timer(ocs, &node->gidpt_delay_timer, gidpt_delay_timer_cb, node, delay_msec);
break;
}
case OCS_EVT_GIDPT_DELAY_EXPIRED:
node->time_last_gidpt_msec = ocs_msectime();
ocs_ns_send_gidpt(node, OCS_FC_ELS_CT_SEND_DEFAULT_TIMEOUT,
OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_ns_gidpt_wait_rsp, NULL);
break;
case OCS_EVT_RSCN_RCVD: {
ocs_log_debug(ocs, "RSCN received while in GIDPT delay - no action\n");
break;
}
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
break;
}
return NULL;
}
void *
__ocs_fabctl_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_node_t *node = ctx->app;
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_send_scr(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_fabctl_wait_scr_rsp, NULL);
break;
case OCS_EVT_NODE_ATTACH_OK:
node->attached = TRUE;
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_fabctl_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;
ocs_send_scr(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_fabctl_wait_scr_rsp, NULL);
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_fabric_initiate_shutdown(node);
break;
case OCS_EVT_SHUTDOWN:
node_printf(node, "Shutdown event received\n");
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_node_transition(node, __ocs_fabric_wait_attach_evt_shutdown, NULL);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_fabctl_wait_scr_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_SRRS_ELS_REQ_OK:
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_SCR, __ocs_fabric_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
ocs_node_transition(node, __ocs_fabctl_ready, NULL);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_fabctl_ready(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_RSCN_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_process_rscn(node, cbdata);
ocs_send_ls_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
ocs_node_transition(node, __ocs_fabctl_wait_ls_acc_cmpl, NULL);
break;
}
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_fabctl_wait_ls_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:
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
ocs_node_transition(node, __ocs_fabctl_ready, NULL);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
static void
ocs_fabric_initiate_shutdown(ocs_node_t *node)
{
ocs_hw_rtn_e rc;
ocs_t *ocs = node->ocs;
ocs_scsi_io_alloc_disable(node);
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);
}
}
ocs_node_initiate_cleanup(node);
}
static void *
__ocs_fabric_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_node_t *node = NULL;
ocs_assert(ctx, NULL);
ocs_assert(ctx->app, NULL);
node = ctx->app;
switch(evt) {
case OCS_EVT_DOMAIN_ATTACH_OK:
break;
case OCS_EVT_SHUTDOWN:
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_fabric_initiate_shutdown(node);
break;
default:
__ocs_node_common(funcname, ctx, evt, arg);
break;
}
return NULL;
}
int32_t
ocs_rnode_is_nport(fc_plogi_payload_t *remote_sparms)
{
return (ocs_be32toh(remote_sparms->common_service_parameters[1]) & (1U << 28)) == 0;
}
static uint64_t
ocs_get_wwpn(fc_plogi_payload_t *sp)
{
return (((uint64_t)ocs_be32toh(sp->port_name_hi) << 32ll) | (ocs_be32toh(sp->port_name_lo)));
}
static int32_t
ocs_rnode_is_winner(ocs_sport_t *sport)
{
fc_plogi_payload_t *remote_sparms = (fc_plogi_payload_t*) sport->domain->flogi_service_params;
uint64_t remote_wwpn = ocs_get_wwpn(remote_sparms);
uint64_t local_wwpn = sport->wwpn;
char prop_buf[32];
uint64_t wwn_bump = 0;
if (ocs_get_property("wwn_bump", prop_buf, sizeof(prop_buf)) == 0) {
wwn_bump = ocs_strtoull(prop_buf, 0, 0);
}
local_wwpn ^= wwn_bump;
remote_wwpn = ocs_get_wwpn(remote_sparms);
ocs_log_debug(sport->ocs, "r: %08x %08x\n", ocs_be32toh(remote_sparms->port_name_hi), ocs_be32toh(remote_sparms->port_name_lo));
ocs_log_debug(sport->ocs, "l: %08x %08x\n", (uint32_t) (local_wwpn >> 32ll), (uint32_t) local_wwpn);
if (remote_wwpn == local_wwpn) {
ocs_log_warn(sport->ocs, "WWPN of remote node [%08x %08x] matches local WWPN\n",
(uint32_t) (local_wwpn >> 32ll), (uint32_t) local_wwpn);
return (-1);
}
return (remote_wwpn > local_wwpn);
}
void *
__ocs_p2p_wait_domain_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_DOMAIN_ATTACH_OK: {
ocs_sport_t *sport = node->sport;
ocs_node_t *rnode;
ocs_assert (node->sport->p2p_winner, NULL);
rnode = ocs_node_find(sport, node->sport->p2p_remote_port_id);
if (rnode != NULL) {
node_printf(node, "Node with fc_id x%x already exists\n", rnode->rnode.fc_id);
ocs_assert (rnode != node, NULL);
} else {
rnode = ocs_node_alloc(sport, sport->p2p_remote_port_id, FALSE, FALSE);
if (rnode == NULL) {
ocs_log_err(ocs, "node alloc failed\n");
return NULL;
}
ocs_fabric_notify_topology(node);
ocs_node_transition(rnode, __ocs_p2p_rnode_init, NULL);
}
if (node->rnode.fc_id == 0) {
ocs_node_init_device(node, FALSE);
} else {
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_fabric_initiate_shutdown(node);
}
break;
}
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_p2p_rnode_init(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:
ocs_send_plogi(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL);
ocs_node_transition(node, __ocs_p2p_wait_plogi_rsp, NULL);
break;
case OCS_EVT_ABTS_RCVD:
ocs_bls_send_acc_hdr(cbdata->io, cbdata->header->dma.virt);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_p2p_wait_flogi_acc_cmpl(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:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_CMPL_OK:
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
if (node->sport->p2p_winner) {
ocs_node_transition(node, __ocs_p2p_wait_domain_attach, NULL);
if (node->sport->domain->attached &&
!(node->sport->domain->domain_notify_pend)) {
node_printf(node, "Domain already attached\n");
ocs_node_post_event(node, OCS_EVT_DOMAIN_ATTACH_OK, NULL);
}
} else {
ocs_node_init_device(node, FALSE);
}
break;
case OCS_EVT_SRRS_ELS_CMPL_FAIL:
node_printf(node, "FLOGI LS_ACC failed, shutting down\n");
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_fabric_initiate_shutdown(node);
break;
case OCS_EVT_ABTS_RCVD: {
ocs_bls_send_acc_hdr(cbdata->io, cbdata->header->dma.virt);
break;
}
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_p2p_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_SRRS_ELS_REQ_OK: {
if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_fabric_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);
rc = ocs_node_attach(node);
ocs_node_transition(node, __ocs_p2p_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_fabric_common, __func__)) {
return NULL;
}
node_printf(node, "PLOGI failed, shutting down\n");
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_fabric_initiate_shutdown(node);
break;
}
case OCS_EVT_PLOGI_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
if (node->ocs->external_loopback) {
ocs_send_plogi_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL);
break;
} else{
__ocs_fabric_common(__func__, ctx, evt, arg);
}
break;
}
case OCS_EVT_PRLI_RCVD:
ocs_process_prli_payload(node, cbdata->payload->dma.virt);
ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PRLI);
ocs_node_transition(node, __ocs_p2p_wait_plogi_rsp_recvd_prli, NULL);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_p2p_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_fabric_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_p2p_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_fabric_common, __func__)) {
return NULL;
}
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
ocs_fabric_initiate_shutdown(node);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_p2p_wait_node_attach(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:
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_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_PLOGI:
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_fabric_initiate_shutdown(node);
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_fabric_wait_attach_evt_shutdown, NULL);
break;
case OCS_EVT_PRLI_RCVD:
node_printf(node, "%s: PRLI received before node is attached\n", ocs_sm_event_name(evt));
ocs_process_prli_payload(node, cbdata->payload->dma.virt);
ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PRLI);
break;
default:
__ocs_fabric_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
static int32_t
ocs_start_ns_node(ocs_sport_t *sport)
{
ocs_node_t *ns;
ns = ocs_node_find(sport, FC_ADDR_NAMESERVER);
if (ns == NULL) {
ns = ocs_node_alloc(sport, FC_ADDR_NAMESERVER, FALSE, FALSE);
if (ns == NULL) {
return -1;
}
}
if (ns->ocs->nodedb_mask & OCS_NODEDB_PAUSE_NAMESERVER) {
ocs_node_pause(ns, __ocs_ns_init);
} else {
ocs_node_transition(ns, __ocs_ns_init, NULL);
}
return 0;
}
static int32_t
ocs_start_fabctl_node(ocs_sport_t *sport)
{
ocs_node_t *fabctl;
fabctl = ocs_node_find(sport, FC_ADDR_CONTROLLER);
if (fabctl == NULL) {
fabctl = ocs_node_alloc(sport, FC_ADDR_CONTROLLER, FALSE, FALSE);
if (fabctl == NULL) {
return -1;
}
}
ocs_node_transition(fabctl, __ocs_fabctl_init, NULL);
return 0;
}
static int32_t
ocs_process_gidpt_payload(ocs_node_t *node, fcct_gidpt_acc_t *gidpt, uint32_t gidpt_len)
{
uint32_t i;
uint32_t j;
ocs_node_t *newnode;
ocs_sport_t *sport = node->sport;
ocs_t *ocs = node->ocs;
uint32_t port_id;
uint32_t port_count;
ocs_node_t *n;
ocs_node_t **active_nodes;
uint32_t portlist_count;
uint16_t residual;
residual = ocs_be16toh(gidpt->hdr.max_residual_size);
if (residual != 0) {
ocs_log_debug(node->ocs, "residual is %u words\n", residual);
}
if (ocs_be16toh(gidpt->hdr.cmd_rsp_code) == FCCT_HDR_CMDRSP_REJECT) {
node_printf(node, "GIDPT request failed: rsn x%x rsn_expl x%x\n",
gidpt->hdr.reason_code, gidpt->hdr.reason_code_explanation);
return -1;
}
portlist_count = (gidpt_len - sizeof(fcct_iu_header_t)) / sizeof(gidpt->port_list);
port_count = 0;
ocs_sport_lock(sport);
ocs_list_foreach(&sport->node_list, n) {
port_count ++;
}
active_nodes = ocs_malloc(node->ocs, port_count * sizeof(*active_nodes), OCS_M_NOWAIT | OCS_M_ZERO);
if (active_nodes == NULL) {
node_printf(node, "ocs_malloc failed\n");
ocs_sport_unlock(sport);
return -1;
}
i = 0;
ocs_list_foreach(&sport->node_list, n) {
port_id = n->rnode.fc_id;
switch (port_id) {
case FC_ADDR_FABRIC:
case FC_ADDR_CONTROLLER:
case FC_ADDR_NAMESERVER:
break;
default:
if (!FC_ADDR_IS_DOMAIN_CTRL(port_id)) {
active_nodes[i++] = n;
}
break;
}
}
for (i = 0; i < portlist_count; i ++) {
port_id = fc_be24toh(gidpt->port_list[i].port_id);
for (j = 0; j < port_count; j ++) {
if ((active_nodes[j] != NULL) && (port_id == active_nodes[j]->rnode.fc_id)) {
active_nodes[j] = NULL;
}
}
if (gidpt->port_list[i].ctl & FCCT_GID_PT_LAST_ID)
break;
}
for (i = 0; i < port_count; i ++) {
if (active_nodes[i] != NULL) {
if ((node->sport->enable_ini && active_nodes[i]->targ) ||
(node->sport->enable_tgt && enable_target_rscn(ocs))) {
ocs_node_post_event(active_nodes[i], OCS_EVT_NODE_MISSING, NULL);
} else {
node_printf(node, "GID_PT: skipping non-tgt port_id x%06x\n",
active_nodes[i]->rnode.fc_id);
}
}
}
ocs_free(ocs, active_nodes, port_count * sizeof(*active_nodes));
for(i = 0; i < portlist_count; i ++) {
uint32_t port_id = fc_be24toh(gidpt->port_list[i].port_id);
if (port_id != node->rnode.sport->fc_id && !ocs_sport_find(sport->domain, port_id)) {
newnode = ocs_node_find(sport, port_id);
if (newnode) {
if (node->sport->enable_ini && newnode->targ) {
ocs_node_post_event(newnode, OCS_EVT_NODE_REFOUND, NULL);
}
} else {
if (node->sport->enable_ini) {
newnode = ocs_node_alloc(sport, port_id, 0, 0);
if (newnode == NULL) {
ocs_log_err(ocs, "ocs_node_alloc() failed\n");
ocs_sport_unlock(sport);
return -1;
}
ocs_node_init_device(newnode, TRUE);
}
}
}
if (gidpt->port_list[i].ctl & FCCT_GID_PT_LAST_ID) {
break;
}
}
ocs_sport_unlock(sport);
return 0;
}
int32_t
ocs_p2p_setup(ocs_sport_t *sport)
{
ocs_t *ocs = sport->ocs;
int32_t rnode_winner;
rnode_winner = ocs_rnode_is_winner(sport);
if (rnode_winner == 1) {
sport->p2p_remote_port_id = 0;
sport->p2p_port_id = 0;
sport->p2p_winner = FALSE;
} else if (rnode_winner == 0) {
sport->p2p_remote_port_id = 2;
sport->p2p_port_id = 1;
sport->p2p_winner = TRUE;
} else {
if (sport->ocs->external_loopback) {
ocs_log_debug(ocs, "External loopback mode enabled\n");
sport->p2p_remote_port_id = 1;
sport->p2p_port_id = 1;
sport->p2p_winner = TRUE;
} else {
ocs_log_warn(ocs, "failed to determine p2p winner\n");
return rnode_winner;
}
}
return 0;
}
static void
ocs_process_rscn(ocs_node_t *node, ocs_node_cb_t *cbdata)
{
ocs_t *ocs = node->ocs;
ocs_sport_t *sport = node->sport;
ocs_node_t *ns;
ns = ocs_node_find(sport, FC_ADDR_NAMESERVER);
if (ns != NULL) {
ocs_node_post_event(ns, OCS_EVT_RSCN_RCVD, cbdata);
} else {
ocs_log_warn(ocs, "can't find name server node\n");
}
}