Skip to content

Commit af984c8

Browse files
jsmart-ghmartinkpetersen
authored andcommitted
scsi: lpfc: Allow fabric node recovery if recovery is in progress before devloss
A link bounce to a slow fabric may observe FDISC response delays lasting longer than devloss tmo. Current logic decrements the final fabric node kref during a devloss tmo event. This results in a NULL ptr dereference crash if the FDISC completes for that fabric node after devloss tmo. Fix by adding the NLP_IN_RECOV_POST_DEV_LOSS flag, which is set when devloss tmo triggers and we've noticed that fabric node recovery has already started or finished in between the time lpfc_dev_loss_tmo_callbk queues lpfc_dev_loss_tmo_handler. If fabric node recovery succeeds, then the driver reverses the devloss tmo marked kref put with a kref get. If fabric node recovery fails, then the final kref put relies on the ELS timing out or the REG_LOGIN cmpl routine. Link: https://lore.kernel.org/r/[email protected] Co-developed-by: Justin Tee <[email protected]> Signed-off-by: Justin Tee <[email protected]> Signed-off-by: James Smart <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 1854f53 commit af984c8

File tree

6 files changed

+139
-15
lines changed

6 files changed

+139
-15
lines changed

drivers/scsi/lpfc/lpfc_crtn.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ int lpfc_check_sli_ndlp(struct lpfc_hba *, struct lpfc_sli_ring *,
119119
struct lpfc_nodelist *lpfc_nlp_init(struct lpfc_vport *vport, uint32_t did);
120120
struct lpfc_nodelist *lpfc_nlp_get(struct lpfc_nodelist *);
121121
int lpfc_nlp_put(struct lpfc_nodelist *);
122+
void lpfc_check_nlp_post_devloss(struct lpfc_vport *vport,
123+
struct lpfc_nodelist *ndlp);
122124
void lpfc_ignore_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
123125
struct lpfc_iocbq *rspiocb);
124126
int lpfc_nlp_not_used(struct lpfc_nodelist *ndlp);

drivers/scsi/lpfc/lpfc_disc.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ enum lpfc_fc4_xpt_flags {
8585
NLP_XPT_HAS_HH = 0x10
8686
};
8787

88+
enum lpfc_nlp_save_flags {
89+
/* devloss occurred during recovery */
90+
NLP_IN_RECOV_POST_DEV_LOSS = 0x1,
91+
/* wait for outstanding LOGO to cmpl */
92+
NLP_WAIT_FOR_LOGO = 0x2,
93+
};
94+
8895
struct lpfc_nodelist {
8996
struct list_head nlp_listp;
9097
struct serv_parm fc_sparam; /* buffer for service params */
@@ -144,8 +151,9 @@ struct lpfc_nodelist {
144151
unsigned long *active_rrqs_xri_bitmap;
145152
struct lpfc_scsicmd_bkt *lat_data; /* Latency data */
146153
uint32_t fc4_prli_sent;
147-
u32 upcall_flags;
148-
#define NLP_WAIT_FOR_LOGO 0x2
154+
155+
/* flags to keep ndlp alive until special conditions are met */
156+
enum lpfc_nlp_save_flags save_flags;
149157

150158
enum lpfc_fc4_xpt_flags fc4_xpt_flags;
151159

drivers/scsi/lpfc/lpfc_els.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,9 +2905,9 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
29052905
irsp = &(rspiocb->iocb);
29062906
spin_lock_irq(&ndlp->lock);
29072907
ndlp->nlp_flag &= ~NLP_LOGO_SND;
2908-
if (ndlp->upcall_flags & NLP_WAIT_FOR_LOGO) {
2908+
if (ndlp->save_flags & NLP_WAIT_FOR_LOGO) {
29092909
wake_up_waiter = 1;
2910-
ndlp->upcall_flags &= ~NLP_WAIT_FOR_LOGO;
2910+
ndlp->save_flags &= ~NLP_WAIT_FOR_LOGO;
29112911
}
29122912
spin_unlock_irq(&ndlp->lock);
29132913

@@ -10735,6 +10735,9 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
1073510735
irsp->ulpStatus, irsp->un.ulpWord[4]);
1073610736
goto fdisc_failed;
1073710737
}
10738+
10739+
lpfc_check_nlp_post_devloss(vport, ndlp);
10740+
1073810741
spin_lock_irq(shost->host_lock);
1073910742
vport->fc_flag &= ~FC_VPORT_CVL_RCVD;
1074010743
vport->fc_flag &= ~FC_VPORT_LOGO_RCVD;

drivers/scsi/lpfc/lpfc_hbadisc.c

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,12 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
209209

210210
spin_lock_irqsave(&ndlp->lock, iflags);
211211
ndlp->nlp_flag |= NLP_IN_DEV_LOSS;
212-
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
212+
213+
/* If there is a PLOGI in progress, and we are in a
214+
* NLP_NPR_2B_DISC state, don't turn off the flag.
215+
*/
216+
if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE)
217+
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
213218

