Daniel Wagner 99c48d
From: James Smart <jsmart2021@gmail.com>
Daniel Wagner 99c48d
Date: Fri, 14 May 2021 12:55:49 -0700
Daniel Wagner 99c48d
Subject: scsi: lpfc: Fix unreleased RPIs when NPIV ports are created
Daniel Wagner 99c48d
Patch-mainline: Queued in subsystem maintainer repository
Daniel Wagner 99c48d
Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git
Daniel Wagner 99c48d
Git-commit: 01131e7aae5d30e23e3cdd1eebe51bbc5489ae8f
Daniel Wagner 99c48d
References: bsc#1186452
Daniel Wagner 99c48d
Daniel Wagner 99c48d
While testing NPIV and watching logins and used RPI levels, it was seen the
Daniel Wagner 99c48d
used RPI count was much higher than the number of remote ports discovered.
Daniel Wagner 99c48d
Daniel Wagner 99c48d
Code inspection showed that remote port removals on any NPIV instance are
Daniel Wagner 99c48d
releasing the RPI, but not performing an UNREG_RPI with the adapter thus
Daniel Wagner 99c48d
the reference counting never fully drops and the RPI is never fully
Daniel Wagner 99c48d
released. This was happening on NPIV nodes due to a log of fabric ELS's to
Daniel Wagner 99c48d
fabric addresses. This lack of UNREG_RPI was introduced by a prior node
Daniel Wagner 99c48d
rework patch that performed the UNREG_RPI as part of node cleanup.
Daniel Wagner 99c48d
Daniel Wagner 99c48d
To resolve the issue, do the following:
Daniel Wagner 99c48d
Daniel Wagner 99c48d
 - Restore the RPI release code, but move the location to so that it is in
Daniel Wagner 99c48d
   line with the new node cleanup design.
Daniel Wagner 99c48d
Daniel Wagner 99c48d
 - NPIV ports now release the RPI and drop the node when the caller sets
Daniel Wagner 99c48d
   the NLP_RELEASE_RPI flag.
Daniel Wagner 99c48d
Daniel Wagner 99c48d
 - Set the NLP_RELEASE_RPI flag in node cleanup which will trigger a
Daniel Wagner 99c48d
   release of RPI to free pool.
Daniel Wagner 99c48d
Daniel Wagner 99c48d
 - Ensure there's an UNREG_RPI at LOGO completion so that RPI release is
Daniel Wagner 99c48d
   completed.
Daniel Wagner 99c48d
Daniel Wagner 99c48d
 - Stop offline_prep from skipping nodes that are UNUSED. The RPI may
Daniel Wagner 99c48d
   not have been released.
Daniel Wagner 99c48d
Daniel Wagner 99c48d
 - Stop the default RPI handling in lpfc_cmpl_els_rsp() for SLI4.
Daniel Wagner 99c48d
Daniel Wagner 99c48d
 - Fixed up debugfs RPI displays for better debugging.
Daniel Wagner 99c48d
Daniel Wagner 99c48d
Fixes: a70e63eee1c1 ("scsi: lpfc: Fix NPIV Fabric Node reference counting")
Daniel Wagner 99c48d
Link: https://lore.kernel.org/r/20210514195559.119853-2-jsmart2021@gmail.com
Daniel Wagner 99c48d
Cc: <stable@vger.kernel.org> # v5.11+
Daniel Wagner 99c48d
Co-developed-by: Justin Tee <justin.tee@broadcom.com>
Daniel Wagner 99c48d
Signed-off-by: Justin Tee <justin.tee@broadcom.com>
Daniel Wagner 99c48d
Signed-off-by: James Smart <jsmart2021@gmail.com>
Daniel Wagner 99c48d
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Daniel Wagner 99c48d
Acked-by: Daniel Wagner <dwagner@suse.de>
Daniel Wagner 99c48d
---
Daniel Wagner 99c48d
 drivers/scsi/lpfc/lpfc_debugfs.c   |    7 ---
Daniel Wagner 99c48d
 drivers/scsi/lpfc/lpfc_els.c       |   79 +++++++++++++++++++++++++++++--------
Daniel Wagner 99c48d
 drivers/scsi/lpfc/lpfc_hbadisc.c   |   27 +++++++++++-
