scsi: elx: libefc: SLI and FC PORT state machine interfaces
authorJames Smart <jsmart2021@gmail.com>
Tue, 1 Jun 2021 23:54:52 +0000 (16:54 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 16 Jun 2021 03:39:30 +0000 (23:39 -0400)
Add library interface definitions for:

 - SLI and FC port (aka n_port_id) registration, allocation and
   deallocation.

Link: https://lore.kernel.org/r/20210601235512.20104-12-jsmart2021@gmail.com
Reviewed-by: Daniel Wagner <dwagner@suse.de>
Co-developed-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/elx/libefc/efc_nport.c [new file with mode: 0644]
drivers/scsi/elx/libefc/efc_nport.h [new file with mode: 0644]

diff --git a/drivers/scsi/elx/libefc/efc_nport.c b/drivers/scsi/elx/libefc/efc_nport.c
new file mode 100644 (file)
index 0000000..2e83a66
--- /dev/null
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * NPORT
+ *
+ * Port object for physical port and NPIV ports.
+ */
+
+/*
+ * NPORT REFERENCE COUNTING
+ *
+ * A nport reference should be taken when:
+ * - an nport is allocated
+ * - a vport populates associated nport
+ * - a remote node is allocated
+ * - a unsolicited frame is processed
+ * The reference should be dropped when:
+ * - the unsolicited frame processesing is done
+ * - the remote node is removed
+ * - the vport is removed
+ * - the nport is removed
+ */
+
+#include "efc.h"
+
+void
+efc_nport_cb(void *arg, int event, void *data)
+{
+       struct efc *efc = arg;
+       struct efc_nport *nport = data;
+       unsigned long flags = 0;
+
+       efc_log_debug(efc, "nport event: %s\n", efc_sm_event_name(event));
+
+       spin_lock_irqsave(&efc->lock, flags);
+       efc_sm_post_event(&nport->sm, event, NULL);
+       spin_unlock_irqrestore(&efc->lock, flags);
+}
+
+static struct efc_nport *
+efc_nport_find_wwn(struct efc_domain *domain, uint64_t wwnn, uint64_t wwpn)
+{
+       struct efc_nport *nport = NULL;
+
+       /* Find a nport, given the WWNN and WWPN */
+       list_for_each_entry(nport, &domain->nport_list, list_entry) {
+               if (nport->wwnn == wwnn && nport->wwpn == wwpn)
+                       return nport;
+       }
+       return NULL;
+}
+
+static void
+_efc_nport_free(struct kref *arg)
+{
+       struct efc_nport *nport = container_of(arg, struct efc_nport, ref);
+
+       kfree(nport);
+}
+
+struct efc_nport *
+efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
+               u32 fc_id, bool enable_ini, bool enable_tgt)
+{
+       struct efc_nport *nport;
+
+       if (domain->efc->enable_ini)
+               enable_ini = 0;
+
+       /* Return a failure if this nport has already been allocated */
+       if ((wwpn != 0) || (wwnn != 0)) {
+               nport = efc_nport_find_wwn(domain, wwnn, wwpn);
+               if (nport) {
+                       efc_log_err(domain->efc,
+                                   "NPORT %016llX %016llX already allocated\n",
+                                   wwnn, wwpn);
+                       return NULL;
+               }
+       }
+
+       nport = kzalloc(sizeof(*nport), GFP_ATOMIC);
+       if (!nport)
+               return nport;
+
+       /* initialize refcount */
+       kref_init(&nport->ref);
+       nport->release = _efc_nport_free;
+
+       nport->efc = domain->efc;
+       snprintf(nport->display_name, sizeof(nport->display_name), "------");
+       nport->domain = domain;
+       xa_init(&nport->lookup);
+       nport->instance_index = domain->nport_count++;
+       nport->sm.app = nport;
+       nport->enable_ini = enable_ini;
+       nport->enable_tgt = enable_tgt;
+       nport->enable_rscn = (nport->enable_ini ||
+                       (nport->enable_tgt && enable_target_rscn(nport->efc)));
+
+       /* Copy service parameters from domain */
+       memcpy(nport->service_params, domain->service_params,
+              sizeof(struct fc_els_flogi));
+
+       /* Update requested fc_id */
+       nport->fc_id = fc_id;
+
+       /* Update the nport's service parameters for the new wwn's */
+       nport->wwpn = wwpn;
+       nport->wwnn = wwnn;
+       snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), "%016llX",
+                (unsigned long long)wwnn);
+
+       /*
+        * if this is the "first" nport of the domain,
+        * then make it the "phys" nport
+        */
+       if (list_empty(&domain->nport_list))
+               domain->nport = nport;
+
+       INIT_LIST_HEAD(&nport->list_entry);
+       list_add_tail(&nport->list_entry, &domain->nport_list);
+
+       kref_get(&domain->ref);
+
+       efc_log_debug(domain->efc, "New Nport [%s]\n", nport->display_name);
+
+       return nport;
+}
+
+void
+efc_nport_free(struct efc_nport *nport)
+{
+       struct efc_domain *domain;
+
+       if (!nport)
+               return;
+
+       domain = nport->domain;
+       efc_log_debug(domain->efc, "[%s] free nport\n", nport->display_name);
+       list_del(&nport->list_entry);
+       /*
+        * if this is the physical nport,
+        * then clear it out of the domain
+        */
+       if (nport == domain->nport)
+               domain->nport = NULL;
+
+       xa_destroy(&nport->lookup);
+       xa_erase(&domain->lookup, nport->fc_id);
+
+       if (list_empty(&domain->nport_list))
+               efc_domain_post_event(domain, EFC_EVT_ALL_CHILD_NODES_FREE,
+                                     NULL);
+
+       kref_put(&domain->ref, domain->release);
+       kref_put(&nport->ref, nport->release);
+}
+
+struct efc_nport *
+efc_nport_find(struct efc_domain *domain, u32 d_id)
+{
+       struct efc_nport *nport;
+
+       /* Find a nport object, given an FC_ID */
+       nport = xa_load(&domain->lookup, d_id);
+       if (!nport || !kref_get_unless_zero(&nport->ref))
+               return NULL;
+
+       return nport;
+}
+
+int
+efc_nport_attach(struct efc_nport *nport, u32 fc_id)
+{
+       int rc;
+       struct efc_node *node;
+       struct efc *efc = nport->efc;
+       unsigned long index;
+
+       /* Set our lookup */
+       rc = xa_err(xa_store(&nport->domain->lookup, fc_id, nport, GFP_ATOMIC));
+       if (rc) {
+               efc_log_err(efc, "Sport lookup store failed: %d\n", rc);
+               return rc;
+       }
+
+       /* Update our display_name */
+       efc_node_fcid_display(fc_id, nport->display_name,
+                             sizeof(nport->display_name));
+
+       xa_for_each(&nport->lookup, index, node) {
+               efc_node_update_display_name(node);
+       }
+
+       efc_log_debug(nport->efc, "[%s] attach nport: fc_id x%06x\n",
+                     nport->display_name, fc_id);
+
+       /* Register a nport, given an FC_ID */
+       rc = efc_cmd_nport_attach(efc, nport, fc_id);
+       if (rc < 0) {
+               efc_log_err(nport->efc,
+                           "efc_hw_port_attach failed: %d\n", rc);
+               return -EIO;
+       }
+       return 0;
+}
+
+static void
+efc_nport_shutdown(struct efc_nport *nport)
+{
+       struct efc *efc = nport->efc;
+       struct efc_node *node;
+       unsigned long index;
+
+       xa_for_each(&nport->lookup, index, node) {
+               if (!(node->rnode.fc_id == FC_FID_FLOGI && nport->is_vport)) {
+                       efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
+                       continue;
+               }
+
+               /*
+                * If this is a vport, logout of the fabric
+                * controller so that it deletes the vport
+                * on the switch.
+                */
+               /* if link is down, don't send logo */
+               if (efc->link_status == EFC_LINK_STATUS_DOWN) {
+                       efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
+                       continue;
+               }
+
+               efc_log_debug(efc, "[%s] nport shutdown vport, send logo\n",
+                             node->display_name);
+
+               if (!efc_send_logo(node)) {
+                       /* sent LOGO, wait for response */
+                       efc_node_transition(node, __efc_d_wait_logo_rsp, NULL);
+                       continue;
+               }
+
+               /*
+                * failed to send LOGO,
+                * go ahead and cleanup node anyways
+                */
+               node_printf(node, "Failed to send LOGO\n");
+               efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
+       }
+}
+
+static void
+efc_vport_link_down(struct efc_nport *nport)
+{
+       struct efc *efc = nport->efc;
+       struct efc_vport *vport;
+
+       /* Clear the nport reference in the vport specification */
+       list_for_each_entry(vport, &efc->vport_list, list_entry) {
+               if (vport->nport == nport) {
+                       kref_put(&nport->ref, nport->release);
+                       vport->nport = NULL;
+                       break;
+               }
+       }
+}
+
+static void
+__efc_nport_common(const char *funcname, struct efc_sm_ctx *ctx,
+                  enum efc_sm_event evt, void *arg)
+{
+       struct efc_nport *nport = ctx->app;
+       struct efc_domain *domain = nport->domain;
+       struct efc *efc = nport->efc;
+
+       switch (evt) {
+       case EFC_EVT_ENTER:
+       case EFC_EVT_REENTER:
+       case EFC_EVT_EXIT:
+       case EFC_EVT_ALL_CHILD_NODES_FREE:
+               break;
+       case EFC_EVT_NPORT_ATTACH_OK:
+                       efc_sm_transition(ctx, __efc_nport_attached, NULL);
+               break;
+       case EFC_EVT_SHUTDOWN:
+               /* Flag this nport as shutting down */
+               nport->shutting_down = true;
+
+               if (nport->is_vport)
+                       efc_vport_link_down(nport);
+
+               if (xa_empty(&nport->lookup)) {
+                       /* Remove the nport from the domain's lookup table */
+                       xa_erase(&domain->lookup, nport->fc_id);
+                       efc_sm_transition(ctx, __efc_nport_wait_port_free,
+                                         NULL);
+                       if (efc_cmd_nport_free(efc, nport)) {
+                               efc_log_debug(nport->efc,
+                                             "efc_hw_port_free failed\n");
+                               /* Not much we can do, free the nport anyways */
+                               efc_nport_free(nport);
+                       }
+               } else {
+                       /* sm: node list is not empty / shutdown nodes */
+                       efc_sm_transition(ctx,
+                                         __efc_nport_wait_shutdown, NULL);
+                       efc_nport_shutdown(nport);
+               }
+               break;
+       default:
+               efc_log_debug(nport->efc, "[%s] %-20s %-20s not handled\n",
+                             nport->display_name, funcname,
+                             efc_sm_event_name(evt));
+       }
+}
+
+void
+__efc_nport_allocated(struct efc_sm_ctx *ctx,
+                     enum efc_sm_event evt, void *arg)
+{
+       struct efc_nport *nport = ctx->app;
+       struct efc_domain *domain = nport->domain;
+
+       nport_sm_trace(nport);
+
+       switch (evt) {
+       /* the physical nport is attached */
+       case EFC_EVT_NPORT_ATTACH_OK:
+               WARN_ON(nport != domain->nport);
+               efc_sm_transition(ctx, __efc_nport_attached, NULL);
+               break;
+
+       case EFC_EVT_NPORT_ALLOC_OK:
+               /* ignore */
+               break;
+       default:
+               __efc_nport_common(__func__, ctx, evt, arg);
+       }
+}
+
+void
+__efc_nport_vport_init(struct efc_sm_ctx *ctx,
+                      enum efc_sm_event evt, void *arg)
+{
+       struct efc_nport *nport = ctx->app;
+       struct efc *efc = nport->efc;
+
+       nport_sm_trace(nport);
+
+       switch (evt) {
+       case EFC_EVT_ENTER: {
+               __be64 be_wwpn = cpu_to_be64(nport->wwpn);
+
+               if (nport->wwpn == 0)
+                       efc_log_debug(efc, "vport: letting f/w select WWN\n");
+
+               if (nport->fc_id != U32_MAX) {
+                       efc_log_debug(efc, "vport: hard coding port id: %x\n",
+                                     nport->fc_id);
+               }
+
+               efc_sm_transition(ctx, __efc_nport_vport_wait_alloc, NULL);
+               /* If wwpn is zero, then we'll let the f/w assign wwpn*/
+               if (efc_cmd_nport_alloc(efc, nport, nport->domain,
+                                       nport->wwpn == 0 ? NULL :
+                                       (uint8_t *)&be_wwpn)) {
+                       efc_log_err(efc, "Can't allocate port\n");
+                       break;
+               }
+
+               break;
+       }
+       default:
+               __efc_nport_common(__func__, ctx, evt, arg);
+       }
+}
+
+void
+__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx,
+                            enum efc_sm_event evt, void *arg)
+{
+       struct efc_nport *nport = ctx->app;
+       struct efc *efc = nport->efc;
+
+       nport_sm_trace(nport);
+
+       switch (evt) {
+       case EFC_EVT_NPORT_ALLOC_OK: {
+               struct fc_els_flogi *sp;
+
+               sp = (struct fc_els_flogi *)nport->service_params;
+
+               if (nport->wwnn == 0) {
+                       nport->wwnn = be64_to_cpu(nport->sli_wwnn);
+                       nport->wwpn = be64_to_cpu(nport->sli_wwpn);
+                       snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),
+                                "%016llX", nport->wwpn);
+               }
+
+               /* Update the nport's service parameters */
+               sp->fl_wwpn = cpu_to_be64(nport->wwpn);
+               sp->fl_wwnn = cpu_to_be64(nport->wwnn);
+
+               /*
+                * if nport->fc_id is uninitialized,
+                * then request that the fabric node use FDISC
+                * to find an fc_id.
+                * Otherwise we're restoring vports, or we're in
+                * fabric emulation mode, so attach the fc_id
+                */
+               if (nport->fc_id == U32_MAX) {
+                       struct efc_node *fabric;
+
+                       fabric = efc_node_alloc(nport, FC_FID_FLOGI, false,
+                                               false);
+                       if (!fabric) {
+                               efc_log_err(efc, "efc_node_alloc() failed\n");
+                               return;
+                       }
+                       efc_node_transition(fabric, __efc_vport_fabric_init,
+                                           NULL);
+               } else {
+                       snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),
+                                "%016llX", nport->wwpn);
+                       efc_nport_attach(nport, nport->fc_id);
+               }
+               efc_sm_transition(ctx, __efc_nport_vport_allocated, NULL);
+               break;
+       }
+       default:
+               __efc_nport_common(__func__, ctx, evt, arg);
+       }
+}
+
+void
+__efc_nport_vport_allocated(struct efc_sm_ctx *ctx,
+                           enum efc_sm_event evt, void *arg)
+{
+       struct efc_nport *nport = ctx->app;
+       struct efc *efc = nport->efc;
+
+       nport_sm_trace(nport);
+
+       /*
+        * This state is entered after the nport is allocated;
+        * it then waits for a fabric node
+        * FDISC to complete, which requests a nport attach.
+        * The nport attach complete is handled in this state.
+        */
+       switch (evt) {
+       case EFC_EVT_NPORT_ATTACH_OK: {
+               struct efc_node *node;
+
+               /* Find our fabric node, and forward this event */
+               node = efc_node_find(nport, FC_FID_FLOGI);
+               if (!node) {
+                       efc_log_debug(efc, "can't find node %06x\n", FC_FID_FLOGI);
+                       break;
+               }
+               /* sm: / forward nport attach to fabric node */
+               efc_node_post_event(node, evt, NULL);
+               efc_sm_transition(ctx, __efc_nport_attached, NULL);
+               break;
+       }
+       default:
+               __efc_nport_common(__func__, ctx, evt, arg);
+       }
+}
+
+static void
+efc_vport_update_spec(struct efc_nport *nport)
+{
+       struct efc *efc = nport->efc;
+       struct efc_vport *vport;
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&efc->vport_lock, flags);
+       list_for_each_entry(vport, &efc->vport_list, list_entry) {
+               if (vport->nport == nport) {
+                       vport->wwnn = nport->wwnn;
+                       vport->wwpn = nport->wwpn;
+                       vport->tgt_data = nport->tgt_data;
+                       vport->ini_data = nport->ini_data;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&efc->vport_lock, flags);
+}
+
+void
+__efc_nport_attached(struct efc_sm_ctx *ctx,
+                    enum efc_sm_event evt, void *arg)
+{
+       struct efc_nport *nport = ctx->app;
+       struct efc *efc = nport->efc;
+
+       nport_sm_trace(nport);
+
+       switch (evt) {
+       case EFC_EVT_ENTER: {
+               struct efc_node *node;
+               unsigned long index;
+
+               efc_log_debug(efc,
+                             "[%s] NPORT attached WWPN %016llX WWNN %016llX\n",
+                             nport->display_name,
+                             nport->wwpn, nport->wwnn);
+
+               xa_for_each(&nport->lookup, index, node)
+                       efc_node_update_display_name(node);
+
+               efc->tt.new_nport(efc, nport);
+
+               /*
+                * Update the vport (if its not the physical nport)
+                * parameters
+                */
+               if (nport->is_vport)
+                       efc_vport_update_spec(nport);
+               break;
+       }
+
+       case EFC_EVT_EXIT:
+               efc_log_debug(efc,
+                             "[%s] NPORT deattached WWPN %016llX WWNN %016llX\n",
+                             nport->display_name,
+                             nport->wwpn, nport->wwnn);
+
+               efc->tt.del_nport(efc, nport);
+               break;
+       default:
+               __efc_nport_common(__func__, ctx, evt, arg);
+       }
+}
+
+void
+__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx,
+                         enum efc_sm_event evt, void *arg)
+{
+       struct efc_nport *nport = ctx->app;
+       struct efc_domain *domain = nport->domain;
+       struct efc *efc = nport->efc;
+
+       nport_sm_trace(nport);
+
+       switch (evt) {
+       case EFC_EVT_NPORT_ALLOC_OK:
+       case EFC_EVT_NPORT_ALLOC_FAIL:
+       case EFC_EVT_NPORT_ATTACH_OK:
+       case EFC_EVT_NPORT_ATTACH_FAIL:
+               /* ignore these events - just wait for the all free event */
+               break;
+
+       case EFC_EVT_ALL_CHILD_NODES_FREE: {
+               /*
+                * Remove the nport from the domain's
+                * sparse vector lookup table
+                */
+               xa_erase(&domain->lookup, nport->fc_id);
+               efc_sm_transition(ctx, __efc_nport_wait_port_free, NULL);
+               if (efc_cmd_nport_free(efc, nport)) {
+                       efc_log_err(nport->efc, "efc_hw_port_free failed\n");
+                       /* Not much we can do, free the nport anyways */
+                       efc_nport_free(nport);
+               }
+               break;
+       }
+       default:
+               __efc_nport_common(__func__, ctx, evt, arg);
+       }
+}
+
+void
+__efc_nport_wait_port_free(struct efc_sm_ctx *ctx,
+                          enum efc_sm_event evt, void *arg)
+{
+       struct efc_nport *nport = ctx->app;
+
+       nport_sm_trace(nport);
+
+       switch (evt) {
+       case EFC_EVT_NPORT_ATTACH_OK:
+               /* Ignore as we are waiting for the free CB */
+               break;
+       case EFC_EVT_NPORT_FREE_OK: {
+               /* All done, free myself */
+               efc_nport_free(nport);
+               break;
+       }
+       default:
+               __efc_nport_common(__func__, ctx, evt, arg);
+       }
+}
+
+static int
+efc_vport_nport_alloc(struct efc_domain *domain, struct efc_vport *vport)
+{
+       struct efc_nport *nport;
+
+       lockdep_assert_held(&domain->efc->lock);
+
+       nport = efc_nport_alloc(domain, vport->wwpn, vport->wwnn, vport->fc_id,
+                               vport->enable_ini, vport->enable_tgt);
+       vport->nport = nport;
+       if (!nport)
+               return -EIO;
+
+       kref_get(&nport->ref);
+       nport->is_vport = true;
+       nport->tgt_data = vport->tgt_data;
+       nport->ini_data = vport->ini_data;
+
+       efc_sm_transition(&nport->sm, __efc_nport_vport_init, NULL);
+
+       return 0;
+}
+
+int
+efc_vport_start(struct efc_domain *domain)
+{
+       struct efc *efc = domain->efc;
+       struct efc_vport *vport;
+       struct efc_vport *next;
+       int rc = 0;
+       unsigned long flags = 0;
+
+       /* Use the vport spec to find the associated vports and start them */
+       spin_lock_irqsave(&efc->vport_lock, flags);
+       list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
+               if (!vport->nport) {
+                       if (efc_vport_nport_alloc(domain, vport))
+                               rc = -EIO;
+               }
+       }
+       spin_unlock_irqrestore(&efc->vport_lock, flags);
+
+       return rc;
+}
+
+int
+efc_nport_vport_new(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
+                   u32 fc_id, bool ini, bool tgt, void *tgt_data,
+                   void *ini_data)
+{
+       struct efc *efc = domain->efc;
+       struct efc_vport *vport;
+       int rc = 0;
+       unsigned long flags = 0;
+
+       if (ini && domain->efc->enable_ini == 0) {
+               efc_log_debug(efc, "driver initiator mode not enabled\n");
+               return -EIO;
+       }
+
+       if (tgt && domain->efc->enable_tgt == 0) {
+               efc_log_debug(efc, "driver target mode not enabled\n");
+               return -EIO;
+       }
+
+       /*
+        * Create a vport spec if we need to recreate
+        * this vport after a link up event
+        */
+       vport = efc_vport_create_spec(domain->efc, wwnn, wwpn, fc_id, ini, tgt,
+                                     tgt_data, ini_data);
+       if (!vport) {
+               efc_log_err(efc, "failed to create vport object entry\n");
+               return -EIO;
+       }
+
+       spin_lock_irqsave(&efc->lock, flags);
+       rc = efc_vport_nport_alloc(domain, vport);
+       spin_unlock_irqrestore(&efc->lock, flags);
+
+       return rc;
+}
+
+int
+efc_nport_vport_del(struct efc *efc, struct efc_domain *domain,
+                   u64 wwpn, uint64_t wwnn)
+{
+       struct efc_nport *nport;
+       struct efc_vport *vport;
+       struct efc_vport *next;
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&efc->vport_lock, flags);
+       /* walk the efc_vport_list and remove from there */
+       list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
+               if (vport->wwpn == wwpn && vport->wwnn == wwnn) {
+                       list_del(&vport->list_entry);
+                       kfree(vport);
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&efc->vport_lock, flags);
+
+       if (!domain) {
+               /* No domain means no nport to look for */
+               return 0;
+       }
+
+       spin_lock_irqsave(&efc->lock, flags);
+       list_for_each_entry(nport, &domain->nport_list, list_entry) {
+               if (nport->wwpn == wwpn && nport->wwnn == wwnn) {
+                       kref_put(&nport->ref, nport->release);
+                       /* Shutdown this NPORT */
+                       efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL);
+                       break;
+               }
+       }
+
+       spin_unlock_irqrestore(&efc->lock, flags);
+       return 0;
+}
+
+void
+efc_vport_del_all(struct efc *efc)
+{
+       struct efc_vport *vport;
+       struct efc_vport *next;
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&efc->vport_lock, flags);
+       list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
+               list_del(&vport->list_entry);
+               kfree(vport);
+       }
+       spin_unlock_irqrestore(&efc->vport_lock, flags);
+}
+
+struct efc_vport *
+efc_vport_create_spec(struct efc *efc, uint64_t wwnn, uint64_t wwpn,
+                     u32 fc_id, bool enable_ini,
+                     bool enable_tgt, void *tgt_data, void *ini_data)
+{
+       struct efc_vport *vport;
+       unsigned long flags = 0;
+
+       /*
+        * walk the efc_vport_list and return failure
+        * if a valid(vport with non zero WWPN and WWNN) vport entry
+        * is already created
+        */
+       spin_lock_irqsave(&efc->vport_lock, flags);
+       list_for_each_entry(vport, &efc->vport_list, list_entry) {
+               if ((wwpn && vport->wwpn == wwpn) &&
+                   (wwnn && vport->wwnn == wwnn)) {
+                       efc_log_err(efc,
+                                   "VPORT %016llX %016llX already allocated\n",
+                                   wwnn, wwpn);
+                       spin_unlock_irqrestore(&efc->vport_lock, flags);
+                       return NULL;
+               }
+       }
+
+       vport = kzalloc(sizeof(*vport), GFP_ATOMIC);
+       if (!vport) {
+               spin_unlock_irqrestore(&efc->vport_lock, flags);
+               return NULL;
+       }
+
+       vport->wwnn = wwnn;
+       vport->wwpn = wwpn;
+       vport->fc_id = fc_id;
+       vport->enable_tgt = enable_tgt;
+       vport->enable_ini = enable_ini;
+       vport->tgt_data = tgt_data;
+       vport->ini_data = ini_data;
+
+       INIT_LIST_HEAD(&vport->list_entry);
+       list_add_tail(&vport->list_entry, &efc->vport_list);
+       spin_unlock_irqrestore(&efc->vport_lock, flags);
+       return vport;
+}
diff --git a/drivers/scsi/elx/libefc/efc_nport.h b/drivers/scsi/elx/libefc/efc_nport.h
new file mode 100644 (file)
index 0000000..b575ea2
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/**
+ * EFC FC port (NPORT) exported declarations
+ *
+ */
+
+#ifndef __EFC_NPORT_H__
+#define __EFC_NPORT_H__
+
+struct efc_nport *
+efc_nport_find(struct efc_domain *domain, u32 d_id);
+struct efc_nport *
+efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
+               u32 fc_id, bool enable_ini, bool enable_tgt);
+void
+efc_nport_free(struct efc_nport *nport);
+int
+efc_nport_attach(struct efc_nport *nport, u32 fc_id);
+
+void
+__efc_nport_allocated(struct efc_sm_ctx *ctx,
+                     enum efc_sm_event evt, void *arg);
+void
+__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx,
+                         enum efc_sm_event evt, void *arg);
+void
+__efc_nport_wait_port_free(struct efc_sm_ctx *ctx,
+                          enum efc_sm_event evt, void *arg);
+void
+__efc_nport_vport_init(struct efc_sm_ctx *ctx,
+                      enum efc_sm_event evt, void *arg);
+void
+__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx,
+                            enum efc_sm_event evt, void *arg);
+void
+__efc_nport_vport_allocated(struct efc_sm_ctx *ctx,
+                           enum efc_sm_event evt, void *arg);
+void
+__efc_nport_attached(struct efc_sm_ctx *ctx,
+                    enum efc_sm_event evt, void *arg);
+
+int
+efc_vport_start(struct efc_domain *domain);
+
+#endif /* __EFC_NPORT_H__ */