scsi: qla2xxx: set UNLOADING before waiting for session deletion
[linux-2.6-microblaze.git] / drivers / scsi / lpfc / lpfc_els.c
index 42a2bf3..80d1e66 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2020 Broadcom. All Rights Reserved. The term *
  * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.     *
  * Copyright (C) 2004-2016 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
@@ -3008,10 +3008,9 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
  * This routine is a generic completion callback function for ELS commands.
  * Specifically, it is the callback function which does not need to perform
  * any command specific operations. It is currently used by the ELS command
- * issuing routines for the ELS State Change  Request (SCR),
- * lpfc_issue_els_scr(), and the ELS Fibre Channel Address Resolution
- * Protocol Response (FARPR) routine, lpfc_issue_els_farpr(). Other than
- * certain debug loggings, this callback function simply invokes the
+ * issuing routines for RSCN, lpfc_issue_els_rscn, and the ELS Fibre Channel
+ * Address Resolution Protocol Response (FARPR) routine, lpfc_issue_els_farpr().
+ * Other than certain debug loggings, this callback function simply invokes the
  * lpfc_els_chk_latt() routine to check whether link went down during the
  * discovery process.
  **/
@@ -3024,15 +3023,118 @@ lpfc_cmpl_els_cmd(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 
        irsp = &rspiocb->iocb;
 
+       lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+                             "ELS cmd cmpl:    status:x%x/x%x did:x%x",
+                             irsp->ulpStatus, irsp->un.ulpWord[4],
+                             irsp->un.elsreq64.remoteID);
+
+       /* ELS cmd tag <ulpIoTag> completes */
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+                        "0106 ELS cmd tag x%x completes Data: x%x x%x x%x\n",
+                        irsp->ulpIoTag, irsp->ulpStatus,
+                        irsp->un.ulpWord[4], irsp->ulpTimeout);
+
+       /* Check to see if link went down during discovery */
+       lpfc_els_chk_latt(vport);
+       lpfc_els_free_iocb(phba, cmdiocb);
+}
+
+/**
+ * lpfc_cmpl_els_disc_cmd - Completion callback function for Discovery ELS cmd
+ * @phba: pointer to lpfc hba data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @rspiocb: pointer to lpfc response iocb data structure.
+ *
+ * This routine is a generic completion callback function for Discovery ELS cmd.
+ * Currently used by the ELS command issuing routines for the ELS State Change
+ * Request (SCR), lpfc_issue_els_scr() and the ELS RDF, lpfc_issue_els_rdf().
+ * These commands will be retried once only for ELS timeout errors.
+ **/
+static void
+lpfc_cmpl_els_disc_cmd(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+                      struct lpfc_iocbq *rspiocb)
+{
+       struct lpfc_vport *vport = cmdiocb->vport;
+       IOCB_t *irsp;
+       struct lpfc_els_rdf_rsp *prdf;
+       struct lpfc_dmabuf *pcmd, *prsp;
+       u32 *pdata;
+       u32 cmd;
+
+       irsp = &rspiocb->iocb;
+
        lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
                "ELS cmd cmpl:    status:x%x/x%x did:x%x",
                irsp->ulpStatus, irsp->un.ulpWord[4],
                irsp->un.elsreq64.remoteID);
        /* ELS cmd tag <ulpIoTag> completes */
        lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
-                        "0106 ELS cmd tag x%x completes Data: x%x x%x x%x\n",
+                        "0217 ELS cmd tag x%x completes Data: x%x x%x x%x "
+                        "x%x\n",
                         irsp->ulpIoTag, irsp->ulpStatus,