Daniel Wagner 99c48d
 drivers/scsi/lpfc/lpfc_init.c      |    7 ---
Daniel Wagner 99c48d
 drivers/scsi/lpfc/lpfc_nportdisc.c |   25 +++++++----
Daniel Wagner 99c48d
 drivers/scsi/lpfc/lpfc_sli.c       |   10 +++-
Daniel Wagner 99c48d
 6 files changed, 115 insertions(+), 40 deletions(-)
Daniel Wagner 99c48d
Daniel Wagner 99c48d
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
Daniel Wagner 99c48d
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
Daniel Wagner 99c48d
@@ -871,11 +871,8 @@ lpfc_debugfs_nodelist_data(struct lpfc_v
Daniel Wagner 99c48d
 		len += scnprintf(buf+len, size-len,
Daniel Wagner 99c48d
 				"WWNN x%llx ",
Daniel Wagner 99c48d
 				wwn_to_u64(ndlp->nlp_nodename.u.wwn));
Daniel Wagner 99c48d
-		if (ndlp->nlp_flag & NLP_RPI_REGISTERED)
Daniel Wagner 99c48d
-			len += scnprintf(buf+len, size-len, "RPI:%04d ",
Daniel Wagner 99c48d
-					ndlp->nlp_rpi);
Daniel Wagner 99c48d
-		else
Daniel Wagner 99c48d
-			len += scnprintf(buf+len, size-len, "RPI:none ");
Daniel Wagner 99c48d
+		len += scnprintf(buf+len, size-len, "RPI:x%04x ",
Daniel Wagner 99c48d
+				 ndlp->nlp_rpi);
Daniel Wagner 99c48d
 		len +=  scnprintf(buf+len, size-len, "flag:x%08x ",
Daniel Wagner 99c48d
 			ndlp->nlp_flag);
Daniel Wagner 99c48d
 		if (!ndlp->nlp_type)
Daniel Wagner 99c48d
--- a/drivers/scsi/lpfc/lpfc_els.c
Daniel Wagner 99c48d
+++ b/drivers/scsi/lpfc/lpfc_els.c
Daniel Wagner 99c48d
@@ -2869,6 +2869,11 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba
Daniel Wagner 99c48d
 	 * log into the remote port.
Daniel Wagner 99c48d
 	 */
Daniel Wagner 99c48d
 	if (ndlp->nlp_flag & NLP_TARGET_REMOVE) {
Daniel Wagner 99c48d
+		spin_lock_irq(&ndlp->lock);
Daniel Wagner 99c48d
+		if (phba->sli_rev == LPFC_SLI_REV4)
Daniel Wagner 99c48d
+			ndlp->nlp_flag |= NLP_RELEASE_RPI;
Daniel Wagner 99c48d
+		ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
Daniel Wagner 99c48d
+		spin_unlock_irq(&ndlp->lock);
Daniel Wagner 99c48d
 		lpfc_disc_state_machine(vport, ndlp, cmdiocb,
Daniel Wagner 99c48d
 					NLP_EVT_DEVICE_RM);
Daniel Wagner 99c48d
 		lpfc_els_free_iocb(phba, cmdiocb);
Daniel Wagner 99c48d
@@ -4371,6 +4376,7 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *
Daniel Wagner 99c48d
 	struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
Daniel Wagner 99c48d
 	struct lpfc_vport *vport = cmdiocb->vport;
Daniel Wagner 99c48d
 	IOCB_t *irsp;
Daniel Wagner 99c48d
+	u32 xpt_flags = 0, did_mask = 0;
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
 	irsp = &rspiocb->iocb;
Daniel Wagner 99c48d
 	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
Daniel Wagner 99c48d
@@ -4386,9 +4392,20 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *
Daniel Wagner 99c48d
 	if (ndlp->nlp_state == NLP_STE_NPR_NODE) {
Daniel Wagner 99c48d
 		/* NPort Recovery mode or node is just allocated */
Daniel Wagner 99c48d
 		if (!lpfc_nlp_not_used(ndlp)) {
Daniel Wagner 99c48d
-			/* If the ndlp is being used by another discovery
Daniel Wagner 99c48d
-			 * thread, just unregister the RPI.
Daniel Wagner 99c48d
+			/* A LOGO is completing and the node is in NPR state.
Daniel Wagner 99c48d
+			 * If this a fabric node that cleared its transport
Daniel Wagner 99c48d
+			 * registration, release the rpi.
Daniel Wagner 99c48d
 			 */
Daniel Wagner 99c48d
+			xpt_flags = SCSI_XPT_REGD | NVME_XPT_REGD;
Daniel Wagner 99c48d
+			did_mask = ndlp->nlp_DID & Fabric_DID_MASK;
Daniel Wagner 99c48d
+			if (did_mask == Fabric_DID_MASK &&
Daniel Wagner 99c48d
+			    !(ndlp->fc4_xpt_flags & xpt_flags)) {
Daniel Wagner 99c48d
+				spin_lock_irq(&ndlp->lock);
Daniel Wagner 99c48d
+				ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
Daniel Wagner 99c48d
+				if (phba->sli_rev == LPFC_SLI_REV4)
Daniel Wagner 99c48d
+					ndlp->nlp_flag |= NLP_RELEASE_RPI;
Daniel Wagner 99c48d
+				spin_unlock_irq(&ndlp->lock);
Daniel Wagner 99c48d
+			}
Daniel Wagner 99c48d
 			lpfc_unreg_rpi(vport, ndlp);
Daniel Wagner 99c48d
 		} else {
Daniel Wagner 99c48d
 			/* Indicate the node has already released, should
Daniel Wagner 99c48d
@@ -4424,28 +4441,37 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *
Daniel Wagner 99c48d
 {
Daniel Wagner 99c48d
 	struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *)(pmb->ctx_buf);
Daniel Wagner 99c48d
 	struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *)pmb->ctx_ndlp;
Daniel Wagner 99c48d
+	u32 mbx_flag = pmb->mbox_flag;
Daniel Wagner 99c48d
+	u32 mbx_cmd = pmb->u.mb.mbxCommand;
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
 	pmb->ctx_buf = NULL;
Daniel Wagner 99c48d
 	pmb->ctx_ndlp = NULL;
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
-	lpfc_mbuf_free(phba, mp->virt, mp->phys);
Daniel Wagner 99c48d
-	kfree(mp);
Daniel Wagner 99c48d
-	mempool_free(pmb, phba->mbox_mem_pool);
Daniel Wagner 99c48d
 	if (ndlp) {
Daniel Wagner 99c48d
 		lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
Daniel Wagner 99c48d
-				 "0006 rpi x%x DID:%x flg:%x %d x%px\n",
Daniel Wagner 99c48d
+				 "0006 rpi x%x DID:%x flg:%x %d x%px "
Daniel Wagner 99c48d
+				 "mbx_cmd x%x mbx_flag x%x x%px\n",
Daniel Wagner 99c48d
 				 ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
Daniel Wagner 99c48d
-				 kref_read(&ndlp->kref),
Daniel Wagner 99c48d
-				 ndlp);
Daniel Wagner 99c48d
-		/* This is the end of the default RPI cleanup logic for
Daniel Wagner 99c48d
-		 * this ndlp and it could get released.  Clear the nlp_flags to
Daniel Wagner 99c48d
-		 * prevent any further processing.
Daniel Wagner 99c48d
+				 kref_read(&ndlp->kref), ndlp, mbx_cmd,
Daniel Wagner 99c48d
+				 mbx_flag, pmb);
Daniel Wagner 99c48d
+
Daniel Wagner 99c48d
+		/* This ends the default/temporary RPI cleanup logic for this
Daniel Wagner 99c48d
+		 * ndlp and the node and rpi needs to be released. Free the rpi
Daniel Wagner 99c48d
+		 * first on an UNREG_LOGIN and then release the final
Daniel Wagner 99c48d
+		 * references.
Daniel Wagner 99c48d
 		 */
Daniel Wagner 99c48d
+		spin_lock_irq(&ndlp->lock);
Daniel Wagner 99c48d
 		ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND;
Daniel Wagner 99c48d
+		if (mbx_cmd == MBX_UNREG_LOGIN)
Daniel Wagner 99c48d
+			ndlp->nlp_flag &= ~NLP_UNREG_INP;
Daniel Wagner 99c48d
+		spin_unlock_irq(&ndlp->lock);
Daniel Wagner 99c48d
 		lpfc_nlp_put(ndlp);
Daniel Wagner 99c48d
-		lpfc_nlp_not_used(ndlp);
Daniel Wagner 99c48d
+		lpfc_drop_node(ndlp->vport, ndlp);
Daniel Wagner 99c48d
 	}
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
+	lpfc_mbuf_free(phba, mp->virt, mp->phys);
Daniel Wagner 99c48d
+	kfree(mp);
Daniel Wagner 99c48d
+	mempool_free(pmb, phba->mbox_mem_pool);
Daniel Wagner 99c48d
 	return;
Daniel Wagner 99c48d
 }
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
@@ -4503,11 +4529,11 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba,
Daniel Wagner 99c48d
 	/* ELS response tag <ulpIoTag> completes */
Daniel Wagner 99c48d
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
Daniel Wagner 99c48d
 			 "0110 ELS response tag x%x completes "
Daniel Wagner 99c48d
-			 "Data: x%x x%x x%x x%x x%x x%x x%x\n",
Daniel Wagner 99c48d
+			 "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%px\n",
Daniel Wagner 99c48d
 			 cmdiocb->iocb.ulpIoTag, rspiocb->iocb.ulpStatus,
Daniel Wagner 99c48d
 			 rspiocb->iocb.un.ulpWord[4], rspiocb->iocb.ulpTimeout,
Daniel Wagner 99c48d
 			 ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
Daniel Wagner 99c48d
-			 ndlp->nlp_rpi);
Daniel Wagner 99c48d
+			 ndlp->nlp_rpi, kref_read(&ndlp->kref), mbox);
Daniel Wagner 99c48d
 	if (mbox) {
Daniel Wagner 99c48d
 		if ((rspiocb->iocb.ulpStatus == 0) &&
Daniel Wagner 99c48d
 		    (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) {
Daniel Wagner 99c48d
@@ -4587,6 +4613,16 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba,
Daniel Wagner 99c48d
 		spin_unlock_irq(&ndlp->lock);
Daniel Wagner 99c48d
 	}
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
+	/* An SLI4 NPIV instance wants to drop the node at this point under
Daniel Wagner 99c48d
+	 * these conditions and release the RPI.
Daniel Wagner 99c48d
+	 */
Daniel Wagner 99c48d
+	if (phba->sli_rev == LPFC_SLI_REV4 &&
Daniel Wagner 99c48d
+	    (vport && vport->port_type == LPFC_NPIV_PORT) &&
Daniel Wagner 99c48d
+	    ndlp->nlp_flag & NLP_RELEASE_RPI) {
Daniel Wagner 99c48d
+		lpfc_sli4_free_rpi(phba, ndlp->nlp_rpi);
Daniel Wagner 99c48d
+		lpfc_drop_node(vport, ndlp);
Daniel Wagner 99c48d
+	}
Daniel Wagner 99c48d
+
Daniel Wagner 99c48d
 	/* Release the originating I/O reference. */
Daniel Wagner 99c48d
 	lpfc_els_free_iocb(phba, cmdiocb);
Daniel Wagner 99c48d
 	lpfc_nlp_put(ndlp);
Daniel Wagner 99c48d
@@ -4775,10 +4811,10 @@ lpfc_els_rsp_acc(struct lpfc_vport *vpor
Daniel Wagner 99c48d
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
Daniel Wagner 99c48d
 			 "0128 Xmit ELS ACC response Status: x%x, IoTag: x%x, "
Daniel Wagner 99c48d
 			 "XRI: x%x, DID: x%x, nlp_flag: x%x nlp_state: x%x "
Daniel Wagner 99c48d
-			 "RPI: x%x, fc_flag x%x\n",
Daniel Wagner 99c48d
+			 "RPI: x%x, fc_flag x%x refcnt %d\n",
Daniel Wagner 99c48d
 			 rc, elsiocb->iotag, elsiocb->sli4_xritag,
Daniel Wagner 99c48d
 			 ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
Daniel Wagner 99c48d
-			 ndlp->nlp_rpi, vport->fc_flag);
Daniel Wagner 99c48d
+			 ndlp->nlp_rpi, vport->fc_flag, kref_read(&ndlp->kref));
Daniel Wagner 99c48d
 	return 0;
Daniel Wagner 99c48d
 }
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
@@ -4856,6 +4892,17 @@ lpfc_els_rsp_reject(struct lpfc_vport *v
Daniel Wagner 99c48d
 		return 1;
Daniel Wagner 99c48d
 	}
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
+	/* The NPIV instance is rejecting this unsolicited ELS. Make sure the
Daniel Wagner 99c48d
+	 * node's assigned RPI needs to be released as this node will get
Daniel Wagner 99c48d
+	 * freed.
Daniel Wagner 99c48d
+	 */
Daniel Wagner 99c48d
+	if (phba->sli_rev == LPFC_SLI_REV4 &&
Daniel Wagner 99c48d
+	    vport->port_type == LPFC_NPIV_PORT) {
Daniel Wagner 99c48d
+		spin_lock_irq(&ndlp->lock);
Daniel Wagner 99c48d
+		ndlp->nlp_flag |= NLP_RELEASE_RPI;
Daniel Wagner 99c48d
+		spin_unlock_irq(&ndlp->lock);
Daniel Wagner 99c48d
+	}
Daniel Wagner 99c48d
+
Daniel Wagner 99c48d
 	rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
