scsi: lpfc: Rework FDMI initialization after link up
authorJames Smart <jsmart2021@gmail.com>
Fri, 6 May 2022 03:55:16 +0000 (20:55 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 11 May 2022 02:12:04 +0000 (22:12 -0400)
After a link up, it's possible for the switch to change FDMI support (e.g.
FDMI1 vs FDMI2 vs SmartSAN).  If the switch reverts to FDMI1, then the
revert is currently not detected.

Additionally, when NPIV is configured, it's possible the physical port's
RHBA is unprocessed by the switch before reciept of an NPIV port issued
RPRT.  This causes some switches vendors to reject the NPIV's RPRT.

Fix by reinitializing base FDMI mode on link up, and defer FDMI vport RPRT
submission until after confirming physical port's RHBA is completed.

Link: https://lore.kernel.org/r/20220506035519.50908-10-jsmart2021@gmail.com
Co-developed-by: Justin Tee <justin.tee@broadcom.com>
Signed-off-by: Justin Tee <justin.tee@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_crtn.h
drivers/scsi/lpfc/lpfc_ct.c
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_init.c

index f8f5b4a..da9070c 100644 (file)
@@ -612,6 +612,7 @@ struct lpfc_vport {
 #define FC_CT_RSNN_NN          0x4      /* RSNN_NN accepted by switch */
 #define FC_CT_RSPN_ID          0x8      /* RSPN_ID accepted by switch */
 #define FC_CT_RFT_ID           0x10     /* RFT_ID accepted by switch */
+#define FC_CT_RPRT_DEFER       0x20     /* Defer issuing FDMI RPRT */
 
        struct list_head fc_nodes;
 
@@ -1059,6 +1060,7 @@ struct lpfc_hba {
 #define HBA_HBEAT_INP          0x4000000 /* mbox HBEAT is in progress */
 #define HBA_HBEAT_TMO          0x8000000 /* HBEAT initiated after timeout */
 #define HBA_FLOGI_OUTSTANDING  0x10000000 /* FLOGI is outstanding */
+#define HBA_RHBA_CMPL          0x20000000 /* RHBA FDMI command is successful */
 
        struct completion *fw_dump_cmpl; /* cmpl event tracker for fw_dump */
        uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/
index 6f88fd0..b0775be 100644 (file)
@@ -434,6 +434,7 @@ void lpfc_nvmet_buf_free(struct lpfc_hba *phba, void *virtp, dma_addr_t dma);
 
 void lpfc_in_buf_free(struct lpfc_hba *, struct lpfc_dmabuf *);
 void lpfc_rq_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp);
+void lpfc_setup_fdmi_mask(struct lpfc_vport *vport);
 int lpfc_link_reset(struct lpfc_vport *vport);
 
 /* Function prototypes. */
index 094199d..9d36b20 100644 (file)
@@ -2168,6 +2168,41 @@ ns_cmd_exit:
 }
 
 /**
+ * lpfc_fdmi_rprt_defer - Check for any deferred FDMI RPRT commands
+ * @phba: Pointer to HBA context object.
+ * @mask: Initial port attributes mask
+ *
+ * This function checks to see if any vports have deferred their FDMI RPRT.
+ * A vports RPRT may be deferred if it is issued before the primary ports
+ * RHBA completes.
+ */
+static void
+lpfc_fdmi_rprt_defer(struct lpfc_hba *phba, uint32_t mask)
+{
+       struct lpfc_vport **vports;
+       struct lpfc_vport *vport;
+       struct lpfc_nodelist *ndlp;
+       int i;
+
+       phba->hba_flag |= HBA_RHBA_CMPL;
+       vports = lpfc_create_vport_work_array(phba);
+       if (vports) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+                       vport = vports[i];
+                       ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+                       if (!ndlp)
+                               continue;
+                       if (vport->ct_flags & FC_CT_RPRT_DEFER) {
+                               vport->ct_flags &= ~FC_CT_RPRT_DEFER;
+                               vport->fdmi_port_mask = mask;
+                               lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT, 0);
+                       }
+               }
+       }
+       lpfc_destroy_vport_work_array(phba, vports);
+}
+
+/**
  * lpfc_cmpl_ct_disc_fdmi - Handle a discovery FDMI completion
  * @phba: Pointer to HBA context object.
  * @cmdiocb: Pointer to the command IOCBQ.
@@ -2255,8 +2290,12 @@ lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                switch (cmd) {
                case SLI_MGMT_RHBA:
                        if (vport->fdmi_hba_mask == LPFC_FDMI2_HBA_ATTR) {
-                               /* Fallback to FDMI-1 */
+                               /* Fallback to FDMI-1 for HBA attributes */
                                vport->fdmi_hba_mask = LPFC_FDMI1_HBA_ATTR;