-                        irsp->un.ulpWord[4], irsp->ulpTimeout);
+                        irsp->un.ulpWord[4], irsp->ulpTimeout,
+                        cmdiocb->retry);
+
+       pcmd = (struct lpfc_dmabuf *)cmdiocb->context2;
+       if (!pcmd)
+               goto out;
+
+       pdata = (u32 *)pcmd->virt;
+       if (!pdata)
+               goto out;
+       cmd = *pdata;
+
+       /* Only 1 retry for ELS Timeout only */
+       if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT &&
+           ((irsp->un.ulpWord[4] & IOERR_PARAM_MASK) ==
+           IOERR_SEQUENCE_TIMEOUT)) {
+               cmdiocb->retry++;
+               if (cmdiocb->retry <= 1) {
+                       switch (cmd) {
+                       case ELS_CMD_SCR:
+                               lpfc_issue_els_scr(vport, cmdiocb->retry);
+                               break;
+                       case ELS_CMD_RDF:
+                               cmdiocb->context1 = NULL; /* save ndlp refcnt */
+                               lpfc_issue_els_rdf(vport, cmdiocb->retry);
+                               break;
+                       }
+                       goto out;
+               }
+               phba->fc_stat.elsRetryExceeded++;
+       }
+       if (irsp->ulpStatus) {
+               /* ELS discovery cmd completes with error */
+               lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS,
+                                "4203 ELS cmd x%x error: x%x x%X\n", cmd,
+                                irsp->ulpStatus, irsp->un.ulpWord[4]);
+               goto out;
+       }
+
+       /* The RDF response doesn't have any impact on the running driver
+        * but the notification descriptors are dumped here for support.
+        */
+       if (cmd == ELS_CMD_RDF) {
+               int i;
+
+               prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list);
+               if (!prsp)
+                       goto out;
+
+               prdf = (struct lpfc_els_rdf_rsp *)prsp->virt;
+               if (!prdf)
+                       goto out;
+
+               for (i = 0; i < ELS_RDF_REG_TAG_CNT &&
+                           i < be32_to_cpu(prdf->reg_d1.reg_desc.count); i++)
+                       lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+                                "4677 Fabric RDF Notification Grant Data: "
+                                "0x%08x\n",
+                                be32_to_cpu(
+                                       prdf->reg_d1.desc_tags[i]));
+       }
+
+out:
        /* Check to see if link went down during discovery */
        lpfc_els_chk_latt(vport);
        lpfc_els_free_iocb(phba, cmdiocb);