Daniel Wagner 99c48d
 	if (rc == IOCB_ERROR) {
Daniel Wagner 99c48d
 		lpfc_els_free_iocb(phba, elsiocb);
Daniel Wagner 99c48d
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
Daniel Wagner 99c48d
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
Daniel Wagner 99c48d
@@ -4786,12 +4786,17 @@ lpfc_nlp_logo_unreg(struct lpfc_hba *phb
Daniel Wagner 99c48d
 		ndlp->nlp_defer_did = NLP_EVT_NOTHING_PENDING;
Daniel Wagner 99c48d
 		lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
Daniel Wagner 99c48d
 	} else {
Daniel Wagner 99c48d
+		/* NLP_RELEASE_RPI is only set for SLI4 ports. */
Daniel Wagner 99c48d
 		if (ndlp->nlp_flag & NLP_RELEASE_RPI) {
Daniel Wagner 99c48d
 			lpfc_sli4_free_rpi(vport->phba, ndlp->nlp_rpi);
Daniel Wagner 99c48d
+			spin_lock_irq(&ndlp->lock);
Daniel Wagner 99c48d
 			ndlp->nlp_flag &= ~NLP_RELEASE_RPI;
Daniel Wagner 99c48d
 			ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR;
Daniel Wagner 99c48d
+			spin_unlock_irq(&ndlp->lock);
Daniel Wagner 99c48d
 		}
Daniel Wagner 99c48d
+		spin_lock_irq(&ndlp->lock);
Daniel Wagner 99c48d
 		ndlp->nlp_flag &= ~NLP_UNREG_INP;
Daniel Wagner 99c48d
+		spin_unlock_irq(&ndlp->lock);
Daniel Wagner 99c48d
 	}
Daniel Wagner 99c48d
 }
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
@@ -5126,8 +5131,10 @@ lpfc_cleanup_node(struct lpfc_vport *vpo
Daniel Wagner 99c48d
 	list_del_init(&ndlp->dev_loss_evt.evt_listp);
Daniel Wagner 99c48d
 	list_del_init(&ndlp->recovery_evt.evt_listp);
Daniel Wagner 99c48d
 	lpfc_cleanup_vports_rrqs(vport, ndlp);
Daniel Wagner 99c48d
+
Daniel Wagner 99c48d
 	if (phba->sli_rev == LPFC_SLI_REV4)
Daniel Wagner 99c48d
 		ndlp->nlp_flag |= NLP_RELEASE_RPI;
Daniel Wagner 99c48d
+
Daniel Wagner 99c48d
 	return 0;
Daniel Wagner 99c48d
 }
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
@@ -6172,8 +6179,23 @@ lpfc_nlp_release(struct kref *kref)
Daniel Wagner 99c48d
 	lpfc_cancel_retry_delay_tmo(vport, ndlp);
