提交 982fc396 编写于 作者: J James Smart 提交者: Martin K. Petersen

scsi: lpfc: Don't release final kref on Fport node while ABTS outstanding

In a rarely executed path, FLOGI failure, there is a refcounting error.  If
FLOGI completed with an error, typically a timeout, the initial completion
handler would remove the job reference. However, the job completion isn't
the actual end of the job/exchange as the timeout usually initiates an
ABTS, and upon that ABTS completion, a final completion is sent. The driver
removes the reference again in the final completion. Thus the imbalance.

In the buggy cases, if there was a link bounce while the delayed response
is outstanding, the fport node may be referenced again but there was no
additional reference as it is already present. The delayed completion then
occurs and removes the last reference freeing the node and causing issues
in the link up processed that is using the node.

Fix this scenario by removing the snippet that removed the reference in the
initial FLOGI completion. The bad snippet was poorly trying to identify the
FLOGI as OK to do so by realizing the node was not registered with either
SCSI or NVMe transport.

Link: https://lore.kernel.org/r/20210910233159.115896-3-jsmart2021@gmail.com
Fixes: 618e2ee1 ("scsi: lpfc: Fix FLOGI failure due to accessing a freed node")
Cc: <stable@vger.kernel.org> # v5.13+
Co-developed-by: NJustin Tee <justin.tee@broadcom.com>
Signed-off-by: NJustin Tee <justin.tee@broadcom.com>
Signed-off-by: NJames Smart <jsmart2021@gmail.com>
Signed-off-by: NMartin K. Petersen <martin.petersen@oracle.com>
上级 99154581
...@@ -1059,9 +1059,10 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, ...@@ -1059,9 +1059,10 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_printf_vlog(vport, KERN_WARNING, LOG_TRACE_EVENT, lpfc_printf_vlog(vport, KERN_WARNING, LOG_TRACE_EVENT,
"0150 FLOGI failure Status:x%x/x%x " "0150 FLOGI failure Status:x%x/x%x "
"xri x%x TMO:x%x\n", "xri x%x TMO:x%x refcnt %d\n",
irsp->ulpStatus, irsp->un.ulpWord[4], irsp->ulpStatus, irsp->un.ulpWord[4],
cmdiocb->sli4_xritag, irsp->ulpTimeout); cmdiocb->sli4_xritag, irsp->ulpTimeout,
kref_read(&ndlp->kref));
/* If this is not a loop open failure, bail out */ /* If this is not a loop open failure, bail out */
if (!(irsp->ulpStatus == IOSTAT_LOCAL_REJECT && if (!(irsp->ulpStatus == IOSTAT_LOCAL_REJECT &&
...@@ -1122,12 +1123,12 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, ...@@ -1122,12 +1123,12 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
/* FLOGI completes successfully */ /* FLOGI completes successfully */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0101 FLOGI completes successfully, I/O tag:x%x, " "0101 FLOGI completes successfully, I/O tag:x%x, "
"xri x%x Data: x%x x%x x%x x%x x%x x%x x%x\n", "xri x%x Data: x%x x%x x%x x%x x%x x%x x%x %d\n",
cmdiocb->iotag, cmdiocb->sli4_xritag, cmdiocb->iotag, cmdiocb->sli4_xritag,
irsp->un.ulpWord[4], sp->cmn.e_d_tov, irsp->un.ulpWord[4], sp->cmn.e_d_tov,
sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution, sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution,
vport->port_state, vport->fc_flag, vport->port_state, vport->fc_flag,
sp->cmn.priority_tagging); sp->cmn.priority_tagging, kref_read(&ndlp->kref));
if (sp->cmn.priority_tagging) if (sp->cmn.priority_tagging)
vport->vmid_flag |= LPFC_VMID_ISSUE_QFPA; vport->vmid_flag |= LPFC_VMID_ISSUE_QFPA;
...@@ -1205,8 +1206,6 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, ...@@ -1205,8 +1206,6 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
phba->fcf.fcf_flag &= ~FCF_DISCOVERY; phba->fcf.fcf_flag &= ~FCF_DISCOVERY;
spin_unlock_irq(&phba->hbalock); spin_unlock_irq(&phba->hbalock);
if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)))
lpfc_nlp_put(ndlp);
if (!lpfc_error_lost_link(irsp)) { if (!lpfc_error_lost_link(irsp)) {
/* FLOGI failed, so just use loop map to make discovery list */ /* FLOGI failed, so just use loop map to make discovery list */
lpfc_disc_list_loopmap(vport); lpfc_disc_list_loopmap(vport);
......
...@@ -4449,8 +4449,9 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) ...@@ -4449,8 +4449,9 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
fc_remote_port_rolechg(rport, rport_ids.roles); fc_remote_port_rolechg(rport, rport_ids.roles);
lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
"3183 %s rport x%px DID x%x, role x%x\n", "3183 %s rport x%px DID x%x, role x%x refcnt %d\n",
__func__, rport, rport->port_id, rport->roles); __func__, rport, rport->port_id, rport->roles,
kref_read(&ndlp->kref));
if ((rport->scsi_target_id != -1) && if ((rport->scsi_target_id != -1) &&
(rport->scsi_target_id < LPFC_MAX_TARGET)) { (rport->scsi_target_id < LPFC_MAX_TARGET)) {
...@@ -4475,8 +4476,9 @@ lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp) ...@@ -4475,8 +4476,9 @@ lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp)
lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE, lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
"3184 rport unregister x%06x, rport x%px " "3184 rport unregister x%06x, rport x%px "
"xptflg x%x\n", "xptflg x%x refcnt %d\n",
ndlp->nlp_DID, rport, ndlp->fc4_xpt_flags); ndlp->nlp_DID, rport, ndlp->fc4_xpt_flags,
kref_read(&ndlp->kref));
fc_remote_port_delete(rport); fc_remote_port_delete(rport);
lpfc_nlp_put(ndlp); lpfc_nlp_put(ndlp);
......
...@@ -209,8 +209,9 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport) ...@@ -209,8 +209,9 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport)
* calling state machine to remove the node. * calling state machine to remove the node.
*/ */
lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
"6146 remoteport delete of remoteport x%px\n", "6146 remoteport delete of remoteport x%px, ndlp x%px "
remoteport); "DID x%x xflags x%x\n",
remoteport, ndlp, ndlp->nlp_DID, ndlp->fc4_xpt_flags);
spin_lock_irq(&ndlp->lock); spin_lock_irq(&ndlp->lock);
/* The register rebind might have occurred before the delete /* The register rebind might have occurred before the delete
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册