// 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 = kcalloc(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 = timer_container_of(node, t, gidpt_delay_timer);
timer_delete(&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 >> 32 ll),
(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 = 0 x1 ) ; 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 ;
}
Messung V0.5 in Prozent C=95 H=99 G=96
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet am 2026-10-02)
¤
*© Formatika GbR, Deutschland