Daniel Wagner 99c48d
 	lpfc_cleanup_node(vport, ndlp);
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
-	/* Clear Node key fields to give other threads notice
Daniel Wagner 99c48d
-	 * that this node memory is not valid anymore.
Daniel Wagner 99c48d
+	/* Not all ELS transactions have registered the RPI with the port.
Daniel Wagner 99c48d
+	 * In these cases the rpi usage is temporary and the node is
Daniel Wagner 99c48d
+	 * released when the WQE is completed.  Catch this case to free the
Daniel Wagner 99c48d
+	 * RPI to the pool.  Because this node is in the release path, a lock
Daniel Wagner 99c48d
+	 * is unnecessary.  All references are gone and the node has been
Daniel Wagner 99c48d
+	 * dequeued.
Daniel Wagner 99c48d
+	 */
Daniel Wagner 99c48d
+	if (ndlp->nlp_flag & NLP_RELEASE_RPI) {
Daniel Wagner 99c48d
+		if (ndlp->nlp_rpi != LPFC_RPI_ALLOC_ERROR &&
Daniel Wagner 99c48d
+		    !(ndlp->nlp_flag & (NLP_RPI_REGISTERED | NLP_UNREG_INP))) {
Daniel Wagner 99c48d
+			lpfc_sli4_free_rpi(vport->phba, ndlp->nlp_rpi);
Daniel Wagner 99c48d
+			ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR;
Daniel Wagner 99c48d
+		}
Daniel Wagner 99c48d
+	}
Daniel Wagner 99c48d
+
Daniel Wagner 99c48d
+	/* The node is not freed back to memory, it is released to a pool so
Daniel Wagner 99c48d
+	 * the node fields need to be cleaned up.
Daniel Wagner 99c48d
 	 */