@@ -3042,11 +3144,10 @@ lpfc_cmpl_els_cmd(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 /**
  * lpfc_issue_els_scr - Issue a scr to an node on a vport
  * @vport: pointer to a host virtual N_Port data structure.
- * @nportid: N_Port identifier to the remote node.
- * @retry: number of retries to the command IOCB.
+ * @retry: retry counter for the command IOCB.
  *
  * This routine issues a State Change Request (SCR) to a fabric node
- * on a @vport. The remote node @nportid is passed into the function. It
+ * on a @vport. The remote node is Fabric Controller (0xfffffd). It
  * first search the @vport node list to find the matching ndlp. If no such
  * ndlp is found, a new ndlp shall be created for this (SCR) purpose. An
  * IOCB is allocated, payload prepared, and the lpfc_sli_issue_iocb()
@@ -3062,7 +3163,7 @@ lpfc_cmpl_els_cmd(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
  *   1 - Failed to issue scr command
  **/
 int
-lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
+lpfc_issue_els_scr(struct lpfc_vport *vport, uint8_t retry)
 {
        struct lpfc_hba  *phba = vport->phba;
        struct lpfc_iocbq *elsiocb;
@@ -3072,9 +3173,9 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
 
        cmdsize = (sizeof(uint32_t) + sizeof(SCR));
 
-       ndlp = lpfc_findnode_did(vport, nportid);
+       ndlp = lpfc_findnode_did(vport, Fabric_Cntl_DID);
        if (!ndlp) {
-               ndlp = lpfc_nlp_init(vport, nportid);
+               ndlp = lpfc_nlp_init(vport, Fabric_Cntl_DID);
                if (!ndlp)
                        return 1;
                lpfc_enqueue_node(vport, ndlp);
@@ -3109,7 +3210,7 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
                ndlp->nlp_DID, 0, 0);
 
        phba->fc_stat.elsXmitSCR++;
-       elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_disc_cmd;
        if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
            IOCB_ERROR) {
                /* The additional lpfc_nlp_put will cause the following
@@ -3339,6 +3440,102 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
        /* This will cause the callback-function lpfc_cmpl_els_cmd to
         * trigger the release of the node.
         */
+       /* Don't release reference count as RDF is likely outstanding */
+       return 0;
+}
+
+/**
+ * lpfc_issue_els_rdf - Register for diagnostic functions from the fabric.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @retry: retry counter for the command IOCB.
+ *
+ * This routine issues an ELS RDF to the Fabric Controller to register
+ * for diagnostic functions.
+ *
+ * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
+ * will be incremented by 1 for holding the ndlp and the reference to ndlp
+ * will be stored into the context1 field of the IOCB for the completion
+ * callback function to the RDF ELS command.
+ *
+ * Return code
+ *   0 - Successfully issued rdf command
+ *   1 - Failed to issue rdf command
+ **/
+int
+lpfc_issue_els_rdf(struct lpfc_vport *vport, uint8_t retry)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_els_rdf_req *prdf;
+       struct lpfc_nodelist *ndlp;
+       uint16_t cmdsize;
+
+       cmdsize = sizeof(*prdf);
+
+       ndlp = lpfc_findnode_did(vport, Fabric_Cntl_DID);
+       if (!ndlp) {
+               ndlp = lpfc_nlp_init(vport, Fabric_Cntl_DID);
+               if (!ndlp)
+                       return -ENODEV;
+               lpfc_enqueue_node(vport, ndlp);
+       } else if (!NLP_CHK_NODE_ACT(ndlp)) {
+               ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE);
+               if (!ndlp)
+                       return -ENODEV;
+       }
+
+       /* RDF ELS is not required on an NPIV VN_Port.  */
+       if (vport->port_type == LPFC_NPIV_PORT) {
+               lpfc_nlp_put(ndlp);
+               return -EACCES;
+       }
+
+       elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
+                                    ndlp->nlp_DID, ELS_CMD_RDF);
+       if (!elsiocb) {
+               /* This will trigger the release of the node just
+                * allocated
+                */
+               lpfc_nlp_put(ndlp);
+               return -ENOMEM;
+       }
+
+       /* Configure the payload for the supported FPIN events. */
+       prdf = (struct lpfc_els_rdf_req *)
+               (((struct lpfc_dmabuf *)elsiocb->context2)->virt);
+       memset(prdf, 0, cmdsize);
+       prdf->rdf.fpin_cmd = ELS_RDF;
+       prdf->rdf.desc_len = cpu_to_be32(sizeof(struct lpfc_els_rdf_req) -
+                                        sizeof(struct fc_els_rdf));
+       prdf->reg_d1.reg_desc.desc_tag = cpu_to_be32(ELS_DTAG_FPIN_REGISTER);
+       prdf->reg_d1.reg_desc.desc_len = cpu_to_be32(
+                               FC_TLV_DESC_LENGTH_FROM_SZ(prdf->reg_d1));
+       prdf->reg_d1.reg_desc.count = cpu_to_be32(ELS_RDF_REG_TAG_CNT);
+       prdf->reg_d1.desc_tags[0] = cpu_to_be32(ELS_DTAG_LNK_INTEGRITY);
+
+       lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+                             "Issue RDF:       did:x%x",
+                             ndlp->nlp_DID, 0, 0);
+
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+                        "6444 Xmit RDF to remote NPORT x%x\n",
+                        ndlp->nlp_DID);
+
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_disc_cmd;
+       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+           IOCB_ERROR) {
+               /* The additional lpfc_nlp_put will cause the following
+                * lpfc_els_free_iocb routine to trigger the rlease of
+                * the node.
+                */
+               lpfc_nlp_put(ndlp);
+               lpfc_els_free_iocb(phba, elsiocb);
+               return -EIO;
+       }
+
+       /* An RDF was issued - this put ensures the ndlp is cleaned up
+        * when the RDF completes.
+        */
        lpfc_nlp_put(ndlp);
        return 0;
 }
