[SCSI] lpfc 8.2.6 : Multiple discovery fixes
Multiple Discovery Fixes:
- Fix race on discovery due to link events coinciding with vport_delete.
- Use NLP_FABRIC state to filter out switch-based pseudo initiators that
reuse the same WWNs.
- Correct erroneous setting of DID=0 in lpfc_matchdid()
- Correct extra reference count that was in the lookup path for the
remoteid from an unsolicited ELS.
- Correct double-free bug in els abort path.
- Correct FDMI server discovery logic for switch that return a WWN of 0.
- Fix bugs in ndlp mgmt when a node changes address
- Correct bug that did not delete RSCNs for vports upon link transitions
- Fix "0216 Link event during NS query" error which pops up when vports
are swapped to different switch ports.
- Add sanity checks on ndlp structures
- Fix devloss log message to dump WWN correctly
- Hold off mgmt commands that were interferring with discovery mailbox cmds
- Remove unnecessary FC_ESTABLISH_LINK logic.
- Correct some race conditions in the worker thread, resulting in devloss:
- Clear the work_port_events field before handling the work port events
- Clear the deferred ring event before handling a deferred ring event
- Hold the hba lock when waking up the work thread
- Send an acc for the rscn even when we aren't going to handle it
- Fix locking behavior that was not properly protecting the ACTIVE flag,
thus allowing mailbox command order to shift.
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index cbb68a9..6e0e991 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -719,9 +719,9 @@
if (icmd->ulpCommand == CMD_ELS_REQUEST64_CR &&
icmd->un.elsreq64.bdl.ulpIoTag32) {
ndlp = (struct lpfc_nodelist *)(iocb->context1);
- if (ndlp && (ndlp->nlp_DID == Fabric_DID)) {
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
+ (ndlp->nlp_DID == Fabric_DID))
lpfc_sli_issue_abort_iotag(phba, pring, iocb);
- }
}
}
spin_unlock_irq(&phba->hbalock);
@@ -829,7 +829,7 @@
struct fc_rport *rport;
struct serv_parm *sp;
uint8_t name[sizeof(struct lpfc_name)];
- uint32_t rc;
+ uint32_t rc, keepDID = 0;
/* Fabric nodes can have the same WWPN so we don't bother searching
* by WWPN. Just return the ndlp that was given to us.
@@ -858,11 +858,17 @@
return ndlp;
lpfc_nlp_init(vport, new_ndlp, ndlp->nlp_DID);
} else if (!NLP_CHK_NODE_ACT(new_ndlp)) {
+ rc = memcmp(&ndlp->nlp_portname, name,
+ sizeof(struct lpfc_name));
+ if (!rc)
+ return ndlp;
new_ndlp = lpfc_enable_node(vport, new_ndlp,
NLP_STE_UNUSED_NODE);
if (!new_ndlp)
return ndlp;
- }
+ keepDID = new_ndlp->nlp_DID;
+ } else
+ keepDID = new_ndlp->nlp_DID;
lpfc_unreg_rpi(vport, new_ndlp);
new_ndlp->nlp_DID = ndlp->nlp_DID;
@@ -893,12 +899,24 @@
}
new_ndlp->nlp_type = ndlp->nlp_type;
}
+ /* We shall actually free the ndlp with both nlp_DID and
+ * nlp_portname fields equals 0 to avoid any ndlp on the
+ * nodelist never to be used.
+ */
+ if (ndlp->nlp_DID == 0) {
+ spin_lock_irq(&phba->ndlp_lock);
+ NLP_SET_FREE_REQ(ndlp);
+ spin_unlock_irq(&phba->ndlp_lock);
+ }
+ /* Two ndlps cannot have the same did on the nodelist */
+ ndlp->nlp_DID = keepDID;
lpfc_drop_node(vport, ndlp);
}
else {
lpfc_unreg_rpi(vport, ndlp);
- ndlp->nlp_DID = 0; /* Two ndlps cannot have the same did */
+ /* Two ndlps cannot have the same did */
+ ndlp->nlp_DID = keepDID;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
}
return new_ndlp;
@@ -2091,7 +2109,7 @@
}
phba->fc_stat.elsXmitRetry++;
- if (ndlp && delay) {
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp) && delay) {
phba->fc_stat.elsDelayRetry++;
ndlp->nlp_retry = cmdiocb->retry;
@@ -2121,7 +2139,7 @@
lpfc_issue_els_fdisc(vport, ndlp, cmdiocb->retry);
return 1;
case ELS_CMD_PLOGI:
- if (ndlp) {
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_PLOGI_ISSUE);
@@ -2302,7 +2320,7 @@
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
mempool_free(pmb, phba->mbox_mem_pool);
- if (ndlp) {
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
lpfc_nlp_put(ndlp);
/* This is the end of the default RPI cleanup logic for this
* ndlp. If no other discovery threads are using this ndlp.
@@ -2335,7 +2353,8 @@
* function can have cmdiocb->contest1 (ndlp) field set to NULL.
*/
pcmd = (uint8_t *) (((struct lpfc_dmabuf *) cmdiocb->context2)->virt);
- if (ndlp && (*((uint32_t *) (pcmd)) == ELS_CMD_LS_RJT)) {
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
+ (*((uint32_t *) (pcmd)) == ELS_CMD_LS_RJT)) {
/* A LS_RJT associated with Default RPI cleanup has its own
* seperate code path.
*/
@@ -2344,7 +2363,7 @@
}
/* Check to see if link went down during discovery */
- if (!ndlp || lpfc_els_chk_latt(vport)) {
+ if (!ndlp || !NLP_CHK_NODE_ACT(ndlp) || lpfc_els_chk_latt(vport)) {
if (mbox) {
mp = (struct lpfc_dmabuf *) mbox->context1;
if (mp) {
@@ -2353,7 +2372,8 @@
}
mempool_free(mbox, phba->mbox_mem_pool);
}
- if (ndlp && (ndlp->nlp_flag & NLP_RM_DFLT_RPI))
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
+ (ndlp->nlp_flag & NLP_RM_DFLT_RPI))
if (lpfc_nlp_not_used(ndlp)) {
ndlp = NULL;
/* Indicate the node has already released,
@@ -2443,7 +2463,7 @@
mempool_free(mbox, phba->mbox_mem_pool);
}
out:
- if (ndlp) {
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_ACC_REGLOGIN | NLP_RM_DFLT_RPI);
spin_unlock_irq(shost->host_lock);
@@ -3139,6 +3159,8 @@
/* Another thread is walking fc_rscn_id_list on this vport */
spin_unlock_irq(shost->host_lock);
vport->fc_flag |= FC_RSCN_DISCOVERY;
+ /* Send back ACC */
+ lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
return 0;
}
/* Indicate we are walking fc_rscn_id_list on this vport */
@@ -3928,7 +3950,7 @@
else {
struct lpfc_nodelist *ndlp;
ndlp = __lpfc_findnode_rpi(vport, cmd->ulpContext);
- if (ndlp)
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp))
remote_ID = ndlp->nlp_DID;
}
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
@@ -4097,21 +4119,22 @@
newnode = 1;
if ((did & Fabric_DID_MASK) == Fabric_DID_MASK)
ndlp->nlp_type |= NLP_FABRIC;
- } else {
- if (!NLP_CHK_NODE_ACT(ndlp)) {
- ndlp = lpfc_enable_node(vport, ndlp,
- NLP_STE_UNUSED_NODE);
- if (!ndlp)
- goto dropit;
- }
- if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
- /* This is simular to the new node path */
- ndlp = lpfc_nlp_get(ndlp);
- if (!ndlp)
- goto dropit;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- newnode = 1;
- }
+ } else if (!NLP_CHK_NODE_ACT(ndlp)) {
+ ndlp = lpfc_enable_node(vport, ndlp,
+ NLP_STE_UNUSED_NODE);
+ if (!ndlp)
+ goto dropit;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ newnode = 1;
+ if ((did & Fabric_DID_MASK) == Fabric_DID_MASK)
+ ndlp->nlp_type |= NLP_FABRIC;
+ } else if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
+ /* This is similar to the new node path */
+ ndlp = lpfc_nlp_get(ndlp);
+ if (!ndlp)
+ goto dropit;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ newnode = 1;
}
phba->fc_stat.elsRcvFrame++;
@@ -4451,7 +4474,6 @@
return;
}
lpfc_nlp_init(vport, ndlp, NameServer_DID);
- ndlp->nlp_type |= NLP_FABRIC;
} else if (!NLP_CHK_NODE_ACT(ndlp)) {
ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE);
if (!ndlp) {
@@ -4465,6 +4487,7 @@
return;
}
}
+ ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
@@ -4481,8 +4504,8 @@
if (ndlp_fdmi) {
lpfc_nlp_init(vport, ndlp_fdmi, FDMI_DID);
ndlp_fdmi->nlp_type |= NLP_FABRIC;
- ndlp_fdmi->nlp_state =
- NLP_STE_PLOGI_ISSUE;
+ lpfc_nlp_set_state(vport, ndlp_fdmi,
+ NLP_STE_PLOGI_ISSUE);
lpfc_issue_els_plogi(vport, ndlp_fdmi->nlp_DID,
0);
}