forked from Qortal/Brooklyn
Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey!
1564 lines
36 KiB
C
1564 lines
36 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
|
|
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
|
|
*/
|
|
|
|
/*
|
|
* This file implements remote node state machines for:
|
|
* - Fabric logins.
|
|
* - Fabric controller events.
|
|
* - Name/directory services interaction.
|
|
* - Point-to-point logins.
|
|
*/
|
|
|
|
/*
|
|
* fabric_sm Node State Machine: Fabric States
|
|
* ns_sm Node State Machine: Name/Directory Services States
|
|
* p2p_sm Node State Machine: Point-to-Point Node States
|
|
*/
|
|
|
|
#include "efc.h"
|
|
|
|
static void
|
|
efc_fabric_initiate_shutdown(struct efc_node *node)
|
|
{
|
|
struct efc *efc = node->efc;
|
|
|
|
node->els_io_enabled = false;
|
|
|
|
if (node->attached) {
|
|
int rc;
|
|
|
|
/* issue hw node free; don't care if succeeds right away
|
|
* or sometime later, will check node->attached later in
|
|
* shutdown process
|
|
*/
|
|
rc = efc_cmd_node_detach(efc, &node->rnode);
|
|
if (rc < 0) {
|
|
node_printf(node, "Failed freeing HW node, rc=%d\n",
|
|
rc);
|
|
}
|
|
}
|
|
/*
|
|
* node has either been detached or is in the process of being detached,
|
|
* call common node's initiate cleanup function
|
|
*/
|
|
efc_node_initiate_cleanup(node);
|
|
}
|
|
|
|
static void
|
|
__efc_fabric_common(const char *funcname, struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = NULL;
|
|
|
|
node = ctx->app;
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_DOMAIN_ATTACH_OK:
|
|
break;
|
|
case EFC_EVT_SHUTDOWN:
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_fabric_initiate_shutdown(node);
|
|
break;
|
|
|
|
default:
|
|
/* call default event handler common to all nodes */
|
|
__efc_node_common(funcname, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_fabric_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
|
|
void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
struct efc *efc = node->efc;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_REENTER:
|
|
efc_log_debug(efc, ">>> reenter !!\n");
|
|
fallthrough;
|
|
|
|
case EFC_EVT_ENTER:
|
|
/* send FLOGI */
|
|
efc_send_flogi(node);
|
|
efc_node_transition(node, __efc_fabric_flogi_wait_rsp, NULL);
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
efc_fabric_set_topology(struct efc_node *node,
|
|
enum efc_nport_topology topology)
|
|
{
|
|
node->nport->topology = topology;
|
|
}
|
|
|
|
void
|
|
efc_fabric_notify_topology(struct efc_node *node)
|
|
{
|
|
struct efc_node *tmp_node;
|
|
unsigned long index;
|
|
|
|
/*
|
|
* now loop through the nodes in the nport
|
|
* and send topology notification
|
|
*/
|
|
xa_for_each(&node->nport->lookup, index, tmp_node) {
|
|
if (tmp_node != node) {
|
|
efc_node_post_event(tmp_node,
|
|
EFC_EVT_NPORT_TOPOLOGY_NOTIFY,
|
|
&node->nport->topology);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool efc_rnode_is_nport(struct fc_els_flogi *rsp)
|
|
{
|
|
return !(ntohs(rsp->fl_csp.sp_features) & FC_SP_FT_FPORT);
|
|
}
|
|
|
|
void
|
|
__efc_fabric_flogi_wait_rsp(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_SRRS_ELS_REQ_OK: {
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_FLOGI,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
|
|
memcpy(node->nport->domain->flogi_service_params,
|
|
cbdata->els_rsp.virt,
|
|
sizeof(struct fc_els_flogi));
|
|
|
|
/* Check to see if the fabric is an F_PORT or and N_PORT */
|
|
if (!efc_rnode_is_nport(cbdata->els_rsp.virt)) {
|
|
/* sm: if not nport / efc_domain_attach */
|
|
/* ext_status has the fc_id, attach domain */
|
|
efc_fabric_set_topology(node, EFC_NPORT_TOPO_FABRIC);
|
|
efc_fabric_notify_topology(node);
|
|
WARN_ON(node->nport->domain->attached);
|
|
efc_domain_attach(node->nport->domain,
|
|
cbdata->ext_status);
|
|
efc_node_transition(node,
|
|
__efc_fabric_wait_domain_attach,
|
|
NULL);
|
|
break;
|
|
}
|
|
|
|
/* sm: if nport and p2p_winner / efc_domain_attach */
|
|
efc_fabric_set_topology(node, EFC_NPORT_TOPO_P2P);
|
|
if (efc_p2p_setup(node->nport)) {
|
|
node_printf(node,
|
|
"p2p setup failed, shutting down node\n");
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_fabric_initiate_shutdown(node);
|
|
break;
|
|
}
|
|
|
|
if (node->nport->p2p_winner) {
|
|
efc_node_transition(node,
|
|
__efc_p2p_wait_domain_attach,
|
|
NULL);
|
|
if (node->nport->domain->attached &&
|
|
!node->nport->domain->domain_notify_pend) {
|
|
/*
|
|
* already attached,
|
|
* just send ATTACH_OK
|
|
*/
|
|
node_printf(node,
|
|
"p2p winner, domain already attached\n");
|
|
efc_node_post_event(node,
|
|
EFC_EVT_DOMAIN_ATTACH_OK,
|
|
NULL);
|
|
}
|
|
} else {
|
|
/*
|
|
* peer is p2p winner;
|
|
* PLOGI will be received on the
|
|
* remote SID=1 node;
|
|
* this node has served its purpose
|
|
*/
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_fabric_initiate_shutdown(node);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case EFC_EVT_ELS_REQ_ABORTED:
|
|
case EFC_EVT_SRRS_ELS_REQ_RJT:
|
|
case EFC_EVT_SRRS_ELS_REQ_FAIL: {
|
|
struct efc_nport *nport = node->nport;
|
|
/*
|
|
* with these errors, we have no recovery,
|
|
* so shutdown the nport, leave the link
|
|
* up and the domain ready
|
|
*/
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_FLOGI,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
node_printf(node,
|
|
"FLOGI failed evt=%s, shutting down nport [%s]\n",
|
|
efc_sm_event_name(evt), nport->display_name);
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_vport_fabric_init(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
/* sm: / send FDISC */
|
|
efc_send_fdisc(node);
|
|
efc_node_transition(node, __efc_fabric_fdisc_wait_rsp, NULL);
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_fabric_fdisc_wait_rsp(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_SRRS_ELS_REQ_OK: {
|
|
/* fc_id is in ext_status */
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_FDISC,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
/* sm: / efc_nport_attach */
|
|
efc_nport_attach(node->nport, cbdata->ext_status);
|
|
efc_node_transition(node, __efc_fabric_wait_domain_attach,
|
|
NULL);
|
|
break;
|
|
}
|
|
|
|
case EFC_EVT_SRRS_ELS_REQ_RJT:
|
|
case EFC_EVT_SRRS_ELS_REQ_FAIL: {
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_FDISC,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
efc_log_err(node->efc, "FDISC failed, shutting down nport\n");
|
|
/* sm: / shutdown nport */
|
|
efc_sm_post_event(&node->nport->sm, EFC_EVT_SHUTDOWN, NULL);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
static int
|
|
efc_start_ns_node(struct efc_nport *nport)
|
|
{
|
|
struct efc_node *ns;
|
|
|
|
/* Instantiate a name services node */
|
|
ns = efc_node_find(nport, FC_FID_DIR_SERV);
|
|
if (!ns) {
|
|
ns = efc_node_alloc(nport, FC_FID_DIR_SERV, false, false);
|
|
if (!ns)
|
|
return -EIO;
|
|
}
|
|
/*
|
|
* for found ns, should we be transitioning from here?
|
|
* breaks transition only
|
|
* 1. from within state machine or
|
|
* 2. if after alloc
|
|
*/
|
|
if (ns->efc->nodedb_mask & EFC_NODEDB_PAUSE_NAMESERVER)
|
|
efc_node_pause(ns, __efc_ns_init);
|
|
else
|
|
efc_node_transition(ns, __efc_ns_init, NULL);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
efc_start_fabctl_node(struct efc_nport *nport)
|
|
{
|
|
struct efc_node *fabctl;
|
|
|
|
fabctl = efc_node_find(nport, FC_FID_FCTRL);
|
|
if (!fabctl) {
|
|
fabctl = efc_node_alloc(nport, FC_FID_FCTRL,
|
|
false, false);
|
|
if (!fabctl)
|
|
return -EIO;
|
|
}
|
|
/*
|
|
* for found ns, should we be transitioning from here?
|
|
* breaks transition only
|
|
* 1. from within state machine or
|
|
* 2. if after alloc
|
|
*/
|
|
efc_node_transition(fabctl, __efc_fabctl_init, NULL);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
__efc_fabric_wait_domain_attach(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
efc_node_hold_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_EXIT:
|
|
efc_node_accept_frames(node);
|
|
break;
|
|
case EFC_EVT_DOMAIN_ATTACH_OK:
|
|
case EFC_EVT_NPORT_ATTACH_OK: {
|
|
int rc;
|
|
|
|
rc = efc_start_ns_node(node->nport);
|
|
if (rc)
|
|
return;
|
|
|
|
/* sm: if enable_ini / start fabctl node */
|
|
/* Instantiate the fabric controller (sends SCR) */
|
|
if (node->nport->enable_rscn) {
|
|
rc = efc_start_fabctl_node(node->nport);
|
|
if (rc)
|
|
return;
|
|
}
|
|
efc_node_transition(node, __efc_fabric_idle, NULL);
|
|
break;
|
|
}
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_fabric_idle(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
|
|
void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_DOMAIN_ATTACH_OK:
|
|
break;
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_ns_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
/* sm: / send PLOGI */
|
|
efc_send_plogi(node);
|
|
efc_node_transition(node, __efc_ns_plogi_wait_rsp, NULL);
|
|
break;
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_ns_plogi_wait_rsp(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_SRRS_ELS_REQ_OK: {
|
|
int rc;
|
|
|
|
/* Save service parameters */
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
/* sm: / save sparams, efc_node_attach */
|
|
efc_node_save_sparms(node, cbdata->els_rsp.virt);
|
|
rc = efc_node_attach(node);
|
|
efc_node_transition(node, __efc_ns_wait_node_attach, NULL);
|
|
if (rc < 0)
|
|
efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL,
|
|
NULL);
|
|
break;
|
|
}
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_ns_wait_node_attach(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
efc_node_hold_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_EXIT:
|
|
efc_node_accept_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_NODE_ATTACH_OK:
|
|
node->attached = true;
|
|
/* sm: / send RFTID */
|
|
efc_ns_send_rftid(node);
|
|
efc_node_transition(node, __efc_ns_rftid_wait_rsp, NULL);
|
|
break;
|
|
|
|
case EFC_EVT_NODE_ATTACH_FAIL:
|
|
/* node attach failed, shutdown the node */
|
|
node->attached = false;
|
|
node_printf(node, "Node attach failed\n");
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_fabric_initiate_shutdown(node);
|
|
break;
|
|
|
|
case EFC_EVT_SHUTDOWN:
|
|
node_printf(node, "Shutdown event received\n");
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_node_transition(node,
|
|
__efc_fabric_wait_attach_evt_shutdown,
|
|
NULL);
|
|
break;
|
|
|
|
/*
|
|
* if receive RSCN just ignore,
|
|
* we haven't sent GID_PT yet (ACC sent by fabctl node)
|
|
*/
|
|
case EFC_EVT_RSCN_RCVD:
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_fabric_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
efc_node_hold_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_EXIT:
|
|
efc_node_accept_frames(node);
|
|
break;
|
|
|
|
/* wait for any of these attach events and then shutdown */
|
|
case EFC_EVT_NODE_ATTACH_OK:
|
|
node->attached = true;
|
|
node_printf(node, "Attach evt=%s, proceed to shutdown\n",
|
|
efc_sm_event_name(evt));
|
|
efc_fabric_initiate_shutdown(node);
|
|
break;
|
|
|
|
case EFC_EVT_NODE_ATTACH_FAIL:
|
|
node->attached = false;
|
|
node_printf(node, "Attach evt=%s, proceed to shutdown\n",
|
|
efc_sm_event_name(evt));
|
|
efc_fabric_initiate_shutdown(node);
|
|
break;
|
|
|
|
/* ignore shutdown event as we're already in shutdown path */
|
|
case EFC_EVT_SHUTDOWN:
|
|
node_printf(node, "Shutdown event received\n");
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_ns_rftid_wait_rsp(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_SRRS_ELS_REQ_OK:
|
|
if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_RFT_ID,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
/* sm: / send RFFID */
|
|
efc_ns_send_rffid(node);
|
|
efc_node_transition(node, __efc_ns_rffid_wait_rsp, NULL);
|
|
break;
|
|
|
|
/*
|
|
* if receive RSCN just ignore,
|
|
* we haven't sent GID_PT yet (ACC sent by fabctl node)
|
|
*/
|
|
case EFC_EVT_RSCN_RCVD:
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_ns_rffid_wait_rsp(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
/*
|
|
* Waits for an RFFID response event;
|
|
* if rscn enabled, a GIDPT name services request is issued.
|
|
*/
|
|
switch (evt) {
|
|
case EFC_EVT_SRRS_ELS_REQ_OK: {
|
|
if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_RFF_ID,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
if (node->nport->enable_rscn) {
|
|
/* sm: if enable_rscn / send GIDPT */
|
|
efc_ns_send_gidpt(node);
|
|
|
|
efc_node_transition(node, __efc_ns_gidpt_wait_rsp,
|
|
NULL);
|
|
} else {
|
|
/* if 'T' only, we're done, go to idle */
|
|
efc_node_transition(node, __efc_ns_idle, NULL);
|
|
}
|
|
break;
|
|
}
|
|
/*
|
|
* if receive RSCN just ignore,
|
|
* we haven't sent GID_PT yet (ACC sent by fabctl node)
|
|
*/
|
|
case EFC_EVT_RSCN_RCVD:
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
static int
|
|
efc_process_gidpt_payload(struct efc_node *node,
|
|
void *data, u32 gidpt_len)
|
|
{
|
|
u32 i, j;
|
|
struct efc_node *newnode;
|
|
struct efc_nport *nport = node->nport;
|
|
struct efc *efc = node->efc;
|
|
u32 port_id = 0, port_count, plist_count;
|
|
struct efc_node *n;
|
|
struct efc_node **active_nodes;
|
|
int residual;
|
|
struct {
|
|
struct fc_ct_hdr hdr;
|
|
struct fc_gid_pn_resp pn_rsp;
|
|
} *rsp;
|
|
struct fc_gid_pn_resp *gidpt;
|
|
unsigned long index;
|
|
|
|
rsp = data;
|
|
gidpt = &rsp->pn_rsp;
|
|
residual = be16_to_cpu(rsp->hdr.ct_mr_size);
|
|
|
|
if (residual != 0)
|
|
efc_log_debug(node->efc, "residual is %u words\n", residual);
|
|
|
|
if (be16_to_cpu(rsp->hdr.ct_cmd) == FC_FS_RJT) {
|
|
node_printf(node,
|
|
"GIDPT request failed: rsn x%x rsn_expl x%x\n",
|
|
rsp->hdr.ct_reason, rsp->hdr.ct_explan);
|
|
return -EIO;
|
|
}
|
|
|
|
plist_count = (gidpt_len - sizeof(struct fc_ct_hdr)) / sizeof(*gidpt);
|
|
|
|
/* Count the number of nodes */
|
|
port_count = 0;
|
|
xa_for_each(&nport->lookup, index, n) {
|
|
port_count++;
|
|
}
|
|
|
|
/* Allocate a buffer for all nodes */
|
|
active_nodes = kzalloc(port_count * sizeof(*active_nodes), GFP_ATOMIC);
|
|
if (!active_nodes) {
|
|
node_printf(node, "efc_malloc failed\n");
|
|
return -EIO;
|
|
}
|
|
|
|
/* Fill buffer with fc_id of active nodes */
|
|
i = 0;
|
|
xa_for_each(&nport->lookup, index, n) {
|
|
port_id = n->rnode.fc_id;
|
|
switch (port_id) {
|
|
case FC_FID_FLOGI:
|
|
case FC_FID_FCTRL:
|
|
case FC_FID_DIR_SERV:
|
|
break;
|
|
default:
|
|
if (port_id != FC_FID_DOM_MGR)
|
|
active_nodes[i++] = n;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* update the active nodes buffer */
|
|
for (i = 0; i < plist_count; i++) {
|
|
hton24(gidpt[i].fp_fid, port_id);
|
|
|
|
for (j = 0; j < port_count; j++) {
|
|
if (active_nodes[j] &&
|
|
port_id == active_nodes[j]->rnode.fc_id) {
|
|
active_nodes[j] = NULL;
|
|
}
|
|
}
|
|
|
|
if (gidpt[i].fp_resvd & FC_NS_FID_LAST)
|
|
break;
|
|
}
|
|
|
|
/* Those remaining in the active_nodes[] are now gone ! */
|
|
for (i = 0; i < port_count; i++) {
|
|
/*
|
|
* if we're an initiator and the remote node
|
|
* is a target, then post the node missing event.
|
|
* if we're target and we have enabled
|
|
* target RSCN, then post the node missing event.
|
|
*/
|
|
if (!active_nodes[i])
|
|
continue;
|
|
|
|
if ((node->nport->enable_ini && active_nodes[i]->targ) ||
|
|
(node->nport->enable_tgt && enable_target_rscn(efc))) {
|
|
efc_node_post_event(active_nodes[i],
|
|
EFC_EVT_NODE_MISSING, NULL);
|
|
} else {
|
|
node_printf(node,
|
|
"GID_PT: skipping non-tgt port_id x%06x\n",
|
|
active_nodes[i]->rnode.fc_id);
|
|
}
|
|
}
|
|
kfree(active_nodes);
|
|
|
|
for (i = 0; i < plist_count; i++) {
|
|
hton24(gidpt[i].fp_fid, port_id);
|
|
|
|
/* Don't create node for ourselves */
|
|
if (port_id == node->rnode.nport->fc_id) {
|
|
if (gidpt[i].fp_resvd & FC_NS_FID_LAST)
|
|
break;
|
|
continue;
|
|
}
|
|
|
|
newnode = efc_node_find(nport, port_id);
|
|
if (!newnode) {
|
|
if (!node->nport->enable_ini)
|
|
continue;
|
|
|
|
newnode = efc_node_alloc(nport, port_id, false, false);
|
|
if (!newnode) {
|
|
efc_log_err(efc, "efc_node_alloc() failed\n");
|
|
return -EIO;
|
|
}
|
|
/*
|
|
* send PLOGI automatically
|
|
* if initiator
|
|
*/
|
|
efc_node_init_device(newnode, true);
|
|
}
|
|
|
|
if (node->nport->enable_ini && newnode->targ) {
|
|
efc_node_post_event(newnode, EFC_EVT_NODE_REFOUND,
|
|
NULL);
|
|
}
|
|
|
|
if (gidpt[i].fp_resvd & FC_NS_FID_LAST)
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
__efc_ns_gidpt_wait_rsp(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
/*
|
|
* Wait for a GIDPT response from the name server. Process the FC_IDs
|
|
* that are reported by creating new remote ports, as needed.
|
|
*/
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_SRRS_ELS_REQ_OK: {
|
|
if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_GID_PT,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
/* sm: / process GIDPT payload */
|
|
efc_process_gidpt_payload(node, cbdata->els_rsp.virt,
|
|
cbdata->els_rsp.len);
|
|
efc_node_transition(node, __efc_ns_idle, NULL);
|
|
break;
|
|
}
|
|
|
|
case EFC_EVT_SRRS_ELS_REQ_FAIL: {
|
|
/* not much we can do; will retry with the next RSCN */
|
|
node_printf(node, "GID_PT failed to complete\n");
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
efc_node_transition(node, __efc_ns_idle, NULL);
|
|
break;
|
|
}
|
|
|
|
/* if receive RSCN here, queue up another discovery processing */
|
|
case EFC_EVT_RSCN_RCVD: {
|
|
node_printf(node, "RSCN received during GID_PT processing\n");
|
|
node->rscn_pending = true;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_ns_idle(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
struct efc *efc = node->efc;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
/*
|
|
* Wait for RSCN received events (posted from the fabric controller)
|
|
* and restart the GIDPT name services query and processing.
|
|
*/
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
if (!node->rscn_pending)
|
|
break;
|
|
|
|
node_printf(node, "RSCN pending, restart discovery\n");
|
|
node->rscn_pending = false;
|
|
fallthrough;
|
|
|
|
case EFC_EVT_RSCN_RCVD: {
|
|
/* sm: / send GIDPT */
|
|
/*
|
|
* If target RSCN processing is enabled,
|
|
* and this is target only (not initiator),
|
|
* and tgt_rscn_delay is non-zero,
|
|
* then we delay issuing the GID_PT
|
|
*/
|
|
if (efc->tgt_rscn_delay_msec != 0 &&
|
|
!node->nport->enable_ini && node->nport->enable_tgt &&
|
|
enable_target_rscn(efc)) {
|
|
efc_node_transition(node, __efc_ns_gidpt_delay, NULL);
|
|
} else {
|
|
efc_ns_send_gidpt(node);
|
|
efc_node_transition(node, __efc_ns_gidpt_wait_rsp,
|
|
NULL);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gidpt_delay_timer_cb(struct timer_list *t)
|
|
{
|
|
struct efc_node *node = from_timer(node, t, gidpt_delay_timer);
|
|
|
|
del_timer(&node->gidpt_delay_timer);
|
|
|
|
efc_node_post_event(node, EFC_EVT_GIDPT_DELAY_EXPIRED, NULL);
|
|
}
|
|
|
|
void
|
|
__efc_ns_gidpt_delay(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
struct efc *efc = node->efc;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER: {
|
|
u64 delay_msec, tmp;
|
|
|
|
/*
|
|
* Compute the delay time.
|
|
* Set to tgt_rscn_delay, if the time since last GIDPT
|
|
* is less than tgt_rscn_period, then use tgt_rscn_period.
|
|
*/
|
|
delay_msec = efc->tgt_rscn_delay_msec;
|
|
tmp = jiffies_to_msecs(jiffies) - node->time_last_gidpt_msec;
|
|
if (tmp < efc->tgt_rscn_period_msec)
|
|
delay_msec = efc->tgt_rscn_period_msec;
|
|
|
|
timer_setup(&node->gidpt_delay_timer, &gidpt_delay_timer_cb,
|
|
0);
|
|
mod_timer(&node->gidpt_delay_timer,
|
|
jiffies + msecs_to_jiffies(delay_msec));
|
|
|
|
break;
|
|
}
|
|
|
|
case EFC_EVT_GIDPT_DELAY_EXPIRED:
|
|
node->time_last_gidpt_msec = jiffies_to_msecs(jiffies);
|
|
|
|
efc_ns_send_gidpt(node);
|
|
efc_node_transition(node, __efc_ns_gidpt_wait_rsp, NULL);
|
|
break;
|
|
|
|
case EFC_EVT_RSCN_RCVD: {
|
|
efc_log_debug(efc,
|
|
"RSCN received while in GIDPT delay - no action\n");
|
|
break;
|
|
}
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_fabctl_init(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
/* no need to login to fabric controller, just send SCR */
|
|
efc_send_scr(node);
|
|
efc_node_transition(node, __efc_fabctl_wait_scr_rsp, NULL);
|
|
break;
|
|
|
|
case EFC_EVT_NODE_ATTACH_OK:
|
|
node->attached = true;
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_fabctl_wait_scr_rsp(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
/*
|
|
* Fabric controller node state machine:
|
|
* Wait for an SCR response from the fabric controller.
|
|
*/
|
|
switch (evt) {
|
|
case EFC_EVT_SRRS_ELS_REQ_OK:
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_SCR,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
efc_node_transition(node, __efc_fabctl_ready, NULL);
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
static void
|
|
efc_process_rscn(struct efc_node *node, struct efc_node_cb *cbdata)
|
|
{
|
|
struct efc *efc = node->efc;
|
|
struct efc_nport *nport = node->nport;
|
|
struct efc_node *ns;
|
|
|
|
/* Forward this event to the name-services node */
|
|
ns = efc_node_find(nport, FC_FID_DIR_SERV);
|
|
if (ns)
|
|
efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, cbdata);
|
|
else
|
|
efc_log_warn(efc, "can't find name server node\n");
|
|
}
|
|
|
|
void
|
|
__efc_fabctl_ready(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
/*
|
|
* Fabric controller node state machine: Ready.
|
|
* In this state, the fabric controller sends a RSCN, which is received
|
|
* by this node and is forwarded to the name services node object; and
|
|
* the RSCN LS_ACC is sent.
|
|
*/
|
|
switch (evt) {
|
|
case EFC_EVT_RSCN_RCVD: {
|
|
struct fc_frame_header *hdr = cbdata->header->dma.virt;
|
|
|
|
/*
|
|
* sm: / process RSCN (forward to name services node),
|
|
* send LS_ACC
|
|
*/
|
|
efc_process_rscn(node, cbdata);
|
|
efc_send_ls_acc(node, be16_to_cpu(hdr->fh_ox_id));
|
|
efc_node_transition(node, __efc_fabctl_wait_ls_acc_cmpl,
|
|
NULL);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_fabctl_wait_ls_acc_cmpl(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
efc_node_hold_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_EXIT:
|
|
efc_node_accept_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_SRRS_ELS_CMPL_OK:
|
|
WARN_ON(!node->els_cmpl_cnt);
|
|
node->els_cmpl_cnt--;
|
|
efc_node_transition(node, __efc_fabctl_ready, NULL);
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
static uint64_t
|
|
efc_get_wwpn(struct fc_els_flogi *sp)
|
|
{
|
|
return be64_to_cpu(sp->fl_wwnn);
|
|
}
|
|
|
|
static int
|
|
efc_rnode_is_winner(struct efc_nport *nport)
|
|
{
|
|
struct fc_els_flogi *remote_sp;
|
|
u64 remote_wwpn;
|
|
u64 local_wwpn = nport->wwpn;
|
|
u64 wwn_bump = 0;
|
|
|
|
remote_sp = (struct fc_els_flogi *)nport->domain->flogi_service_params;
|
|
remote_wwpn = efc_get_wwpn(remote_sp);
|
|
|
|
local_wwpn ^= wwn_bump;
|
|
|
|
efc_log_debug(nport->efc, "r: %llx\n",
|
|
be64_to_cpu(remote_sp->fl_wwpn));
|
|
efc_log_debug(nport->efc, "l: %llx\n", local_wwpn);
|
|
|
|
if (remote_wwpn == local_wwpn) {
|
|
efc_log_warn(nport->efc,
|
|
"WWPN of remote node [%08x %08x] matches local WWPN\n",
|
|
(u32)(local_wwpn >> 32ll),
|
|
(u32)local_wwpn);
|
|
return -1;
|
|
}
|
|
|
|
return (remote_wwpn > local_wwpn);
|
|
}
|
|
|
|
void
|
|
__efc_p2p_wait_domain_attach(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node *node = ctx->app;
|
|
struct efc *efc = node->efc;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
efc_node_hold_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_EXIT:
|
|
efc_node_accept_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_DOMAIN_ATTACH_OK: {
|
|
struct efc_nport *nport = node->nport;
|
|
struct efc_node *rnode;
|
|
|
|
/*
|
|
* this transient node (SID=0 (recv'd FLOGI)
|
|
* or DID=fabric (sent FLOGI))
|
|
* is the p2p winner, will use a separate node
|
|
* to send PLOGI to peer
|
|
*/
|
|
WARN_ON(!node->nport->p2p_winner);
|
|
|
|
rnode = efc_node_find(nport, node->nport->p2p_remote_port_id);
|
|
if (rnode) {
|
|
/*
|
|
* the "other" transient p2p node has
|
|
* already kicked off the
|
|
* new node from which PLOGI is sent
|
|
*/
|
|
node_printf(node,
|
|
"Node with fc_id x%x already exists\n",
|
|
rnode->rnode.fc_id);
|
|
} else {
|
|
/*
|
|
* create new node (SID=1, DID=2)
|
|
* from which to send PLOGI
|
|
*/
|
|
rnode = efc_node_alloc(nport,
|
|
nport->p2p_remote_port_id,
|
|
false, false);
|
|
if (!rnode) {
|
|
efc_log_err(efc, "node alloc failed\n");
|
|
return;
|
|
}
|
|
|
|
efc_fabric_notify_topology(node);
|
|
/* sm: / allocate p2p remote node */
|
|
efc_node_transition(rnode, __efc_p2p_rnode_init,
|
|
NULL);
|
|
}
|
|
|
|
/*
|
|
* the transient node (SID=0 or DID=fabric)
|
|
* has served its purpose
|
|
*/
|
|
if (node->rnode.fc_id == 0) {
|
|
/*
|
|
* if this is the SID=0 node,
|
|
* move to the init state in case peer
|
|
* has restarted FLOGI discovery and FLOGI is pending
|
|
*/
|
|
/* don't send PLOGI on efc_d_init entry */
|
|
efc_node_init_device(node, false);
|
|
} else {
|
|
/*
|
|
* if this is the DID=fabric node
|
|
* (we initiated FLOGI), shut it down
|
|
*/
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_fabric_initiate_shutdown(node);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_p2p_rnode_init(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
/* sm: / send PLOGI */
|
|
efc_send_plogi(node);
|
|
efc_node_transition(node, __efc_p2p_wait_plogi_rsp, NULL);
|
|
break;
|
|
|
|
case EFC_EVT_ABTS_RCVD:
|
|
/* sm: send BA_ACC */
|
|
efc_send_bls_acc(node, cbdata->header->dma.virt);
|
|
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_p2p_wait_flogi_acc_cmpl(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
efc_node_hold_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_EXIT:
|
|
efc_node_accept_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_SRRS_ELS_CMPL_OK:
|
|
WARN_ON(!node->els_cmpl_cnt);
|
|
node->els_cmpl_cnt--;
|
|
|
|
/* sm: if p2p_winner / domain_attach */
|
|
if (node->nport->p2p_winner) {
|
|
efc_node_transition(node,
|
|
__efc_p2p_wait_domain_attach,
|
|
NULL);
|
|
if (!node->nport->domain->attached) {
|
|
node_printf(node, "Domain not attached\n");
|
|
efc_domain_attach(node->nport->domain,
|
|
node->nport->p2p_port_id);
|
|
} else {
|
|
node_printf(node, "Domain already attached\n");
|
|
efc_node_post_event(node,
|
|
EFC_EVT_DOMAIN_ATTACH_OK,
|
|
NULL);
|
|
}
|
|
} else {
|
|
/* this node has served its purpose;
|
|
* we'll expect a PLOGI on a separate
|
|
* node (remote SID=0x1); return this node
|
|
* to init state in case peer
|
|
* restarts discovery -- it may already
|
|
* have (pending frames may exist).
|
|
*/
|
|
/* don't send PLOGI on efc_d_init entry */
|
|
efc_node_init_device(node, false);
|
|
}
|
|
break;
|
|
|
|
case EFC_EVT_SRRS_ELS_CMPL_FAIL:
|
|
/*
|
|
* LS_ACC failed, possibly due to link down;
|
|
* shutdown node and wait
|
|
* for FLOGI discovery to restart
|
|
*/
|
|
node_printf(node, "FLOGI LS_ACC failed, shutting down\n");
|
|
WARN_ON(!node->els_cmpl_cnt);
|
|
node->els_cmpl_cnt--;
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_fabric_initiate_shutdown(node);
|
|
break;
|
|
|
|
case EFC_EVT_ABTS_RCVD: {
|
|
/* sm: / send BA_ACC */
|
|
efc_send_bls_acc(node, cbdata->header->dma.virt);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_p2p_wait_plogi_rsp(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_SRRS_ELS_REQ_OK: {
|
|
int rc;
|
|
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
/* sm: / save sparams, efc_node_attach */
|
|
efc_node_save_sparms(node, cbdata->els_rsp.virt);
|
|
rc = efc_node_attach(node);
|
|
efc_node_transition(node, __efc_p2p_wait_node_attach, NULL);
|
|
if (rc < 0)
|
|
efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL,
|
|
NULL);
|
|
break;
|
|
}
|
|
case EFC_EVT_SRRS_ELS_REQ_FAIL: {
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
node_printf(node, "PLOGI failed, shutting down\n");
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_fabric_initiate_shutdown(node);
|
|
break;
|
|
}
|
|
|
|
case EFC_EVT_PLOGI_RCVD: {
|
|
struct fc_frame_header *hdr = cbdata->header->dma.virt;
|
|
/* if we're in external loopback mode, just send LS_ACC */
|
|
if (node->efc->external_loopback) {
|
|
efc_send_plogi_acc(node, be16_to_cpu(hdr->fh_ox_id));
|
|
} else {
|
|
/*
|
|
* if this isn't external loopback,
|
|
* pass to default handler
|
|
*/
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
break;
|
|
}
|
|
case EFC_EVT_PRLI_RCVD:
|
|
/* I, or I+T */
|
|
/* sent PLOGI and before completion was seen, received the
|
|
* PRLI from the remote node (WCQEs and RCQEs come in on
|
|
* different queues and order of processing cannot be assumed)
|
|
* Save OXID so PRLI can be sent after the attach and continue
|
|
* to wait for PLOGI response
|
|
*/
|
|
efc_process_prli_payload(node, cbdata->payload->dma.virt);
|
|
efc_send_ls_acc_after_attach(node,
|
|
cbdata->header->dma.virt,
|
|
EFC_NODE_SEND_LS_ACC_PRLI);
|
|
efc_node_transition(node, __efc_p2p_wait_plogi_rsp_recvd_prli,
|
|
NULL);
|
|
break;
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_p2p_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
/*
|
|
* Since we've received a PRLI, we have a port login and will
|
|
* just need to wait for the PLOGI response to do the node
|
|
* attach and then we can send the LS_ACC for the PRLI. If,
|
|
* during this time, we receive FCP_CMNDs (which is possible
|
|
* since we've already sent a PRLI and our peer may have
|
|
* accepted).
|
|
* At this time, we are not waiting on any other unsolicited
|
|
* frames to continue with the login process. Thus, it will not
|
|
* hurt to hold frames here.
|
|
*/
|
|
efc_node_hold_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_EXIT:
|
|
efc_node_accept_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_SRRS_ELS_REQ_OK: { /* PLOGI response received */
|
|
int rc;
|
|
|
|
/* Completion from PLOGI sent */
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
/* sm: / save sparams, efc_node_attach */
|
|
efc_node_save_sparms(node, cbdata->els_rsp.virt);
|
|
rc = efc_node_attach(node);
|
|
efc_node_transition(node, __efc_p2p_wait_node_attach, NULL);
|
|
if (rc < 0)
|
|
efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL,
|
|
NULL);
|
|
break;
|
|
}
|
|
case EFC_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */
|
|
case EFC_EVT_SRRS_ELS_REQ_RJT:
|
|
/* PLOGI failed, shutdown the node */
|
|
if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
|
|
__efc_fabric_common, __func__)) {
|
|
return;
|
|
}
|
|
WARN_ON(!node->els_req_cnt);
|
|
node->els_req_cnt--;
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_fabric_initiate_shutdown(node);
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
__efc_p2p_wait_node_attach(struct efc_sm_ctx *ctx,
|
|
enum efc_sm_event evt, void *arg)
|
|
{
|
|
struct efc_node_cb *cbdata = arg;
|
|
struct efc_node *node = ctx->app;
|
|
|
|
efc_node_evt_set(ctx, evt, __func__);
|
|
|
|
node_sm_trace();
|
|
|
|
switch (evt) {
|
|
case EFC_EVT_ENTER:
|
|
efc_node_hold_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_EXIT:
|
|
efc_node_accept_frames(node);
|
|
break;
|
|
|
|
case EFC_EVT_NODE_ATTACH_OK:
|
|
node->attached = true;
|
|
switch (node->send_ls_acc) {
|
|
case EFC_NODE_SEND_LS_ACC_PRLI: {
|
|
efc_d_send_prli_rsp(node->ls_acc_io,
|
|
node->ls_acc_oxid);
|
|
node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;
|
|
node->ls_acc_io = NULL;
|
|
break;
|
|
}
|
|
case EFC_NODE_SEND_LS_ACC_PLOGI: /* Can't happen in P2P */
|
|
case EFC_NODE_SEND_LS_ACC_NONE:
|
|
default:
|
|
/* Normal case for I */
|
|
/* sm: send_plogi_acc is not set / send PLOGI acc */
|
|
efc_node_transition(node, __efc_d_port_logged_in,
|
|
NULL);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case EFC_EVT_NODE_ATTACH_FAIL:
|
|
/* node attach failed, shutdown the node */
|
|
node->attached = false;
|
|
node_printf(node, "Node attach failed\n");
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_fabric_initiate_shutdown(node);
|
|
break;
|
|
|
|
case EFC_EVT_SHUTDOWN:
|
|
node_printf(node, "%s received\n", efc_sm_event_name(evt));
|
|
node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
|
|
efc_node_transition(node,
|
|
__efc_fabric_wait_attach_evt_shutdown,
|
|
NULL);
|
|
break;
|
|
case EFC_EVT_PRLI_RCVD:
|
|
node_printf(node, "%s: PRLI received before node is attached\n",
|
|
efc_sm_event_name(evt));
|
|
efc_process_prli_payload(node, cbdata->payload->dma.virt);
|
|
efc_send_ls_acc_after_attach(node,
|
|
cbdata->header->dma.virt,
|
|
EFC_NODE_SEND_LS_ACC_PRLI);
|
|
break;
|
|
|
|
default:
|
|
__efc_fabric_common(__func__, ctx, evt, arg);
|
|
}
|
|
}
|
|
|
|
int
|
|
efc_p2p_setup(struct efc_nport *nport)
|
|
{
|
|
struct efc *efc = nport->efc;
|
|
int rnode_winner;
|
|
|
|
rnode_winner = efc_rnode_is_winner(nport);
|
|
|
|
/* set nport flags to indicate p2p "winner" */
|
|
if (rnode_winner == 1) {
|
|
nport->p2p_remote_port_id = 0;
|
|
nport->p2p_port_id = 0;
|
|
nport->p2p_winner = false;
|
|
} else if (rnode_winner == 0) {
|
|
nport->p2p_remote_port_id = 2;
|
|
nport->p2p_port_id = 1;
|
|
nport->p2p_winner = true;
|
|
} else {
|
|
/* no winner; only okay if external loopback enabled */
|
|
if (nport->efc->external_loopback) {
|
|
/*
|
|
* External loopback mode enabled;
|
|
* local nport and remote node
|
|
* will be registered with an NPortID = 1;
|
|
*/
|
|
efc_log_debug(efc,
|
|
"External loopback mode enabled\n");
|
|
nport->p2p_remote_port_id = 1;
|
|
nport->p2p_port_id = 1;
|
|
nport->p2p_winner = true;
|
|
} else {
|
|
efc_log_warn(efc,
|
|
"failed to determine p2p winner\n");
|
|
return rnode_winner;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|