Daniel Wagner 99c48d
 	ndlp->vport = NULL;
Daniel Wagner 99c48d
 	ndlp->nlp_state = NLP_STE_FREED_NODE;
Daniel Wagner 99c48d
@@ -6253,6 +6275,7 @@ lpfc_nlp_not_used(struct lpfc_nodelist *
Daniel Wagner 99c48d
 		"node not used:   did:x%x flg:x%x refcnt:x%x",
Daniel Wagner 99c48d
 		ndlp->nlp_DID, ndlp->nlp_flag,
Daniel Wagner 99c48d
 		kref_read(&ndlp->kref));
Daniel Wagner 99c48d
+
Daniel Wagner 99c48d
 	if (kref_read(&ndlp->kref) == 1)
Daniel Wagner 99c48d
 		if (lpfc_nlp_put(ndlp))
Daniel Wagner 99c48d
 			return 1;
Daniel Wagner 99c48d
--- a/drivers/scsi/lpfc/lpfc_init.c
Daniel Wagner 99c48d
+++ b/drivers/scsi/lpfc/lpfc_init.c
Daniel Wagner 99c48d
@@ -3535,13 +3535,6 @@ lpfc_offline_prep(struct lpfc_hba *phba,
Daniel Wagner 99c48d
 			list_for_each_entry_safe(ndlp, next_ndlp,
Daniel Wagner 99c48d
 						 &vports[i]->fc_nodes,
Daniel Wagner 99c48d
 						 nlp_listp) {
Daniel Wagner 99c48d
-				if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
Daniel Wagner 99c48d
-					/* Driver must assume RPI is invalid for
Daniel Wagner 99c48d
-					 * any unused or inactive node.
Daniel Wagner 99c48d
-					 */
Daniel Wagner 99c48d
-					ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR;
Daniel Wagner 99c48d
-					continue;
Daniel Wagner 99c48d
-				}
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
 				spin_lock_irq(&ndlp->lock);
Daniel Wagner 99c48d
 				ndlp->nlp_flag &= ~NLP_NPR_ADISC;
Daniel Wagner 99c48d
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
Daniel Wagner 99c48d
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
Daniel Wagner 99c48d
@@ -564,15 +564,24 @@ lpfc_rcv_plogi(struct lpfc_vport *vport,
Daniel Wagner 99c48d
 		/* no deferred ACC */
Daniel Wagner 99c48d
 		kfree(save_iocb);
Daniel Wagner 99c48d
 
Daniel Wagner 99c48d
-		/* In order to preserve RPIs, we want to cleanup
Daniel Wagner 99c48d
-		 * the default RPI the firmware created to rcv
Daniel Wagner 99c48d
-		 * this ELS request. The only way to do this is
Daniel Wagner 99c48d
-		 * to register, then unregister the RPI.
Daniel Wagner 99c48d
+		/* This is an NPIV SLI4 instance that does not need to register
Daniel Wagner 99c48d
+		 * a default RPI.
Daniel Wagner 99c48d
 		 */
Daniel Wagner 99c48d
-		spin_lock_irq(&ndlp->lock);
Daniel Wagner 99c48d
-		ndlp->nlp_flag |= (NLP_RM_DFLT_RPI | NLP_ACC_REGLOGIN |
Daniel Wagner 99c48d
-				   NLP_RCV_PLOGI);
Daniel Wagner 99c48d
-		spin_unlock_irq(&ndlp->lock);
Daniel Wagner 99c48d
+		if (phba->sli_rev == LPFC_SLI_REV4) {
Daniel Wagner 99c48d
+			mempool_free(login_mbox, phba->mbox_mem_pool);
Daniel Wagner 99c48d
+			login_mbox = NULL;
Daniel Wagner 99c48d
+		} else {
Daniel Wagner 99c48d
+			/* In order to preserve RPIs, we want to cleanup
Daniel Wagner 99c48d
+			 * the default RPI the firmware created to rcv
Daniel Wagner 99c48d
+			 * this ELS request. The only way to do this is
Daniel Wagner 99c48d
+			 * to register, then unregister the RPI.
Daniel Wagner 99c48d
+			 */
Daniel Wagner 99c48d
+			spin_lock_irq(&ndlp->lock);
Daniel Wagner 99c48d
+			ndlp->nlp_flag |= (NLP_RM_DFLT_RPI | NLP_ACC_REGLOGIN |
Daniel Wagner 99c48d
+					   NLP_RCV_PLOGI);
Daniel Wagner 99c48d
+			spin_unlock_irq(&ndlp->lock);
Daniel Wagner 99c48d
+		}
Daniel Wagner 99c48d
+
Daniel Wagner 99c48d
 		stat.un.b.lsRjtRsnCode = LSRJT_INVALID_CMD;
Daniel Wagner 99c48d
 		stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
Daniel Wagner 99c48d
 		rc = lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
Daniel Wagner 99c48d
--- a/drivers/scsi/lpfc/lpfc_sli.c
Daniel Wagner 99c48d
+++ b/drivers/scsi/lpfc/lpfc_sli.c
Daniel Wagner 99c48d
@@ -13497,9 +13497,15 @@ lpfc_sli4_sp_handle_mbox_event(struct lp
Daniel Wagner 99c48d
 		if (mcqe_status == MB_CQE_STATUS_SUCCESS) {
Daniel Wagner 99c48d
 			mp = (struct lpfc_dmabuf *)(pmb->ctx_buf);
Daniel Wagner 99c48d
 			ndlp = (struct lpfc_nodelist *)pmb->ctx_ndlp;
Daniel Wagner 99c48d
-			/* Reg_LOGIN of dflt RPI was successful. Now lets get
Daniel Wagner 99c48d
-			 * RID of the PPI using the same mbox buffer.
Daniel Wagner 99c48d
+
Daniel Wagner 99c48d
+			/* Reg_LOGIN of dflt RPI was successful. Mark the
Daniel Wagner 99c48d
+			 * node as having an UNREG_LOGIN in progress to stop
Daniel Wagner 99c48d
+			 * an unsolicited PLOGI from the same NPortId from
Daniel Wagner 99c48d
+			 * starting another mailbox transaction.
Daniel Wagner 99c48d
 			 */
Daniel Wagner 99c48d
+			spin_lock_irqsave(&ndlp->lock, iflags);
Daniel Wagner 99c48d
+			ndlp->nlp_flag |= NLP_UNREG_INP;
Daniel Wagner 99c48d
+			spin_unlock_irqrestore(&ndlp->lock, iflags);
Daniel Wagner 99c48d
 			lpfc_unreg_login(phba, vport->vpi,
Daniel Wagner 99c48d
 					 pmbox->un.varWords[0], pmb);
Daniel Wagner 99c48d
 			pmb->mbox_cmpl = lpfc_mbx_cmpl_dflt_rpi;