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

Commit 90160e01 authored by James Smart's avatar James Smart Committed by James Bottomley
Browse files

[SCSI] lpfc 8.2.8 : Miscellaneous Discovery Fixes



Miscellaneous Discovery fixes:
- Fix rejection followed by acceptance in handling RPL and RPS
  unsolicited events
- Fix for vport delete crash
- Fix PLOGI vs ADISC race condition

Signed-off-by: default avatarJames Smart <james.smart@emulex.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent e59058c4
Loading
Loading
Loading
Loading
+97 −89
Original line number Diff line number Diff line
@@ -1555,6 +1555,83 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
	return 0;
}

/**
 * lpfc_rscn_disc: Perform rscn discovery for a vport.
 * @vport: pointer to a host virtual N_Port data structure.
 *
 * This routine performs Registration State Change Notification (RSCN)
 * discovery for a @vport. If the @vport's node port recovery count is not
 * zero, it will invoke the lpfc_els_disc_plogi() to perform PLOGI for all
 * the nodes that need recovery. If none of the PLOGI were needed through
 * the lpfc_els_disc_plogi() routine, the lpfc_end_rscn() routine shall be
 * invoked to check and handle possible more RSCN came in during the period
 * of processing the current ones.
 **/
static void
lpfc_rscn_disc(struct lpfc_vport *vport)
{
	lpfc_can_disctmo(vport);

	/* RSCN discovery */
	/* go thru NPR nodes and issue ELS PLOGIs */
	if (vport->fc_npr_cnt)
		if (lpfc_els_disc_plogi(vport))
			return;

	lpfc_end_rscn(vport);
}

/**
 * lpfc_adisc_done: Complete the adisc phase of discovery.
 * @vport: pointer to lpfc_vport hba data structure that finished all ADISCs.
 *
 * This function is called when the final ADISC is completed during discovery.
 * This function handles clearing link attention or issuing reg_vpi depending
 * on whether npiv is enabled. This function also kicks off the PLOGI phase of
 * discovery.
 * This function is called with no locks held.
 **/
static void
lpfc_adisc_done(struct lpfc_vport *vport)
{
	struct Scsi_Host   *shost = lpfc_shost_from_vport(vport);
	struct lpfc_hba   *phba = vport->phba;

	/*
	 * For NPIV, cmpl_reg_vpi will set port_state to READY,
	 * and continue discovery.
	 */
	if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
	    !(vport->fc_flag & FC_RSCN_MODE)) {
		lpfc_issue_reg_vpi(phba, vport);
		return;
	}
	/*
	* For SLI2, we need to set port_state to READY
	* and continue discovery.
	*/
	if (vport->port_state < LPFC_VPORT_READY) {
		/* If we get here, there is nothing to ADISC */
		if (vport->port_type == LPFC_PHYSICAL_PORT)
			lpfc_issue_clear_la(phba, vport);
		if (!(vport->fc_flag & FC_ABORT_DISCOVERY)) {
			vport->num_disc_nodes = 0;
			/* go thru NPR list, issue ELS PLOGIs */
			if (vport->fc_npr_cnt)
				lpfc_els_disc_plogi(vport);
			if (!vport->num_disc_nodes) {
				spin_lock_irq(shost->host_lock);
				vport->fc_flag &= ~FC_NDISC_ACTIVE;
				spin_unlock_irq(shost->host_lock);
				lpfc_can_disctmo(vport);
				lpfc_end_rscn(vport);
			}
		}
		vport->port_state = LPFC_VPORT_READY;
	} else
		lpfc_rscn_disc(vport);
}

/**
 * lpfc_more_adisc: Issue more adisc as needed.
 * @vport: pointer to a host virtual N_Port data structure.
@@ -1583,35 +1660,11 @@ lpfc_more_adisc(struct lpfc_vport *vport)
		/* go thru NPR nodes and issue any remaining ELS ADISCs */
		sentadisc = lpfc_els_disc_adisc(vport);
	}
	if (!vport->num_disc_nodes)
		lpfc_adisc_done(vport);
	return;
}

/**
 * lpfc_rscn_disc: Perform rscn discovery for a vport.
 * @vport: pointer to a host virtual N_Port data structure.
 *
 * This routine performs Registration State Change Notification (RSCN)
 * discovery for a @vport. If the @vport's node port recovery count is not
 * zero, it will invoke the lpfc_els_disc_plogi() to perform PLOGI for all
 * the nodes that need recovery. If none of the PLOGI were needed through
 * the lpfc_els_disc_plogi() routine, the lpfc_end_rscn() routine shall be
 * invoked to check and handle possible more RSCN came in during the period
 * of processing the current ones.
 **/
