[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:
James Smart 2008-08-24 21:49:45 -04:00 committed by James Bottomley
parent e59058c440
commit 90160e010b
4 changed files with 172 additions and 110 deletions

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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);