[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: James Smart <james.smart@emulex.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
parent
e59058c440
commit
90160e010b
@ -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 */
|
||||
/* 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,19 +2268,16 @@ 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;
|
||||
spin_unlock_irq(shost->host_lock);
|
||||
lpfc_can_disctmo(vport);
|
||||
lpfc_end_rscn(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user