214219
/*
215220
* The backend does not expect any more calls associated with this
@@ -340,6 +345,37 @@ static void lpfc_check_inactive_vmid(struct lpfc_hba *phba)
340345
lpfc_destroy_vport_work_array(phba, vports);
341346
}
342347

348+
/**
349+
* lpfc_check_nlp_post_devloss - Check to restore ndlp refcnt after devloss
350+
* @vport: Pointer to vport object.
351+
* @ndlp: Pointer to remote node object.
352+
*
353+
* If NLP_IN_RECOV_POST_DEV_LOSS flag was set due to outstanding recovery of
354+
* node during dev_loss_tmo processing, then this function restores the nlp_put
355+
* kref decrement from lpfc_dev_loss_tmo_handler.
356+
**/
357+
void
358+
lpfc_check_nlp_post_devloss(struct lpfc_vport *vport,
359+
struct lpfc_nodelist *ndlp)
360+
{
361+
unsigned long iflags;
362+
363+
spin_lock_irqsave(&ndlp->lock, iflags);
364+
if (ndlp->save_flags & NLP_IN_RECOV_POST_DEV_LOSS) {
365+
ndlp->save_flags &= ~NLP_IN_RECOV_POST_DEV_LOSS;
366+
spin_unlock_irqrestore(&ndlp->lock, iflags);
367+
lpfc_nlp_get(ndlp);
368+
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY | LOG_NODE,
369+
"8438 Devloss timeout reversed on DID x%x "
370+
"refcnt %d ndlp %p flag x%x "
371+
"port_state = x%x\n",
372+
ndlp->nlp_DID, kref_read(&ndlp->kref), ndlp,
373+
ndlp->nlp_flag, vport->port_state);
374+
spin_lock_irqsave(&ndlp->lock, iflags);
375+
}
376+
spin_unlock_irqrestore(&ndlp->lock, iflags);
377+
}
378+
343379
/**
344380
* lpfc_dev_loss_tmo_handler - Remote node devloss timeout handler
345381
* @ndlp: Pointer to remote node object.
@@ -358,6 +394,8 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
358394
uint8_t *name;
359395
int warn_on = 0;
360396
int fcf_inuse = 0;
397+
bool recovering = false;
398+
struct fc_vport *fc_vport = NULL;
361399
unsigned long iflags;
362400

363401
vport = ndlp->vport;
@@ -394,6 +432,64 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
394432

395433
/* Fabric nodes are done. */
396434
if (ndlp->nlp_type & NLP_FABRIC) {
435+
spin_lock_irqsave(&ndlp->lock, iflags);
436+
/* In massive vport configuration settings, it's possible
437+
* dev_loss_tmo fired during node recovery. So, check if
438+
* fabric nodes are in discovery states outstanding.
439+
*/
440+
switch (ndlp->nlp_DID) {
441+
case Fabric_DID:
442+
fc_vport = vport->fc_vport;
443+
if (fc_vport &&
444+
fc_vport->vport_state == FC_VPORT_INITIALIZING)
445+
recovering = true;
446+
break;
447+
case Fabric_Cntl_DID:
448+
if (ndlp->nlp_flag & NLP_REG_LOGIN_SEND)
449+
recovering = true;
450+
break;
451+
case FDMI_DID:
452+
fallthrough;
453+
case NameServer_DID:
454+
if (ndlp->nlp_state >= NLP_STE_PLOGI_ISSUE &&
455+
ndlp->nlp_state <= NLP_STE_REG_LOGIN_ISSUE)
456+
recovering = true;
457+
break;
458+
}
459+
spin_unlock_irqrestore(&ndlp->lock, iflags);
460+
461+
/* Mark an NLP_IN_RECOV_POST_DEV_LOSS flag to know if reversing
462+
* the following lpfc_nlp_put is necessary after fabric node is
463+
* recovered.
464+
*/
465+
if (recovering) {
466+
lpfc_printf_vlog(vport, KERN_INFO,
467+
LOG_DISCOVERY | LOG_NODE,
468+
"8436 Devloss timeout marked on "
469+
"DID x%x refcnt %d ndlp %p "
470+
"flag x%x port_state = x%x\n",
471+
ndlp->nlp_DID, kref_read(&ndlp->kref),
472+
ndlp, ndlp->nlp_flag,
473+
vport->port_state);
474+
spin_lock_irqsave(&ndlp->lock, iflags);
475+
ndlp->save_flags |= NLP_IN_RECOV_POST_DEV_LOSS;
476+
spin_unlock_irqrestore(&ndlp->lock, iflags);
477+
} else if (ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
478+
/* Fabric node fully recovered before this dev_loss_tmo
479+
* queue work is processed. Thus, ignore the
480+
* dev_loss_tmo event.
481+
*/
482+
lpfc_printf_vlog(vport, KERN_INFO,
483+
LOG_DISCOVERY | LOG_NODE,
484+
"8437 Devloss timeout ignored on "
485+
"DID x%x refcnt %d ndlp %p "
486+
"flag x%x port_state = x%x\n",
487+
ndlp->nlp_DID, kref_read(&ndlp->kref),
488+
ndlp, ndlp->nlp_flag,
489+
vport->port_state);
490+
return fcf_inuse;
491+
}
492+
397493
lpfc_nlp_put(ndlp);
398494
return fcf_inuse;
399495
}
@@ -423,6 +519,14 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
423519
ndlp->nlp_state, ndlp->nlp_rpi);
424520
}
425521

