scsi: lpfc: Fix NPIV Fabric Node reference counting
While testing initiator-side cable swaps with NPIV, oops occur. The reference counts for the Fabric nodes on the NPIV vports isn't balanced, resulting in premature node removal. The following fixes were made: - Removed the FC_LBIT check in lpfc_linkup_port. This removed the special case for vports that didn't have them clean up just like the physical port. - Removed the unreg_rpi call in lpfc_cleanup_node. In this section, the node is being removed in the context of a reference count release and a mailbox command can't be issued at this point. - Remove special case handling in the default mailbox completion handler that allowed the skipping of a node reference. Now, reference counting always requires the removal of the reference. - Move the location of the DEVICE_RM event is done during LOGO handling as the driver has additional work to do on the ndlp before puts/releases can be performed. Link: https://lore.kernel.org/r/20201115192646.12977-10-james.smart@broadcom.com Co-developed-by: Dick Kennedy <dick.kennedy@broadcom.com> Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com> Signed-off-by: James Smart <james.smart@broadcom.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
b3f2e67cc2
commit
a70e63eee1
@ -2835,8 +2835,9 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
|||||||
/* LOGO completes to NPort <nlp_DID> */
|
/* LOGO completes to NPort <nlp_DID> */
|
||||||
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
|
||||||
"0105 LOGO completes to NPort x%x "
|
"0105 LOGO completes to NPort x%x "
|
||||||
"Data: x%x x%x x%x x%x\n",
|
"refcnt %d nflags x%x Data: x%x x%x x%x x%x\n",
|
||||||
ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4],
|
ndlp->nlp_DID, kref_read(&ndlp->kref), ndlp->nlp_flag,
|
||||||
|
irsp->ulpStatus, irsp->un.ulpWord[4],
|
||||||
irsp->ulpTimeout, vport->num_disc_nodes);
|
irsp->ulpTimeout, vport->num_disc_nodes);
|
||||||
|
|
||||||
if (lpfc_els_chk_latt(vport)) {
|
if (lpfc_els_chk_latt(vport)) {
|
||||||
@ -2844,17 +2845,6 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check to see if link went down during discovery */
|
|
||||||
if (ndlp->nlp_flag & NLP_TARGET_REMOVE) {
|
|
||||||
/* NLP_EVT_DEVICE_RM should unregister the RPI
|
|
||||||
* which should abort all outstanding IOs.
|
|
||||||
*/
|
|
||||||
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
|
|
||||||
NLP_EVT_DEVICE_RM);
|
|
||||||
skip_recovery = 1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The LOGO will not be retried on failure. A LOGO was
|
/* The LOGO will not be retried on failure. A LOGO was
|
||||||
* issued to the remote rport and a ACC or RJT or no Answer are
|
* issued to the remote rport and a ACC or RJT or no Answer are
|
||||||
* all acceptable. Note the failure and move forward with
|
* all acceptable. Note the failure and move forward with
|
||||||
@ -2876,6 +2866,19 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
|||||||
/* Call state machine. This will unregister the rpi if needed. */
|
/* Call state machine. This will unregister the rpi if needed. */
|
||||||
lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO);
|
lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO);
|
||||||
|
|
||||||
|
/* The driver sets this flag for an NPIV instance that doesn't want to
|
||||||
|
* log into the remote port.
|
||||||
|
*/
|
||||||
|
if (ndlp->nlp_flag & NLP_TARGET_REMOVE) {
|
||||||
|
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
|
||||||
|
NLP_EVT_DEVICE_RM);
|
||||||
|
lpfc_els_free_iocb(phba, cmdiocb);
|
||||||
|
lpfc_nlp_put(ndlp);
|
||||||
|
|
||||||
|
/* Presume the node was released. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
/* Driver is done with the IO. */
|
/* Driver is done with the IO. */
|
||||||
lpfc_els_free_iocb(phba, cmdiocb);
|
lpfc_els_free_iocb(phba, cmdiocb);
|
||||||
@ -4399,10 +4402,10 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
|||||||
irsp->ulpStatus, irsp->un.ulpWord[4], ndlp->nlp_DID);
|
irsp->ulpStatus, irsp->un.ulpWord[4], ndlp->nlp_DID);
|
||||||
/* ACC to LOGO completes to NPort <nlp_DID> */
|
/* ACC to LOGO completes to NPort <nlp_DID> */
|
||||||
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
|
||||||
"0109 ACC to LOGO completes to NPort x%x "
|
"0109 ACC to LOGO completes to NPort x%x refcnt %d"
|
||||||
"Data: x%x x%x x%x\n",
|
"Data: x%x x%x x%x\n",
|
||||||
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
|
ndlp->nlp_DID, kref_read(&ndlp->kref), ndlp->nlp_flag,
|
||||||
ndlp->nlp_rpi);
|
ndlp->nlp_state, ndlp->nlp_rpi);
|
||||||
|
|
||||||
if (ndlp->nlp_state == NLP_STE_NPR_NODE) {
|
if (ndlp->nlp_state == NLP_STE_NPR_NODE) {
|
||||||
/* NPort Recovery mode or node is just allocated */
|
/* NPort Recovery mode or node is just allocated */
|
||||||
@ -8650,9 +8653,9 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
|
|||||||
/* ELS command <elsCmd> received from NPORT <did> */
|
/* ELS command <elsCmd> received from NPORT <did> */
|
||||||
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
|
||||||
"0112 ELS command x%x received from NPORT x%x "
|
"0112 ELS command x%x received from NPORT x%x "
|
||||||
"Data: x%x x%x x%x x%x\n",
|
"refcnt %d Data: x%x x%x x%x x%x\n",
|
||||||
cmd, did, vport->port_state, vport->fc_flag,
|
cmd, did, kref_read(&ndlp->kref), vport->port_state,
|
||||||
vport->fc_myDID, vport->fc_prevDID);
|
vport->fc_flag, vport->fc_myDID, vport->fc_prevDID);
|
||||||
|
|
||||||
/* reject till our FLOGI completes or PLOGI assigned DID via PT2PT */
|
/* reject till our FLOGI completes or PLOGI assigned DID via PT2PT */
|
||||||
if ((vport->port_state < LPFC_FABRIC_CFG_LINK) &&
|
if ((vport->port_state < LPFC_FABRIC_CFG_LINK) &&
|
||||||
@ -9144,9 +9147,9 @@ lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
|
|||||||
spin_lock_irq(shost->host_lock);
|
spin_lock_irq(shost->host_lock);
|
||||||
if (vport->fc_flag & FC_DISC_DELAYED) {
|
if (vport->fc_flag & FC_DISC_DELAYED) {
|
||||||
spin_unlock_irq(shost->host_lock);
|
spin_unlock_irq(shost->host_lock);
|
||||||
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
|
lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
|
||||||
"3334 Delay fc port discovery for %d seconds\n",
|
"3334 Delay fc port discovery for %d secs\n",
|
||||||
phba->fc_ratov);
|
phba->fc_ratov);
|
||||||
mod_timer(&vport->delayed_disc_tmo,
|
mod_timer(&vport->delayed_disc_tmo,
|
||||||
jiffies + msecs_to_jiffies(1000 * phba->fc_ratov));
|
jiffies + msecs_to_jiffies(1000 * phba->fc_ratov));
|
||||||
return;
|
return;
|
||||||
|
@ -4810,8 +4810,14 @@ lpfc_set_unreg_login_mbx_cmpl(struct lpfc_hba *phba, struct lpfc_vport *vport,
|
|||||||
{
|
{
|
||||||
unsigned long iflags;
|
unsigned long iflags;
|
||||||
|
|
||||||
|
/* Driver always gets a reference on the mailbox job
|
||||||
|
* in support of async jobs.
|
||||||
|
*/
|
||||||
|
mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
|
||||||
|
if (!mbox->ctx_ndlp)
|
||||||
|
return;
|
||||||
|
|
||||||
if (ndlp->nlp_flag & NLP_ISSUE_LOGO) {
|
if (ndlp->nlp_flag & NLP_ISSUE_LOGO) {
|
||||||
mbox->ctx_ndlp = ndlp;
|
|
||||||
mbox->mbox_cmpl = lpfc_nlp_logo_unreg;
|
mbox->mbox_cmpl = lpfc_nlp_logo_unreg;
|
||||||
|
|
||||||
} else if (phba->sli_rev == LPFC_SLI_REV4 &&
|
} else if (phba->sli_rev == LPFC_SLI_REV4 &&
|
||||||
@ -4819,7 +4825,6 @@ lpfc_set_unreg_login_mbx_cmpl(struct lpfc_hba *phba, struct lpfc_vport *vport,
|
|||||||
(bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
|
(bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
|
||||||
LPFC_SLI_INTF_IF_TYPE_2) &&
|
LPFC_SLI_INTF_IF_TYPE_2) &&
|
||||||
(kref_read(&ndlp->kref) > 0)) {
|
(kref_read(&ndlp->kref) > 0)) {
|
||||||
mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
|
|
||||||
mbox->mbox_cmpl = lpfc_sli4_unreg_rpi_cmpl_clr;
|
mbox->mbox_cmpl = lpfc_sli4_unreg_rpi_cmpl_clr;
|
||||||
} else {
|
} else {
|
||||||
if (vport->load_flag & FC_UNLOADING) {
|
if (vport->load_flag & FC_UNLOADING) {
|
||||||
@ -4828,9 +4833,7 @@ lpfc_set_unreg_login_mbx_cmpl(struct lpfc_hba *phba, struct lpfc_vport *vport,
|
|||||||
ndlp->nlp_flag |= NLP_RELEASE_RPI;
|
ndlp->nlp_flag |= NLP_RELEASE_RPI;
|
||||||
spin_unlock_irqrestore(&ndlp->lock, iflags);
|
spin_unlock_irqrestore(&ndlp->lock, iflags);
|
||||||
}
|
}
|
||||||
lpfc_nlp_get(ndlp);
|
|
||||||
}
|
}
|
||||||
mbox->ctx_ndlp = ndlp;
|
|
||||||
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
|
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4888,6 +4891,11 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
|
|||||||
lpfc_unreg_login(phba, vport->vpi, rpi, mbox);
|
lpfc_unreg_login(phba, vport->vpi, rpi, mbox);
|
||||||
mbox->vport = vport;
|
mbox->vport = vport;
|
||||||
lpfc_set_unreg_login_mbx_cmpl(phba, vport, ndlp, mbox);
|
lpfc_set_unreg_login_mbx_cmpl(phba, vport, ndlp, mbox);
|
||||||
|
if (!mbox->ctx_ndlp) {
|
||||||
|
mempool_free(mbox, phba->mbox_mem_pool);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (mbox->mbox_cmpl == lpfc_sli4_unreg_rpi_cmpl_clr)
|
if (mbox->mbox_cmpl == lpfc_sli4_unreg_rpi_cmpl_clr)
|
||||||
/*
|
/*
|
||||||
* accept PLOGIs after unreg_rpi_cmpl
|
* accept PLOGIs after unreg_rpi_cmpl
|
||||||
@ -5057,7 +5065,6 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
|
|||||||
struct lpfc_hba *phba = vport->phba;
|
struct lpfc_hba *phba = vport->phba;
|
||||||
LPFC_MBOXQ_t *mb, *nextmb;
|
LPFC_MBOXQ_t *mb, *nextmb;
|
||||||
struct lpfc_dmabuf *mp;
|
struct lpfc_dmabuf *mp;
|
||||||
unsigned long iflags;
|
|
||||||
|
|
||||||
/* Cleanup node for NPort <nlp_DID> */
|
/* Cleanup node for NPort <nlp_DID> */
|
||||||
lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
|
||||||
@ -5125,18 +5132,6 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
|
|||||||
lpfc_cleanup_vports_rrqs(vport, ndlp);
|
lpfc_cleanup_vports_rrqs(vport, ndlp);
|
||||||
if (phba->sli_rev == LPFC_SLI_REV4)
|
if (phba->sli_rev == LPFC_SLI_REV4)
|
||||||
ndlp->nlp_flag |= NLP_RELEASE_RPI;
|
ndlp->nlp_flag |= NLP_RELEASE_RPI;
|
||||||
if (!lpfc_unreg_rpi(vport, ndlp)) {
|
|
||||||
/* Clean up unregistered and non freed rpis */
|
|
||||||
if ((ndlp->nlp_flag & NLP_RELEASE_RPI) &&
|
|
||||||
!(ndlp->nlp_rpi == LPFC_RPI_ALLOC_ERROR)) {
|
|
||||||
lpfc_sli4_free_rpi(vport->phba,
|
|
||||||
ndlp->nlp_rpi);
|
|
||||||
spin_lock_irqsave(&ndlp->lock, iflags);
|
|
||||||
ndlp->nlp_flag &= ~NLP_RELEASE_RPI;
|
|
||||||
ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR;
|
|
||||||
spin_unlock_irqrestore(&ndlp->lock, iflags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2851,10 +2851,8 @@ lpfc_cleanup(struct lpfc_vport *vport)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* take care of nodes in unused state before the state
|
if (ndlp->nlp_DID == Fabric_Cntl_DID &&
|
||||||
* machine taking action.
|
ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
|
||||||
*/
|
|
||||||
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
|
|
||||||
lpfc_nlp_put(ndlp);
|
lpfc_nlp_put(ndlp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2541,8 +2541,12 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
|
|||||||
} else {
|
} else {
|
||||||
__lpfc_sli_rpi_release(vport, ndlp);
|
__lpfc_sli_rpi_release(vport, ndlp);
|
||||||
}
|
}
|
||||||
if (vport->load_flag & FC_UNLOADING)
|
|
||||||
lpfc_nlp_put(ndlp);
|
/* The unreg_login mailbox is complete and had a
|
||||||
|
* reference that has to be released. The PLOGI
|
||||||
|
* got its own ref.
|
||||||
|
*/
|
||||||
|
lpfc_nlp_put(ndlp);
|
||||||
pmb->ctx_ndlp = NULL;
|
pmb->ctx_ndlp = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2566,7 +2570,7 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
|
|||||||
*
|
*
|
||||||
* This function is the unreg rpi mailbox completion handler. It
|
* This function is the unreg rpi mailbox completion handler. It
|
||||||
* frees the memory resources associated with the completed mailbox
|
* frees the memory resources associated with the completed mailbox
|
||||||
* command. An additional refrenece is put on the ndlp to prevent
|
* command. An additional reference is put on the ndlp to prevent
|
||||||
* lpfc_nlp_release from freeing the rpi bit in the bitmask before
|
* lpfc_nlp_release from freeing the rpi bit in the bitmask before
|
||||||
* the unreg mailbox command completes, this routine puts the
|
* the unreg mailbox command completes, this routine puts the
|
||||||
* reference back.
|
* reference back.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user