@@ -7134,109 +7331,13 @@ lpfc_els_rsp_rls_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
                lpfc_els_free_iocb(phba, elsiocb);
 }
 
-/**
- * lpfc_els_rsp_rps_acc - Completion callbk func for MBX_READ_LNK_STAT mbox cmd
- * @phba: pointer to lpfc hba data structure.
- * @pmb: pointer to the driver internal queue element for mailbox command.
- *
- * This routine is the completion callback function for the MBX_READ_LNK_STAT
- * mailbox command. This callback function is to actually send the Accept
- * (ACC) response to a Read Port Status (RPS) unsolicited IOCB event. It
- * collects the link statistics from the completion of the MBX_READ_LNK_STAT
- * mailbox command, constructs the RPS response with the link statistics
- * collected, and then invokes the lpfc_sli_issue_iocb() routine to send ACC
- * response to the RPS.
- *
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the RPS Accept Response ELS IOCB command.
- *
- **/
-static void
-lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
-{
-       MAILBOX_t *mb;
-       IOCB_t *icmd;
-       RPS_RSP *rps_rsp;
-       uint8_t *pcmd;
-       struct lpfc_iocbq *elsiocb;
-       struct lpfc_nodelist *ndlp;
-       uint16_t status;
-       uint16_t oxid;
-       uint16_t rxid;
-       uint32_t cmdsize;
-
-       mb = &pmb->u.mb;
-
-       ndlp = (struct lpfc_nodelist *)pmb->ctx_ndlp;
-       rxid = (uint16_t)((unsigned long)(pmb->ctx_buf) & 0xffff);
-       oxid = (uint16_t)(((unsigned long)(pmb->ctx_buf) >> 16) & 0xffff);
-       pmb->ctx_ndlp = NULL;
-       pmb->ctx_buf = NULL;
-
-       if (mb->mbxStatus) {
-               mempool_free(pmb, phba->mbox_mem_pool);
-               return;
-       }
-
-       cmdsize = sizeof(RPS_RSP) + sizeof(uint32_t);
-       mempool_free(pmb, phba->mbox_mem_pool);
-       elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize,
-                                    lpfc_max_els_tries, ndlp,
-                                    ndlp->nlp_DID, ELS_CMD_ACC);
-
-       /* Decrement the ndlp reference count from previous mbox command */
-       lpfc_nlp_put(ndlp);
-
-       if (!elsiocb)
-               return;
-
-       icmd = &elsiocb->iocb;
-       icmd->ulpContext = rxid;
-       icmd->unsli3.rcvsli3.ox_id = oxid;
-
-       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
-       *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
-       pcmd += sizeof(uint32_t); /* Skip past command */
-       rps_rsp = (RPS_RSP *)pcmd;
-
-       if (phba->fc_topology != LPFC_TOPOLOGY_LOOP)
-               status = 0x10;
-       else
-               status = 0x8;
-       if (phba->pport->fc_flag & FC_FABRIC)
-               status |= 0x4;
-
-       rps_rsp->rsvd1 = 0;
-       rps_rsp->portStatus = cpu_to_be16(status);
-       rps_rsp->linkFailureCnt = cpu_to_be32(mb->un.varRdLnk.linkFailureCnt);
-       rps_rsp->lossSyncCnt = cpu_to_be32(mb->un.varRdLnk.lossSyncCnt);
-       rps_rsp->lossSignalCnt = cpu_to_be32(mb->un.varRdLnk.lossSignalCnt);
-       rps_rsp->primSeqErrCnt = cpu_to_be32(mb->un.varRdLnk.primSeqErrCnt);
-       rps_rsp->invalidXmitWord = cpu_to_be32(mb->un.varRdLnk.invalidXmitWord);
-       rps_rsp->crcCnt = cpu_to_be32(mb->un.varRdLnk.crcCnt);
-       /* Xmit ELS RPS ACC response tag <ulpIoTag> */
-       lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_ELS,
-                        "0118 Xmit ELS RPS ACC response tag x%x xri x%x, "
-                        "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x\n",
-                        elsiocb->iotag, elsiocb->iocb.ulpContext,
-                        ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
-                        ndlp->nlp_rpi);
-       elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
-       phba->fc_stat.elsXmitACC++;
-       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) == IOCB_ERROR)
-               lpfc_els_free_iocb(phba, elsiocb);
-       return;
-}
-
 /**
  * lpfc_els_rcv_rls - Process an unsolicited rls iocb
  * @vport: pointer to a host virtual N_Port data structure.
  * @cmdiocb: pointer to lpfc command iocb data structure.
  * @ndlp: pointer to a node-list data structure.
  *
- * This routine processes Read Port Status (RPL) IOCB received as an
+ * This routine processes Read Link Status (RLS) IOCB received as an
  * ELS unsolicited event. It first checks the remote port state. If the
  * remote port is not in NLP_STE_UNMAPPED_NODE state or NLP_STE_MAPPED_NODE
  * state, it invokes the lpfc_els_rsl_reject() routine to send the reject
@@ -7258,7 +7359,7 @@ lpfc_els_rcv_rls(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 
        if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
            (ndlp->nlp_state != NLP_STE_MAPPED_NODE))
-               /* reject the unsolicited RPS request and done with it */
+               /* reject the unsolicited RLS request and done with it */
                goto reject_out;
 
        mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC);