+
+                               /* If HBA attributes are FDMI1, so should
+                                * port attributes be for consistency.
+                                */
                                vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR;
                                /* Start over */
                                lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
@@ -2264,6 +2303,11 @@ lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                        return;
 
                case SLI_MGMT_RPRT:
+                       if (vport->port_type != LPFC_PHYSICAL_PORT) {
+                               ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+                               if (!ndlp)
+                                       return;
+                       }
                        if (vport->fdmi_port_mask == LPFC_FDMI2_PORT_ATTR) {
                                /* Fallback to FDMI-1 */
                                vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR;
@@ -2313,6 +2357,9 @@ lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
         */
        switch (cmd) {
        case SLI_MGMT_RHBA:
+               /* Check for any RPRTs deferred till after RHBA completes */
+               lpfc_fdmi_rprt_defer(phba, vport->fdmi_port_mask);
+
                lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPA, 0);
                break;
 
@@ -2321,10 +2368,26 @@ lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                break;
 
        case SLI_MGMT_DPRT:
-               if (vport->port_type == LPFC_PHYSICAL_PORT)
+               if (vport->port_type == LPFC_PHYSICAL_PORT) {
                        lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RHBA, 0);
-               else
-                       lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT, 0);
+               } else {
+                       ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+                       if (!ndlp)
+                               return;
+
+                       /* Only issue a RPRT for the vport if the RHBA
+                        * for the physical port completes successfully.
+                        * We may have to defer the RPRT accordingly.
+                        */
+                       if (phba->hba_flag & HBA_RHBA_CMPL) {
+                               lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT, 0);
+                       } else {
+                               lpfc_printf_vlog(vport, KERN_INFO,
+                                                LOG_DISCOVERY,
+                                                "6078 RPRT deferred\n");
+                               vport->ct_flags |= FC_CT_RPRT_DEFER;
+                       }
+               }
                break;
        case SLI_MGMT_RPA:
                if (vport->port_type == LPFC_PHYSICAL_PORT &&
@@ -2339,7 +2402,8 @@ lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                                break;
                        }
 
-                       lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+                       lpfc_printf_log(phba, KERN_INFO,
+                                       LOG_DISCOVERY | LOG_CGN_MGMT,
                                        "6210 Issue Vendor MI FDMI %x\n",
                                        phba->sli4_hba.pc_sli4_params.mi_ver);
 
@@ -2408,6 +2472,9 @@ lpfc_fdmi_change_check(struct lpfc_vport *vport)
                        phba->link_flag &= ~LS_CT_VEN_RPA;
                        lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
                } else {
+                       ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+                       if (!ndlp)
+                               return;
                        lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT, 0);
                }
 
@@ -2429,6 +2496,9 @@ lpfc_fdmi_change_check(struct lpfc_vport *vport)
                lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPA,
                              LPFC_FDMI_PORT_ATTR_num_disc);
        } else {
+               ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+               if (!ndlp)
+                       return;
                lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT,
                              LPFC_FDMI_PORT_ATTR_num_disc);
        }
@@ -3501,8 +3571,10 @@ lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
 
        /* FDMI request */
        lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
-                        "0218 FDMI Request Data: x%x x%x x%x\n",
-                        vport->fc_flag, vport->port_state, cmdcode);
+                        "0218 FDMI Request x%x mask x%x Data: x%x x%x x%x\n",
+                        cmdcode, new_mask, vport->fdmi_port_mask,
+                        vport->fc_flag, vport->port_state);
+
        CtReq = (struct lpfc_sli_ct_request *)mp->virt;
 
        /* First populate the CT_IU preamble */
@@ -3571,6 +3643,12 @@ hba_out:
                break;
 
        case SLI_MGMT_RPRT:
