On Sat, Apr 11, 2020 at 08:32:45PM -0700, James Smart wrote: > This patch continues the libefc library population. > > This patch adds library interface definitions for: > - Fabric node initialization and logins. > - Name/Directory Services node. > - Fabric Controller node to process rscn events. > > These are all interactions with remote ports that correspond > to well-known fabric entities > > Signed-off-by: Ram Vegesna <ram.vegesna@xxxxxxxxxxxx> > Signed-off-by: James Smart <jsmart2021@xxxxxxxxx> > > --- > v3: > Replace efc_assert with WARN_ON > Return defined return values > --- > drivers/scsi/elx/libefc/efc_fabric.c | 1759 ++++++++++++++++++++++++++++++++++ > drivers/scsi/elx/libefc/efc_fabric.h | 116 +++ > 2 files changed, 1875 insertions(+) > create mode 100644 drivers/scsi/elx/libefc/efc_fabric.c > create mode 100644 drivers/scsi/elx/libefc/efc_fabric.h > > diff --git a/drivers/scsi/elx/libefc/efc_fabric.c b/drivers/scsi/elx/libefc/efc_fabric.c > new file mode 100644 > index 000000000000..251f8702dbc5 > --- /dev/null > +++ b/drivers/scsi/elx/libefc/efc_fabric.c > @@ -0,0 +1,1759 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2019 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) > +{ > + int rc; > + struct efc *efc = node->efc; > + > + efc->tt.scsi_io_alloc_disable(efc, node); > + > + if (node->attached) { > + /* issue hw node free; don't care if succeeds right away > + * or sometime later, will check node->attached later in > + * shutdown process > + */ > + rc = efc->tt.hw_node_detach(efc, &node->rnode); > + if (rc != EFC_HW_RTN_SUCCESS && > + rc != EFC_HW_RTN_SUCCESS_SYNC) { > + 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); > + break; > + } > + return NULL; > +} > + > +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: /* not sure why we're getting these ... */ > + efc_log_debug(efc, ">>> reenter !!\n"); > + /* fall through */ IIRC, /* fall through */ has been replaced by fallthrough; > + case EFC_EVT_ENTER: > + /* sm: / send FLOGI */ > + efc->tt.els_send(efc, node, ELS_FLOGI, > + EFC_FC_FLOGI_TIMEOUT_SEC, > + EFC_FC_ELS_DEFAULT_RETRIES); > + efc_node_transition(node, __efc_fabric_flogi_wait_rsp, NULL); > + break; > + > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + break; > + } > + > + return NULL; > +} > + > +void > +efc_fabric_set_topology(struct efc_node *node, > + enum efc_sport_topology topology) > +{ > + node->sport->topology = topology; > +} > + > +void > +efc_fabric_notify_topology(struct efc_node *node) > +{ > + struct efc_node *tmp_node; > + struct efc_node *next; > + enum efc_sport_topology topology = node->sport->topology; > + > + /* > + * now loop through the nodes in the sport > + * and send topology notification > + */ > + list_for_each_entry_safe(tmp_node, next, &node->sport->node_list, > + list_entry) { > + if (tmp_node != node) { > + efc_node_post_event(tmp_node, > + EFC_EVT_SPORT_TOPOLOGY_NOTIFY, > + (void *)topology); > + } > + } > +} > + > +static bool efc_rnode_is_nport(struct fc_els_flogi *rsp) > +{ > + return !(ntohs(rsp->fl_csp.sp_features) & FC_SP_FT_FPORT); > +} > + > +static bool efc_rnode_is_npiv_capable(struct fc_els_flogi *rsp) > +{ > + return !!(ntohs(rsp->fl_csp.sp_features) & FC_SP_FT_NPIV_ACC); > +} > + > +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 NULL; > + } > + WARN_ON(!node->els_req_cnt); > + node->els_req_cnt--; > + > + memcpy(node->sport->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 */ > + if (efc_rnode_is_npiv_capable(cbdata->els_rsp.virt)) { > + efc_log_debug(node->efc, > + " NPIV is enabled at switch side\n"); > + //node->efc->sw_feature_cap |= 1<<10; Looks like a left over. > + } > + efc_fabric_set_topology(node, > + EFC_SPORT_TOPOLOGY_FABRIC); > + efc_fabric_notify_topology(node); > + WARN_ON(node->sport->domain->attached); > + efc_domain_attach(node->sport->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_SPORT_TOPOLOGY_P2P); > + if (efc_p2p_setup(node->sport)) { > + 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->sport->p2p_winner) { > + efc_node_transition(node, > + __efc_p2p_wait_domain_attach, > + NULL); > + if (node->sport->domain->attached && > + !node->sport->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_sli_port *sport = node->sport; > + /* > + * with these errors, we have no recovery, > + * so shutdown the sport, leave the link > + * up and the domain ready > + */ > + if (efc_node_check_els_req(ctx, evt, arg, ELS_FLOGI, > + __efc_fabric_common, __func__)) { > + return NULL; > + } > + node_printf(node, > + "FLOGI failed evt=%s, shutting down sport [%s]\n", > + efc_sm_event_name(evt), sport->display_name); > + WARN_ON(!node->els_req_cnt); > + node->els_req_cnt--; > + efc_sm_post_event(&sport->sm, EFC_EVT_SHUTDOWN, NULL); > + break; > + } > + > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + break; > + } > + > + return NULL; > +} > + > +void * > +__efc_vport_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_ENTER: > + /* sm: / send FDISC */ > + efc->tt.els_send(efc, node, ELS_FDISC, > + EFC_FC_FLOGI_TIMEOUT_SEC, > + EFC_FC_ELS_DEFAULT_RETRIES); > + > + efc_node_transition(node, __efc_fabric_fdisc_wait_rsp, NULL); > + break; > + > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + break; > + } > + > + return NULL; > +} > + > +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 NULL; > + } > + > + WARN_ON(!node->els_req_cnt); > + node->els_req_cnt--; > + /* sm: / efc_sport_attach */ > + efc_sport_attach(node->sport, 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 NULL; > + } > + WARN_ON(!node->els_req_cnt); > + node->els_req_cnt--; > + efc_log_err(node->efc, "FDISC failed, shutting down sport\n"); > + /* sm: / shutdown sport */ > + efc_sm_post_event(&node->sport->sm, EFC_EVT_SHUTDOWN, NULL); > + break; > + } > + > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + break; > + } > + > + return NULL; > +} > + > +static int > +efc_start_ns_node(struct efc_sli_port *sport) > +{ > + struct efc_node *ns; > + > + /* Instantiate a name services node */ > + ns = efc_node_find(sport, FC_FID_DIR_SERV); > + if (!ns) { > + ns = efc_node_alloc(sport, FC_FID_DIR_SERV, false, false); > + if (!ns) > + return EFC_FAIL; > + } > + /* > + * 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 EFC_SUCCESS; > +} > + > +static int > +efc_start_fabctl_node(struct efc_sli_port *sport) > +{ > + struct efc_node *fabctl; > + > + fabctl = efc_node_find(sport, FC_FID_FCTRL); > + if (!fabctl) { > + fabctl = efc_node_alloc(sport, FC_FID_FCTRL, > + false, false); > + if (!fabctl) > + return EFC_FAIL; > + } > + /* > + * 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 EFC_SUCCESS; > +} > + > +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_SPORT_ATTACH_OK: { > + int rc; > + > + rc = efc_start_ns_node(node->sport); > + if (rc) > + return NULL; > + > + /* sm: if enable_ini / start fabctl node */ > + /* Instantiate the fabric controller (sends SCR) */ > + if (node->sport->enable_rscn) { > + rc = efc_start_fabctl_node(node->sport); > + if (rc) > + return NULL; > + } > + efc_node_transition(node, __efc_fabric_idle, NULL); > + break; > + } > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + return NULL; > + } > + > + return NULL; > +} > + > +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); > + return NULL; > + } > + > + return NULL; > +} > + > +void * > +__efc_ns_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_ENTER: > + /* sm: / send PLOGI */ > + efc->tt.els_send(efc, node, ELS_PLOGI, > + EFC_FC_FLOGI_TIMEOUT_SEC, > + EFC_FC_ELS_DEFAULT_RETRIES); > + efc_node_transition(node, __efc_ns_plogi_wait_rsp, NULL); > + break; > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + break; > + } > + > + return NULL; > +} > + > +void * > +__efc_ns_plogi_wait_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg) > +{ > + int rc; > + 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: { > + /* Save service parameters */ > + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, > + __efc_fabric_common, __func__)) { > + return NULL; > + } > + 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 == EFC_HW_RTN_SUCCESS_SYNC) > + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_OK, > + NULL); > + break; > + } > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + return NULL; > + } > + > + return NULL; > +} > + > +void * > +__efc_ns_wait_node_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_NODE_ATTACH_OK: > + node->attached = true; > + /* sm: / send RFTID */ > + efc->tt.els_send_ct(efc, node, FC_RCTL_ELS_REQ, > + EFC_FC_ELS_SEND_DEFAULT_TIMEOUT, > + EFC_FC_ELS_DEFAULT_RETRIES); > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +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); > + return NULL; > + } > + > + return NULL; > +} > + > +void * > +__efc_ns_rftid_wait_rsp(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_SRRS_ELS_REQ_OK: > + if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_RFT_ID, > + __efc_fabric_common, __func__)) { > + return NULL; > + } > + WARN_ON(!node->els_req_cnt); > + node->els_req_cnt--; > + /* sm: / send RFFID */ > + efc->tt.els_send_ct(efc, node, FC_NS_RFF_ID, > + EFC_FC_ELS_SEND_DEFAULT_TIMEOUT, > + EFC_FC_ELS_DEFAULT_RETRIES); > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +/** > + * Waits for an RFFID response event; if configured for an initiator operation, > + * a GIDPT name services request is issued. > + */ > +void * > +__efc_ns_rffid_wait_rsp(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_SRRS_ELS_REQ_OK: { > + if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_RFF_ID, > + __efc_fabric_common, __func__)) { > + return NULL; > + } > + WARN_ON(!node->els_req_cnt); > + node->els_req_cnt--; > + if (node->sport->enable_rscn) { > + /* sm: if enable_rscn / send GIDPT */ > + efc->tt.els_send_ct(efc, node, FC_NS_GID_PT, > + EFC_FC_ELS_SEND_DEFAULT_TIMEOUT, > + EFC_FC_ELS_DEFAULT_RETRIES); > + > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +static int > +efc_process_gidpt_payload(struct efc_node *node, > + void *data, u32 gidpt_len) > +{ > + u32 i, j; > + struct efc_node *newnode; > + struct efc_sli_port *sport = node->sport; > + struct efc *efc = node->efc; > + u32 port_id = 0, port_count, portlist_count; > + struct efc_node *n; > + struct efc_node **active_nodes; > + int residual; > + struct fc_ct_hdr *hdr = data; > + struct fc_gid_pn_resp *gidpt = data + sizeof(*hdr); > + > + residual = be16_to_cpu(hdr->ct_mr_size); > + > + if (residual != 0) > + efc_log_debug(node->efc, "residual is %u words\n", residual); > + > + if (be16_to_cpu(hdr->ct_cmd) == FC_FS_RJT) { > + node_printf(node, > + "GIDPT request failed: rsn x%x rsn_expl x%x\n", > + hdr->ct_reason, hdr->ct_explan); > + return EFC_FAIL; > + } > + > + portlist_count = (gidpt_len - sizeof(*hdr)) / sizeof(*gidpt); > + > + /* Count the number of nodes */ > + port_count = 0; > + list_for_each_entry(n, &sport->node_list, list_entry) { > + 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 EFC_FAIL; > + } > + > + /* Fill buffer with fc_id of active nodes */ > + i = 0; > + list_for_each_entry(n, &sport->node_list, list_entry) { > + 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 < portlist_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]) { > + if ((node->sport->enable_ini && > + active_nodes[i]->targ) || > + (node->sport->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 < portlist_count; i++) { > + hton24(gidpt[i].fp_fid, port_id); > + > + /* Don't create node for ourselves */ > + if (port_id != node->rnode.sport->fc_id) { > + newnode = efc_node_find(sport, port_id); > + if (!newnode) { > + if (node->sport->enable_ini) { > + newnode = efc_node_alloc(sport, > + port_id, > + false, > + false); > + if (!newnode) { > + efc_log_err(efc, > + "efc_node_alloc() failed\n"); > + return EFC_FAIL; > + } > + /* > + * send PLOGI automatically > + * if initiator > + */ > + efc_node_init_device(newnode, true); > + } > + continue; > + } > + > + if (node->sport->enable_ini && newnode->targ) { > + efc_node_post_event(newnode, > + EFC_EVT_NODE_REFOUND, > + NULL); > + } > + /* > + * original code sends ADISC, > + * has notion of "refound" > + */ > + } A helper function or something which makes this heavy idention go away, please. This is hard to read. > + > + if (gidpt[i].fp_resvd & FC_NS_FID_LAST) > + break; > + } > + return EFC_SUCCESS; > +} > + > +/** > + * Wait for a GIDPT response from the name server. Process the FC_IDs that are > + * reported by creating new remote ports, as needed. > + */ > +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(); > + > + 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 NULL; > + } > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +/** > + * Idle. Waiting for RSCN received events > + * (posted from the fabric controller), and > + * restarts the GIDPT name services query and processing. > + */ Not a proper kerneldoc. There are more of those in this file. > +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(); > + > + switch (evt) { > + case EFC_EVT_ENTER: > + if (!node->rscn_pending) > + break; > + > + node_printf(node, "RSCN pending, restart discovery\n"); > + node->rscn_pending = false; > + > + /* fall through */ > + > + 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->sport->enable_ini && node->sport->enable_tgt && > + enable_target_rscn(efc)) { > + efc_node_transition(node, __efc_ns_gidpt_delay, NULL); > + } else { > + efc->tt.els_send_ct(efc, node, FC_NS_GID_PT, > + EFC_FC_ELS_SEND_DEFAULT_TIMEOUT, > + EFC_FC_ELS_DEFAULT_RETRIES); > + efc_node_transition(node, __efc_ns_gidpt_wait_rsp, > + NULL); > + } > + break; > + } > + > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + break; > + } > + > + return NULL; > +} > + > +/** > + * Handle GIDPT delay timer callback > + * Post an EFC_EVT_GIDPT_DEIALY_EXPIRED event to the passed in node. > + */ > +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); > +} > + > +/** > + * Name services node state machine: Delayed GIDPT. > + * > + * Waiting for GIDPT delay to expire before submitting GIDPT to name server. > + */ > +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: { > + time_t delay_msec; > + > + /* > + * 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; > + if ((jiffies_to_msecs(jiffies) - node->time_last_gidpt_msec) > + < efc->tgt_rscn_period_msec) { Maybe the first part of the condition as new variable to make the if readable. > + 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->tt.els_send_ct(efc, node, FC_NS_GID_PT, > + EFC_FC_ELS_SEND_DEFAULT_TIMEOUT, > + EFC_FC_ELS_DEFAULT_RETRIES); > + 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); > + break; > + } > + > + return NULL; > +} > + > +/** > + * Fabric controller node state machine: Initial state. > + * > + * Issue a PLOGI to a well-known fabric controller address. > + */ > +void * > +__efc_fabctl_init(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg) > +{ > + struct efc_node *node = ctx->app; > + struct efc *efc = node->efc; > + > + node_sm_trace(); > + > + switch (evt) { > + case EFC_EVT_ENTER: > + /* no need to login to fabric controller, just send SCR */ > + efc->tt.els_send(efc, node, ELS_SCR, > + EFC_FC_FLOGI_TIMEOUT_SEC, > + EFC_FC_ELS_DEFAULT_RETRIES); > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +/** > + * Fabric controller node state machine: Wait for a node attach request > + * to complete. > + * > + * Wait for a node attach to complete. If successful, issue an SCR > + * to the fabric controller, subscribing to all RSCN. > + */ > +void * > +__efc_fabctl_wait_node_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_NODE_ATTACH_OK: > + node->attached = true; > + /* sm: / send SCR */ > + efc->tt.els_send(efc, node, ELS_SCR, > + EFC_FC_ELS_SEND_DEFAULT_TIMEOUT, > + EFC_FC_ELS_DEFAULT_RETRIES); > + efc_node_transition(node, __efc_fabctl_wait_scr_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; > + > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + return NULL; > + } > + > + return NULL; > +} > + > +/** > + * Fabric controller node state machine: > + * Wait for an SCR response from the fabric controller. > + */ > +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(); > + > + 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 NULL; > + } > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +static void > +efc_process_rscn(struct efc_node *node, struct efc_node_cb *cbdata) > +{ > + struct efc *efc = node->efc; > + struct efc_sli_port *sport = node->sport; > + struct efc_node *ns; > + > + /* Forward this event to the name-services node */ > + ns = efc_node_find(sport, 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"); > +} > + > +/* 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. > + */ > +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; > + struct efc *efc = node->efc; > + > + efc_node_evt_set(ctx, evt, __func__); > + > + node_sm_trace(); > + > + 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->tt.els_send_resp(efc, node, ELS_LS_ACC, > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +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); > + return NULL; > + } > + > + return NULL; > +} > + > +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_sli_port *sport) > +{ > + struct fc_els_flogi *remote_sp; > + u64 remote_wwpn; > + u64 local_wwpn = sport->wwpn; > + u64 wwn_bump = 0; > + > + remote_sp = (struct fc_els_flogi *)sport->domain->flogi_service_params; > + remote_wwpn = efc_get_wwpn(remote_sp); > + > + local_wwpn ^= wwn_bump; > + > + remote_wwpn = efc_get_wwpn(remote_sp); > + > + efc_log_debug(sport->efc, "r: %llx\n", > + be64_to_cpu(remote_sp->fl_wwpn)); > + efc_log_debug(sport->efc, "l: %llx\n", local_wwpn); > + > + if (remote_wwpn == local_wwpn) { > + efc_log_warn(sport->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_sli_port *sport = node->sport; > + 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->sport->p2p_winner); > + > + rnode = efc_node_find(sport, node->sport->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(sport, > + sport->p2p_remote_port_id, > + false, false); > + if (!rnode) { > + efc_log_err(efc, "node alloc failed\n"); > + return NULL; > + } > + > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +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; > + struct efc *efc = node->efc; > + > + efc_node_evt_set(ctx, evt, __func__); > + > + node_sm_trace(); > + > + switch (evt) { > + case EFC_EVT_ENTER: > + /* sm: / send PLOGI */ > + efc->tt.els_send(efc, node, ELS_PLOGI, > + EFC_FC_FLOGI_TIMEOUT_SEC, > + EFC_FC_ELS_DEFAULT_RETRIES); > + efc_node_transition(node, __efc_p2p_wait_plogi_rsp, NULL); > + break; > + > + case EFC_EVT_ABTS_RCVD: > + /* sm: send BA_ACC */ > + efc->tt.bls_send_acc_hdr(efc, node, cbdata->header->dma.virt); > + break; > + > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + return NULL; > + } > + > + return NULL; > +} > + > +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; > + 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_SRRS_ELS_CMPL_OK: > + WARN_ON(!node->els_cmpl_cnt); > + node->els_cmpl_cnt--; > + > + /* sm: if p2p_winner / domain_attach */ > + if (node->sport->p2p_winner) { > + efc_node_transition(node, > + __efc_p2p_wait_domain_attach, > + NULL); > + if (!node->sport->domain->attached) { > + node_printf(node, "Domain not attached\n"); > + efc_domain_attach(node->sport->domain, > + node->sport->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->tt.bls_send_acc_hdr(efc, node, > + cbdata->header->dma.virt); > + break; > + } > + > + default: > + __efc_fabric_common(__func__, ctx, evt, arg); > + return NULL; > + } > + > + return NULL; > +} > + > +void * > +__efc_p2p_wait_plogi_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg) > +{ > + int rc; > + struct efc_node_cb *cbdata = 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_SRRS_ELS_REQ_OK: { > + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, > + __efc_fabric_common, __func__)) { > + return NULL; > + } > + 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 == EFC_HW_RTN_SUCCESS_SYNC) > + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_OK, > + 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 NULL; > + } > + 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->tt.els_send_resp(efc, node, ELS_PLOGI, > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +void * > +__efc_p2p_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg) > +{ > + int rc; > + 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 */ > + /* Completion from PLOGI sent */ > + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, > + __efc_fabric_common, __func__)) { > + return NULL; > + } > + 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 == EFC_HW_RTN_SUCCESS_SYNC) > + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_OK, > + 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 NULL; > + } > + 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); > + return NULL; > + } > + > + return NULL; > +} > + > +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); > + return NULL; > + } > + > + return NULL; > +} > + > +int > +efc_p2p_setup(struct efc_sli_port *sport) > +{ > + struct efc *efc = sport->efc; > + int rnode_winner; > + > + rnode_winner = efc_rnode_is_winner(sport); > + > + /* set sport flags to indicate p2p "winner" */ > + 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 { > + /* no winner; only okay if external loopback enabled */ > + if (sport->efc->external_loopback) { > + /* > + * External loopback mode enabled; > + * local sport and remote node > + * will be registered with an NPortID = 1; > + */ > + efc_log_debug(efc, > + "External loopback mode enabled\n"); > + sport->p2p_remote_port_id = 1; > + sport->p2p_port_id = 1; > + sport->p2p_winner = true; > + } else { > + efc_log_warn(efc, > + "failed to determine p2p winner\n"); > + return rnode_winner; > + } > + } > + return EFC_SUCCESS; > +} > diff --git a/drivers/scsi/elx/libefc/efc_fabric.h b/drivers/scsi/elx/libefc/efc_fabric.h > new file mode 100644 > index 000000000000..9571b4b7b2ce > --- /dev/null > +++ b/drivers/scsi/elx/libefc/efc_fabric.h > @@ -0,0 +1,116 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2019 Broadcom. All Rights Reserved. The term > + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. > + */ > + > +/* > + * Declarations for the interface exported by efc_fabric > + */ > + > +#ifndef __EFCT_FABRIC_H__ > +#define __EFCT_FABRIC_H__ > +#include "scsi/fc/fc_els.h" > +#include "scsi/fc/fc_fs.h" > +#include "scsi/fc/fc_ns.h" > + > +void * > +__efc_fabric_init(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabric_flogi_wait_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabric_domain_attach_wait(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabric_wait_domain_attach(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > + > +void * > +__efc_vport_fabric_init(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabric_fdisc_wait_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabric_wait_sport_attach(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > + > +void * > +__efc_ns_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg); > +void * > +__efc_ns_plogi_wait_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_ns_rftid_wait_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_ns_rffid_wait_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_ns_wait_node_attach(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabric_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_ns_logo_wait_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event, void *arg); > +void * > +__efc_ns_gidpt_wait_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_ns_idle(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg); > +void * > +__efc_ns_gidpt_delay(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabctl_init(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabctl_wait_node_attach(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabctl_wait_scr_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabctl_ready(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabctl_wait_ls_acc_cmpl(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_fabric_idle(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > + > +void * > +__efc_p2p_rnode_init(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_p2p_domain_attach_wait(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_p2p_wait_flogi_acc_cmpl(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_p2p_wait_plogi_rsp(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_p2p_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_p2p_wait_domain_attach(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > +void * > +__efc_p2p_wait_node_attach(struct efc_sm_ctx *ctx, > + enum efc_sm_event evt, void *arg); > + > +int > +efc_p2p_setup(struct efc_sli_port *sport); > +void > +efc_fabric_set_topology(struct efc_node *node, > + enum efc_sport_topology topology); > +void efc_fabric_notify_topology(struct efc_node *node); > + > +#endif /* __EFCT_FABRIC_H__ */ > -- > 2.16.4 > > Thanks, Daniel