522+
/* If we are devloss, but we are in the process of rediscovering the
523+
* ndlp, don't issue a NLP_EVT_DEVICE_RM event.
524+
*/
525+
if (ndlp->nlp_state >= NLP_STE_PLOGI_ISSUE &&
526+
ndlp->nlp_state <= NLP_STE_PRLI_ISSUE) {
527+
return fcf_inuse;
528+
}
529+
426530
if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD))
427531
lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
428532

@@ -4363,6 +4467,8 @@ lpfc_mbx_cmpl_fc_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
43634467
goto out;
43644468
}
43654469

4470+
lpfc_check_nlp_post_devloss(vport, ndlp);
4471+
43664472
if (phba->sli_rev < LPFC_SLI_REV4)
43674473
ndlp->nlp_rpi = mb->un.varWords[0];
43684474

@@ -4540,9 +4646,10 @@ lpfc_nlp_counters(struct lpfc_vport *vport, int state, int count)
45404646
void
45414647
lpfc_nlp_reg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
45424648
{
4543-
45444649
unsigned long iflags;
45454650

4651+
lpfc_check_nlp_post_devloss(vport, ndlp);
4652+
45464653
spin_lock_irqsave(&ndlp->lock, iflags);
45474654
if (ndlp->fc4_xpt_flags & NLP_XPT_REGD) {
45484655
/* Already registered with backend, trigger rescan */

drivers/scsi/lpfc/lpfc_init.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3753,12 +3753,16 @@ lpfc_offline_prep(struct lpfc_hba *phba, int mbx_action)
37533753
lpfc_disc_state_machine(vports[i], ndlp,
37543754
NULL, NLP_EVT_DEVICE_RECOVERY);
37553755

3756-
/* Don't remove the node unless the
3756+
/* Don't remove the node unless the node
37573757
* has been unregistered with the
3758-
* transport. If so, let dev_loss
3759-
* take care of the node.
3758+
* transport, and we're not in recovery
3759+
* before dev_loss_tmo triggered.
3760+
* Otherwise, let dev_loss take care of
3761+
* the node.
37603762
*/
3761-
if (!(ndlp->fc4_xpt_flags &
3763+
if (!(ndlp->save_flags &
3764+
NLP_IN_RECOV_POST_DEV_LOSS) &&
3765+
!(ndlp->fc4_xpt_flags &
37623766
(NVME_XPT_REGD | SCSI_XPT_REGD)))
37633767
lpfc_disc_state_machine
37643768
(vports[i], ndlp,

drivers/scsi/lpfc/lpfc_scsi.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6475,28 +6475,28 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd)
64756475

64766476
/* Issue LOGO, if no LOGO is outstanding */
64776477
spin_lock_irqsave(&pnode->lock, flags);
6478-
if (!(pnode->upcall_flags & NLP_WAIT_FOR_LOGO) &&
6478+
if (!(pnode->save_flags & NLP_WAIT_FOR_LOGO) &&
64796479
!pnode->logo_waitq) {
64806480
pnode->logo_waitq = &waitq;
64816481
pnode->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
64826482
pnode->nlp_flag |= NLP_ISSUE_LOGO;
6483-
pnode->upcall_flags |= NLP_WAIT_FOR_LOGO;
6483+
pnode->save_flags |= NLP_WAIT_FOR_LOGO;
64846484
spin_unlock_irqrestore(&pnode->lock, flags);
64856485
lpfc_unreg_rpi(vport, pnode);
64866486
wait_event_timeout(waitq,
6487-
(!(pnode->upcall_flags &
6487+
(!(pnode->save_flags &
64886488
NLP_WAIT_FOR_LOGO)),
64896489
msecs_to_jiffies(dev_loss_tmo *
64906490
1000));
64916491

6492-
if (pnode->upcall_flags & NLP_WAIT_FOR_LOGO) {
6492+
if (pnode->save_flags & NLP_WAIT_FOR_LOGO) {
64936493
lpfc_printf_vlog(vport, KERN_ERR, logit,
64946494
"0725 SCSI layer TGTRST "
64956495
"failed & LOGO TMO (%d, %llu) "
64966496
"return x%x\n",
64976497
tgt_id, lun_id, status);
64986498
spin_lock_irqsave(&pnode->lock, flags);
6499-
pnode->upcall_flags &= ~NLP_WAIT_FOR_LOGO;
6499+
pnode->save_flags &= ~NLP_WAIT_FOR_LOGO;
65006500
} else {
65016501
spin_lock_irqsave(&pnode->lock, flags);
65026502
}

0 commit comments

Comments
 (0)