+               if (vport->port_type != LPFC_PHYSICAL_PORT) {
+                       ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+                       if (!ndlp)
+                               return 0;
+               }
+               fallthrough;
        case SLI_MGMT_RPA:
                pab = (struct lpfc_fdmi_reg_portattr *)&CtReq->un.PortID;
                if (cmdcode == SLI_MGMT_RPRT) {
@@ -3635,6 +3713,12 @@ port_out:
                rsp_size = FC_MAX_NS_RSP;
                fallthrough;
        case SLI_MGMT_DPRT:
+               if (vport->port_type != LPFC_PHYSICAL_PORT) {
+                       ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+                       if (!ndlp)
+                               return 0;
+               }
+               fallthrough;
        case SLI_MGMT_DPA:
                pe = (struct lpfc_fdmi_port_entry *)&CtReq->un.PortID;
                memcpy((uint8_t *)&pe->PortName,
index 3ab22ac..fb36f26 100644 (file)
@@ -1359,6 +1359,7 @@ lpfc_linkup_port(struct lpfc_vport *vport)
        vport->fc_flag |= FC_NDISC_ACTIVE;
        vport->fc_ns_retry = 0;
        spin_unlock_irq(shost->host_lock);
+       lpfc_setup_fdmi_mask(vport);
 
        lpfc_linkup_cleanup_nodes(vport);
 }
@@ -1390,8 +1391,8 @@ lpfc_linkup(struct lpfc_hba *phba)
        phba->pport->rcv_flogi_cnt = 0;
        spin_unlock_irq(shost->host_lock);
 
-       /* reinitialize initial FLOGI flag */
-       phba->hba_flag &= ~(HBA_FLOGI_ISSUED);
+       /* reinitialize initial HBA flag */
+       phba->hba_flag &= ~(HBA_FLOGI_ISSUED | HBA_RHBA_CMPL);
        phba->defer_flogi_acc_flag = false;
 
        return 0;
index 3926701..0dedb7c 100644 (file)
@@ -9003,6 +9003,36 @@ lpfc_hba_free(struct lpfc_hba *phba)
 }
 
 /**
+ * lpfc_setup_fdmi_mask - Setup initial FDMI mask for HBA and Port attributes
+ * @vport: pointer to lpfc vport data structure.
+ *
+ * This routine is will setup initial FDMI attribute masks for
+ * FDMI2 or SmartSAN depending on module parameters. The driver will attempt
+ * to get these attributes first before falling back, the attribute
+ * fallback hierarchy is SmartSAN -> FDMI2 -> FMDI1
+ **/
+void
+lpfc_setup_fdmi_mask(struct lpfc_vport *vport)
+{
+       struct lpfc_hba *phba = vport->phba;
+
+       vport->load_flag |= FC_ALLOW_FDMI;
+       if (phba->cfg_enable_SmartSAN ||
+           phba->cfg_fdmi_on == LPFC_FDMI_SUPPORT) {
+               /* Setup appropriate attribute masks */
+               vport->fdmi_hba_mask = LPFC_FDMI2_HBA_ATTR;
+               if (phba->cfg_enable_SmartSAN)
+                       vport->fdmi_port_mask = LPFC_FDMI2_SMART_ATTR;
+               else
+                       vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
+       }
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "6077 Setup FDMI mask: hba x%x port x%x\n",
+                       vport->fdmi_hba_mask, vport->fdmi_port_mask);
+}
+
+/**
  * lpfc_create_shost - Create hba physical port with associated scsi host.
  * @phba: pointer to lpfc hba data structure.
  *
@@ -9045,21 +9075,12 @@ lpfc_create_shost(struct lpfc_hba *phba)
        /* Put reference to SCSI host to driver's device private data */
        pci_set_drvdata(phba->pcidev, shost);
 
+       lpfc_setup_fdmi_mask(vport);
+
        /*
         * At this point we are fully registered with PSA. In addition,
         * any initial discovery should be completed.
         */
-       vport->load_flag |= FC_ALLOW_FDMI;
-       if (phba->cfg_enable_SmartSAN ||
-           (phba->cfg_fdmi_on == LPFC_FDMI_SUPPORT)) {
-
-               /* Setup appropriate attribute masks */
-               vport->fdmi_hba_mask = LPFC_FDMI2_HBA_ATTR;
-               if (phba->cfg_enable_SmartSAN)
-                       vport->fdmi_port_mask = LPFC_FDMI2_SMART_ATTR;
-               else
-                       vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
-       }
        return 0;
 }