Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58da1ffb authored by James Smart's avatar James Smart Committed by James Bottomley
Browse files

[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: default avatarJames Smart <james.smart@emulex.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent b35c07d0
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -268,7 +268,6 @@ struct lpfc_vport {
#define FC_NLP_MORE             0x40	 /* More node to process in node tbl */
#define FC_OFFLINE_MODE         0x80	 /* Interface is offline for diag */
#define FC_FABRIC               0x100	 /* We are fabric attached */
#define FC_ESTABLISH_LINK       0x200	 /* Reestablish Link */
#define FC_RSCN_DISCOVERY       0x400	 /* Auth all devices after RSCN */
#define FC_SCSI_SCAN_TMO        0x4000	 /* scsi scan timer running */
#define FC_ABORT_DISCOVERY      0x8000	 /* we want to abort discovery */
@@ -433,8 +432,6 @@ struct lpfc_hba {

	uint32_t fc_eventTag;	/* event tag for link attention */


	struct timer_list fc_estabtmo;	/* link establishment timer */
	/* These fields used to be binfo */
	uint32_t fc_pref_DID;	/* preferred D_ID */
	uint8_t  fc_pref_ALPA;	/* preferred AL_PA */
+5 −1
Original line number Diff line number Diff line
@@ -1962,7 +1962,11 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,

		phba->sysfs_mbox.mbox->vport = vport;

		if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
		/* Don't allow mailbox commands to be sent when blocked
		 * or when in the middle of discovery
		 */
		if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO ||
		    vport->fc_flag & FC_NDISC_ACTIVE) {
			sysfs_mbox_idle(phba);
			spin_unlock_irq(&phba->hbalock);
			return  -EAGAIN;
+15 −31
Original line number Diff line number Diff line
@@ -438,7 +438,7 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
				    (!(vport->ct_flags & FC_CT_RFF_ID)) ||
				    (!vport->cfg_restrict_login)) {
					ndlp = lpfc_setup_disc_node(vport, Did);
					if (ndlp) {
					if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
						lpfc_debugfs_disc_trc(vport,
						LPFC_DISC_TRC_CT,
						"Parse GID_FTrsp: "
@@ -543,7 +543,7 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
	struct lpfc_dmabuf *outp;
	struct lpfc_sli_ct_request *CTrsp;
	struct lpfc_nodelist *ndlp;
	int rc, retry;
	int rc;

	/* First save ndlp, before we overwrite it */
	ndlp = cmdiocb->context_un.ndlp;
@@ -563,46 +563,30 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
	if (vport->load_flag & FC_UNLOADING)
		goto out;

	if (lpfc_els_chk_latt(vport) || lpfc_error_lost_link(irsp)) {
	if (lpfc_els_chk_latt(vport)) {
		lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
				 "0216 Link event during NS query\n");
		lpfc_vport_set_state(vport, FC_VPORT_FAILED);
		goto out;
	}

	if (lpfc_error_lost_link(irsp)) {
		lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
				 "0226 NS query failed due to link event\n");
		goto out;
	}
	if (irsp->ulpStatus) {
		/* Check for retry */
		if (vport->fc_ns_retry < LPFC_MAX_NS_RETRY) {
			retry = 1;
			if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
				switch (irsp->un.ulpWord[4]) {
				case IOERR_NO_RESOURCES:
					/* We don't increment the retry
					 * count for this case.
					 */
					break;
				case IOERR_LINK_DOWN:
				case IOERR_SLI_ABORTED:
				case IOERR_SLI_DOWN:
					retry = 0;
					break;
				default:
					vport->fc_ns_retry++;
				}
			}
			else
			if (irsp->ulpStatus != IOSTAT_LOCAL_REJECT ||
			    irsp->un.ulpWord[4] != IOERR_NO_RESOURCES)
				vport->fc_ns_retry++;

			if (retry) {
			/* CT command is being retried */
			rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_FT,
					 vport->fc_ns_retry, 0);
				if (rc == 0) {
					/* success */
			if (rc == 0)
				goto out;
		}
			}
		}
		lpfc_vport_set_state(vport, FC_VPORT_FAILED);
		lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
				 "0257 GID_FT Query error: 0x%x 0x%x\n",
@@ -780,7 +764,7 @@ lpfc_cmpl_ct_cmd_gff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,

	/* This is a target port, unregistered port, or the GFF_ID failed */
	ndlp = lpfc_setup_disc_node(vport, did);
	if (ndlp) {
	if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
		lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
				 "0242 Process x%x GFF "
				 "NameServer Rsp Data: x%x x%x x%x\n",
+2 −0
Original line number Diff line number Diff line
@@ -503,6 +503,8 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
				ndlp->nlp_sid);
		if (ndlp->nlp_type & NLP_FCP_INITIATOR)
			len +=  snprintf(buf+len, size-len, "FCP_INITIATOR ");
		len += snprintf(buf+len, size-len, "usgmap:%x ",
			ndlp->nlp_usg_map);
		len += snprintf(buf+len, size-len, "refcnt:%x",
			atomic_read(&ndlp->kref.refcount));
		len +=  snprintf(buf+len, size-len, "\n");
+54 −31
Original line number Diff line number Diff line
@@ -719,11 +719,11 @@ lpfc_els_abort_flogi(struct lpfc_hba *phba)
		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);

	return 0;
@@ -829,7 +829,7 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
	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 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
			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 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
			}
			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 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
		}

		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_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
			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_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
	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 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
	 * 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 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
	}

	/* 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 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
			}
			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 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
		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 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
		/* 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 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
		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,22 +4119,23 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
		newnode = 1;
		if ((did & Fabric_DID_MASK) == Fabric_DID_MASK)
			ndlp->nlp_type |= NLP_FABRIC;
	} else {
		if (!NLP_CHK_NODE_ACT(ndlp)) {
	} 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 */
		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++;
	if (elsiocb->context1)
@@ -4451,7 +4474,6 @@ lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
			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 @@ lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
			return;
		}
	}
	ndlp->nlp_type |= NLP_FABRIC;

	lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);

@@ -4481,8 +4504,8 @@ lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
		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);
		}
Loading