@@ -7306,7 +7407,7 @@ reject_out:
  * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
  * will be incremented by 1 for holding the ndlp and the reference to ndlp
  * will be stored into the context1 field of the IOCB for the completion
- * callback function to the RPS Accept Response ELS IOCB command.
+ * callback function to the RTV Accept Response ELS IOCB command.
  *
  * Return codes
  *   0 - Successfully processed rtv iocb (currently always return 0)
@@ -7325,7 +7426,7 @@ lpfc_els_rcv_rtv(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 
        if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
            (ndlp->nlp_state != NLP_STE_MAPPED_NODE))
-               /* reject the unsolicited RPS request and done with it */
+               /* reject the unsolicited RTV request and done with it */
                goto reject_out;
 
        cmdsize = sizeof(struct RTV_RSP) + sizeof(uint32_t);
@@ -7378,84 +7479,7 @@ reject_out:
        return 0;
 }
 
-/* lpfc_els_rcv_rps - Process an unsolicited rps iocb
- * @vport: pointer to a host virtual N_Port data structure.
- * @cmdiocb: pointer to lpfc command iocb data structure.
- * @ndlp: pointer to a node-list data structure.
- *
- * This routine processes Read Port Status (RPS) IOCB received as an
- * ELS unsolicited event. It first checks the remote port state. If the
- * remote port is not in NLP_STE_UNMAPPED_NODE state or NLP_STE_MAPPED_NODE
- * state, it invokes the lpfc_els_rsp_reject() routine to send the reject
- * response. Otherwise, it issue the MBX_READ_LNK_STAT mailbox command
- * for reading the HBA link statistics. It is for the callback function,
- * lpfc_els_rsp_rps_acc(), set to the MBX_READ_LNK_STAT mailbox command
- * to actually sending out RPS Accept (ACC) response.
- *
- * Return codes
- *   0 - Successfully processed rps iocb (currently always return 0)
- **/
-static int
-lpfc_els_rcv_rps(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
-                struct lpfc_nodelist *ndlp)
-{
-       struct lpfc_hba *phba = vport->phba;
-       uint32_t *lp;
-       uint8_t flag;
-       LPFC_MBOXQ_t *mbox;
-       struct lpfc_dmabuf *pcmd;
-       RPS *rps;
-       struct ls_rjt stat;
-
-       if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
-           (ndlp->nlp_state != NLP_STE_MAPPED_NODE))
-               /* reject the unsolicited RPS request and done with it */
-               goto reject_out;
-
-       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
-       lp = (uint32_t *) pcmd->virt;
-       flag = (be32_to_cpu(*lp++) & 0xf);
-       rps = (RPS *) lp;
-
-       if ((flag == 0) ||
-           ((flag == 1) && (be32_to_cpu(rps->un.portNum) == 0)) ||
-           ((flag == 2) && (memcmp(&rps->un.portName, &vport->fc_portname,
-                                   sizeof(struct lpfc_name)) == 0))) {
-
-               printk("Fix me....\n");
-               dump_stack();
-               mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC);
-               if (mbox) {
-                       lpfc_read_lnk_stat(phba, mbox);
-                       mbox->ctx_buf = (void *)((unsigned long)
-                               ((cmdiocb->iocb.unsli3.rcvsli3.ox_id << 16) |
-                               cmdiocb->iocb.ulpContext)); /* rx_id */
-                       mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
-                       mbox->vport = vport;
-                       mbox->mbox_cmpl = lpfc_els_rsp_rps_acc;
-                       if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT)
-                               != MBX_NOT_FINISHED)
-                               /* Mbox completion will send ELS Response */
-                               return 0;
-                       /* Decrement reference count used for the failed mbox
-                        * command.
-                        */
-                       lpfc_nlp_put(ndlp);
-                       mempool_free(mbox, phba->mbox_mem_pool);
-               }
-       }
-
-reject_out:
-       /* issue rejection response */
-       stat.un.b.lsRjtRsvd0 = 0;
-       stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
-       stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
-       stat.un.b.vendorUnique = 0;
-       lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
-       return 0;
-}
-
-/* lpfc_issue_els_rrq - Process an unsolicited rps iocb
+/* lpfc_issue_els_rrq - Process an unsolicited rrq iocb
  * @vport: pointer to a host virtual N_Port data structure.
  * @ndlp: pointer to a node-list data structure.
  * @did: DID of the target.
@@ -8310,6 +8334,90 @@ lpfc_send_els_event(struct lpfc_vport *vport,
 }
 
 
+DECLARE_ENUM2STR_LOOKUP(lpfc_get_tlv_dtag_nm, fc_ls_tlv_dtag,
+                       FC_LS_TLV_DTAG_INIT);
+
+DECLARE_ENUM2STR_LOOKUP(lpfc_get_fpin_li_event_nm, fc_fpin_li_event_types,
+                       FC_FPIN_LI_EVT_TYPES_INIT);
+
+/**
+ * lpfc_els_rcv_fpin_li - Process an FPIN Link Integrity Event.
+ * @vport: Pointer to vport object.
+ * @lnk_not:  Pointer to the Link Integrity Notification Descriptor.
+ *
+ * This function processes a link integrity FPIN event by
+ * logging a message
+ **/
+static void
+lpfc_els_rcv_fpin_li(struct lpfc_vport *vport, struct fc_tlv_desc *tlv)
+{
+       struct fc_fn_li_desc *li = (struct fc_fn_li_desc *)tlv;
+       const char *li_evt_str;
+       u32 li_evt;
+
+       li_evt = be16_to_cpu(li->event_type);
+       li_evt_str = lpfc_get_fpin_li_event_nm(li_evt);
+
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+                        "4680 FPIN Link Integrity %s (x%x) "
+                        "Detecting PN x%016llx Attached PN x%016llx "
+                        "Duration %d mSecs Count %d Port Cnt %d\n",
+                        li_evt_str, li_evt,
+                        be64_to_cpu(li->detecting_wwpn),
+                        be64_to_cpu(li->attached_wwpn),
+                        be32_to_cpu(li->event_threshold),
+                        be32_to_cpu(li->event_count),
+                        be32_to_cpu(li->pname_count));
+}
+
+static void
+lpfc_els_rcv_fpin(struct lpfc_vport *vport, struct fc_els_fpin *fpin,
+                 u32 fpin_length)
+{
+       struct fc_tlv_desc *tlv;
+       const char *dtag_nm;
+       uint32_t desc_cnt = 0, bytes_remain;
+       u32 dtag;
+
+       /* FPINs handled only if we are in the right discovery state */
+       if (vport->port_state < LPFC_DISC_AUTH)
+               return;
+
+       /* make sure there is the full fpin header */
+       if (fpin_length < sizeof(struct fc_els_fpin))
+               return;
+
+       tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
+       bytes_remain = fpin_length - offsetof(struct fc_els_fpin, fpin_desc);
+       bytes_remain = min_t(u32, bytes_remain, be32_to_cpu(fpin->desc_len));
+
+       /* process each descriptor */
+       while (bytes_remain >= FC_TLV_DESC_HDR_SZ &&
+              bytes_remain >= FC_TLV_DESC_SZ_FROM_LENGTH(tlv)) {
+
+               dtag = be32_to_cpu(tlv->desc_tag);
+               switch (dtag) {
+               case ELS_DTAG_LNK_INTEGRITY:
+                       lpfc_els_rcv_fpin_li(vport, tlv);
+                       break;
+               default:
+                       dtag_nm = lpfc_get_tlv_dtag_nm(dtag);
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+                                        "4678  skipped FPIN descriptor[%d]: "
+                                        "tag x%x (%s)\n",
+                                        desc_cnt, dtag, dtag_nm);
+                       break;
+               }
+
+               desc_cnt++;
+               bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv);
+               tlv = fc_tlv_next_desc(tlv);
+       }
+
+       fc_host_fpin_rcv(lpfc_shost_from_vport(vport), fpin_length,
+                        (char *)fpin);
+}
+
 /**
  * lpfc_els_unsol_buffer - Process an unsolicited event data buffer
  * @phba: pointer to lpfc hba data structure.
@@ -8331,7 +8439,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
        struct Scsi_Host  *shost;
        struct lpfc_nodelist *ndlp;
        struct ls_rjt stat;
-       uint32_t *payload;
+       uint32_t *payload, payload_len;
        uint32_t cmd, did, newnode;
        uint8_t rjt_exp, rjt_err = 0, init_link = 0;
        IOCB_t *icmd = &elsiocb->iocb;
@@ -8342,6 +8450,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
 
        newnode = 0;
        payload = ((struct lpfc_dmabuf *)elsiocb->context2)->virt;
+       payload_len = elsiocb->iocb.unsli3.rcvsli3.acc_len;
        cmd = *payload;
        if ((phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) == 0)
                lpfc_post_buffer(phba, pring, 1);
@@ -8632,16 +8741,6 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                if (newnode)
                        lpfc_nlp_put(ndlp);
                break;
-       case ELS_CMD_RPS:
-               lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
-                       "RCV RPS:         did:x%x/ste:x%x flg:x%x",
-                       did, vport->port_state, ndlp->nlp_flag);
-
-               phba->fc_stat.elsRcvRPS++;
-               lpfc_els_rcv_rps(vport, elsiocb, ndlp);
-               if (newnode)
-                       lpfc_nlp_put(ndlp);
-               break;
        case ELS_CMD_RPL:
                lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
                        "RCV RPL:         did:x%x/ste:x%x flg:x%x",
@@ -8697,12 +8796,14 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                rjt_exp = LSEXP_INVALID_OX_RX;
                break;
        case ELS_CMD_FPIN:
-               /*
-                * Received FPIN from fabric - pass it to the
-                * transport FPIN handler.
-                */
-               fc_host_fpin_rcv(shost, elsiocb->iocb.unsli3.rcvsli3.acc_len,
-                               (char *)payload);
+               lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
+                                     "RCV FPIN:       did:x%x/ste:x%x flg:x%x",
+                                     did, vport->port_state, ndlp->nlp_flag);
+
+               lpfc_els_rcv_fpin(vport, (struct fc_els_fpin *)payload,
+                                 payload_len);
+
+               /* There are no replies, so no rjt codes */
                break;
        default:
                lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,