diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_hbadisc.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hbadisc.c | 27 |
1 files changed, 25 insertions, 2 deletions
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index f5a898c2c904..3ea07034ab97 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -4789,12 +4789,17 @@ lpfc_nlp_logo_unreg(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) ndlp->nlp_defer_did = NLP_EVT_NOTHING_PENDING; lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0); } else { + /* NLP_RELEASE_RPI is only set for SLI4 ports. */ if (ndlp->nlp_flag & NLP_RELEASE_RPI) { lpfc_sli4_free_rpi(vport->phba, ndlp->nlp_rpi); + spin_lock_irq(&ndlp->lock); ndlp->nlp_flag &= ~NLP_RELEASE_RPI; ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; + spin_unlock_irq(&ndlp->lock); } + spin_lock_irq(&ndlp->lock); ndlp->nlp_flag &= ~NLP_UNREG_INP; + spin_unlock_irq(&ndlp->lock); } } @@ -5129,8 +5134,10 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) list_del_init(&ndlp->dev_loss_evt.evt_listp); list_del_init(&ndlp->recovery_evt.evt_listp); lpfc_cleanup_vports_rrqs(vport, ndlp); + if (phba->sli_rev == LPFC_SLI_REV4) ndlp->nlp_flag |= NLP_RELEASE_RPI; + return 0; } @@ -6176,8 +6183,23 @@ lpfc_nlp_release(struct kref *kref) lpfc_cancel_retry_delay_tmo(vport, ndlp); lpfc_cleanup_node(vport, ndlp); - /* Clear Node key fields to give other threads notice - * that this node memory is not valid anymore. + /* Not all ELS transactions have registered the RPI with the port. + * In these cases the rpi usage is temporary and the node is + * released when the WQE is completed. Catch this case to free the + * RPI to the pool. Because this node is in the release path, a lock + * is unnecessary. All references are gone and the node has been + * dequeued. + */ + if (ndlp->nlp_flag & NLP_RELEASE_RPI) { + if (ndlp->nlp_rpi != LPFC_RPI_ALLOC_ERROR && + !(ndlp->nlp_flag & (NLP_RPI_REGISTERED | NLP_UNREG_INP))) { + lpfc_sli4_free_rpi(vport->phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; + } + } + + /* The node is not freed back to memory, it is released to a pool so + * the node fields need to be cleaned up. */ ndlp->vport = NULL; ndlp->nlp_state = NLP_STE_FREED_NODE; @@ -6257,6 +6279,7 @@ lpfc_nlp_not_used(struct lpfc_nodelist *ndlp) "node not used: did:x%x flg:x%x refcnt:x%x", ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref)); + if (kref_read(&ndlp->kref) == 1) if (lpfc_nlp_put(ndlp)) return 1; |