static void
lpfc_rscn_disc(struct lpfc_vport *vport)
{
	lpfc_can_disctmo(vport);

	/* RSCN discovery */
	/* go thru NPR nodes and issue ELS PLOGIs */
	if (vport->fc_npr_cnt)
		if (lpfc_els_disc_plogi(vport))
			return;

	lpfc_end_rscn(vport);
}

/**
 * lpfc_cmpl_els_adisc: Completion callback function for adisc.
 * @phba: pointer to lpfc hba data structure.
@@ -1692,52 +1745,9 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
		lpfc_disc_state_machine(vport, ndlp, cmdiocb,
					NLP_EVT_CMPL_ADISC);

	if (disc && vport->num_disc_nodes) {
	/* Check to see if there are more ADISCs to be sent */
	if (disc && vport->num_disc_nodes)
		lpfc_more_adisc(vport);

		/* Check to see if we are done with ADISC authentication */
		if (vport->num_disc_nodes == 0) {
			/* If we get here, there is nothing left to ADISC */
			/*
			 * For NPIV, cmpl_reg_vpi will set port_state to READY,
			 * and continue discovery.
			 */
			if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
			   !(vport->fc_flag & FC_RSCN_MODE)) {
				lpfc_issue_reg_vpi(phba, vport);
				goto out;
			}
			/*
			 * For SLI2, we need to set port_state to READY
			 * and continue discovery.
			 */
			if (vport->port_state < LPFC_VPORT_READY) {
				/* If we get here, there is nothing to ADISC */
				if (vport->port_type == LPFC_PHYSICAL_PORT)
					lpfc_issue_clear_la(phba, vport);

				if (!(vport->fc_flag & FC_ABORT_DISCOVERY)) {
					vport->num_disc_nodes = 0;
					/* go thru NPR list, issue ELS PLOGIs */
					if (vport->fc_npr_cnt)
						lpfc_els_disc_plogi(vport);

					if (!vport->num_disc_nodes) {
						spin_lock_irq(shost->host_lock);
						vport->fc_flag &=
							~FC_NDISC_ACTIVE;
						spin_unlock_irq(
							shost->host_lock);
						lpfc_can_disctmo(vport);
					}
				}
				vport->port_state = LPFC_VPORT_READY;
			} else {
				lpfc_rscn_disc(vport);
			}
		}
	}
out:
	lpfc_els_free_iocb(phba, cmdiocb);
	return;
@@ -2258,13 +2268,9 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
			if (vport->port_state < LPFC_VPORT_READY) {
				/* Check if there are more ADISCs to be sent */
				lpfc_more_adisc(vport);
				if ((vport->num_disc_nodes == 0) &&
				    (vport->fc_npr_cnt))
					lpfc_els_disc_plogi(vport);
			} else {
				/* Check if there are more PLOGIs to be sent */
				lpfc_more_plogi(vport);
			}
				if (vport->num_disc_nodes == 0) {
					spin_lock_irq(shost->host_lock);
					vport->fc_flag &= ~FC_NDISC_ACTIVE;
@@ -2274,6 +2280,7 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
				}
			}
		}
	}
	return;
}

@@ -4480,14 +4487,9 @@ lpfc_els_rcv_rps(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
	struct ls_rjt stat;

	if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
	    (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) {
		stat.un.b.lsRjtRsvd0 = 0;
		stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
		stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
		stat.un.b.vendorUnique = 0;
		lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
			NULL);
	}
	    (ndlp->nlp_state != NLP_STE_MAPPED_NODE))
		/* reject the unsolicited RPS request and done with it */
		goto reject_out;

	pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
	lp = (uint32_t *) pcmd->virt;
@@ -4520,6 +4522,9 @@ lpfc_els_rcv_rps(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
			mempool_free(mbox, phba->mbox_mem_pool);
		}
	}

reject_out:
	/* issue rejection response */
	stat.un.b.lsRjtRsvd0 = 0;
	stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
	stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
@@ -4629,12 +4634,15 @@ lpfc_els_rcv_rpl(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,

	if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
	    (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) {
		/* issue rejection response */
		stat.un.b.lsRjtRsvd0 = 0;
		stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
		stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
		stat.un.b.vendorUnique = 0;
		lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
			NULL);
		/* rejected the unsolicited RPL request and done with it */
		return 0;
	}

	pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+0 −8
Original line number Diff line number Diff line
@@ -1580,14 +1580,6 @@ lpfc_cleanup(struct lpfc_vport *vport)
		lpfc_disc_state_machine(vport, ndlp, NULL,
					     NLP_EVT_DEVICE_RM);

		/* nlp_type zero is not defined, nlp_flag zero also not defined,
		 * nlp_state is unused, this happens when
		 * an initiator has logged
		 * into us so cleanup this ndlp.
		 */
		if ((ndlp->nlp_type == 0) && (ndlp->nlp_flag == 0) &&
			(ndlp->nlp_state == 0))
			lpfc_nlp_put(ndlp);
	}

	/* At this point, ALL ndlp's should be gone
+1 −13
Original line number Diff line number Diff line
@@ -1003,20 +1003,8 @@ lpfc_rcv_plogi_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
			spin_lock_irq(shost->host_lock);
			ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
			spin_unlock_irq(shost->host_lock);

			if (vport->num_disc_nodes) {
			if (vport->num_disc_nodes)
				lpfc_more_adisc(vport);
				if ((vport->num_disc_nodes == 0) &&
				    (vport->fc_npr_cnt))
					lpfc_els_disc_plogi(vport);
				if (vport->num_disc_nodes == 0) {
					spin_lock_irq(shost->host_lock);
					vport->fc_flag &= ~FC_NDISC_ACTIVE;
					spin_unlock_irq(shost->host_lock);
					lpfc_can_disctmo(vport);
					lpfc_end_rscn(vport);
				}
			}
		}
		return ndlp->nlp_state;
	}
+74 −0
Original line number Diff line number Diff line
@@ -204,6 +204,77 @@ lpfc_unique_wwpn(struct lpfc_hba *phba, struct lpfc_vport *new_vport)
	return 1;
}

/**
 * lpfc_discovery_wait: Wait for driver discovery to quiesce.
 * @vport: The virtual port for which this call is being executed.
 *
 * This driver calls this routine specifically from lpfc_vport_delete
 * to enforce a synchronous execution of vport
 * delete relative to discovery activities.  The
 * lpfc_vport_delete routine should not return until it
 * can reasonably guarantee that discovery has quiesced.
 * Post FDISC LOGO, the driver must wait until its SAN teardown is
 * complete and all resources recovered before allowing
 * cleanup.
 *
 * This routine does not require any locks held.
 **/
static void lpfc_discovery_wait(struct lpfc_vport *vport)
{
	struct lpfc_hba *phba = vport->phba;
	uint32_t wait_flags = 0;
	unsigned long wait_time_max;
	unsigned long start_time;

	wait_flags = FC_RSCN_MODE | FC_RSCN_DISCOVERY | FC_NLP_MORE |
		     FC_RSCN_DEFERRED | FC_NDISC_ACTIVE | FC_DISC_TMO;

	/*
	 * The time constraint on this loop is a balance between the
	 * fabric RA_TOV value and dev_loss tmo.  The driver's
	 * devloss_tmo is 10 giving this loop a 3x multiplier minimally.
	 */
	wait_time_max = msecs_to_jiffies(((phba->fc_ratov * 3) + 3) * 1000);
	wait_time_max += jiffies;
	start_time = jiffies;
	while (time_before(jiffies, wait_time_max)) {
		if ((vport->num_disc_nodes > 0)    ||
		    (vport->fc_flag & wait_flags)  ||
		    ((vport->port_state > LPFC_VPORT_FAILED) &&
		     (vport->port_state < LPFC_VPORT_READY))) {
			lpfc_printf_log(phba, KERN_INFO, LOG_VPORT,
					"1833 Vport discovery quiesce Wait:"
					" vpi x%x state x%x fc_flags x%x"
					" num_nodes x%x, waiting 1000 msecs"
					" total wait msecs x%x\n",
					vport->vpi, vport->port_state,
					vport->fc_flag, vport->num_disc_nodes,
					jiffies_to_msecs(jiffies - start_time));
			msleep(1000);
		} else {
			/* Base case.  Wait variants satisfied.  Break out */
			lpfc_printf_log(phba, KERN_INFO, LOG_VPORT,
					 "1834 Vport discovery quiesced:"
					 " vpi x%x state x%x fc_flags x%x"
					 " wait msecs x%x\n",
					 vport->vpi, vport->port_state,
					 vport->fc_flag,
					 jiffies_to_msecs(jiffies
						- start_time));
			break;
		}
	}

	if (time_after(jiffies, wait_time_max))
		lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
				"1835 Vport discovery quiesce failed:"
				" vpi x%x state x%x fc_flags x%x"
				" wait msecs x%x\n",
				vport->vpi, vport->port_state,
				vport->fc_flag,
				jiffies_to_msecs(jiffies - start_time));
}

int
lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
{
@@ -602,6 +673,9 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
				timeout = schedule_timeout(timeout);
	}

	if (!(phba->pport->load_flag & FC_UNLOADING))
		lpfc_discovery_wait(vport);

skip_logo:
	lpfc_cleanup(vport);
	lpfc_sli_host_down(vport);