Skip to content

Commit 4281f44

Browse files
Justin Teemartinkpetersen
authored andcommitted
scsi: lpfc: Prevent NDLP reference count underflow in dev_loss_tmo callback
Current dev_loss_tmo handling checks whether there has been a previous call to unregister with SCSI transport. If so, the NDLP kref count is decremented a second time in dev_loss_tmo as the final kref release. However, this can sometimes result in a reference count underflow if there is also a race to unregister with NVMe transport as well. Add a check for NVMe transport registration before decrementing the final kref. If NVMe transport is still registered, then the NVMe transport unregistration is designated as the final kref decrement. Signed-off-by: Justin Tee <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Martin K. Petersen <[email protected]>
1 parent eb03836 commit 4281f44

File tree

1 file changed

+24
-12
lines changed

1 file changed

+24
-12
lines changed

drivers/scsi/lpfc/lpfc_hbadisc.c

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
161161
struct lpfc_hba *phba;
162162
struct lpfc_work_evt *evtp;
163163
unsigned long iflags;
164+
bool nvme_reg = false;
164165

165166
ndlp = ((struct lpfc_rport_data *)rport->dd_data)->pnode;
166167
if (!ndlp)
@@ -183,38 +184,49 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
183184
/* Don't schedule a worker thread event if the vport is going down. */
184185
if (test_bit(FC_UNLOADING, &vport->load_flag) ||
185186
!test_bit(HBA_SETUP, &phba->hba_flag)) {
187+
186188
spin_lock_irqsave(&ndlp->lock, iflags);
187189
ndlp->rport = NULL;
188190

191+
if (ndlp->fc4_xpt_flags & NVME_XPT_REGD)
192+
nvme_reg = true;
193+
189194
/* The scsi_transport is done with the rport so lpfc cannot
190-
* call to unregister. Remove the scsi transport reference
191-
* and clean up the SCSI transport node details.
195+
* call to unregister.
192196
*/
193-
if (ndlp->fc4_xpt_flags & (NLP_XPT_REGD | SCSI_XPT_REGD)) {
197+
if (ndlp->fc4_xpt_flags & SCSI_XPT_REGD) {
194198
ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD;
195199

196-
/* NVME transport-registered rports need the
197-
* NLP_XPT_REGD flag to complete an unregister.
200+
/* If NLP_XPT_REGD was cleared in lpfc_nlp_unreg_node,
201+
* unregister calls were made to the scsi and nvme
202+
* transports and refcnt was already decremented. Clear
203+
* the NLP_XPT_REGD flag only if the NVME Rport is
204+
* confirmed unregistered.
198205
*/
199-
if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD))
206+
if (!nvme_reg && ndlp->fc4_xpt_flags & NLP_XPT_REGD) {
200207
ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD;
208+
spin_unlock_irqrestore(&ndlp->lock, iflags);
209+
lpfc_nlp_put(ndlp); /* may free ndlp */
210+
} else {
211+
spin_unlock_irqrestore(&ndlp->lock, iflags);
212+
}
213+
} else {
201214
spin_unlock_irqrestore(&ndlp->lock, iflags);
202-
lpfc_nlp_put(ndlp);
203-
spin_lock_irqsave(&ndlp->lock, iflags);
204215
}
205216

217+
spin_lock_irqsave(&ndlp->lock, iflags);
218+
206219
/* Only 1 thread can drop the initial node reference. If
207220
* another thread has set NLP_DROPPED, this thread is done.
208221
*/
209-
if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD) &&
210-
!(ndlp->nlp_flag & NLP_DROPPED)) {
211-
ndlp->nlp_flag |= NLP_DROPPED;
222+
if (nvme_reg || (ndlp->nlp_flag & NLP_DROPPED)) {
212223
spin_unlock_irqrestore(&ndlp->lock, iflags);
213-
lpfc_nlp_put(ndlp);
214224
return;
215225
}
216226

227+
ndlp->nlp_flag |= NLP_DROPPED;
217228
spin_unlock_irqrestore(&ndlp->lock, iflags);
229+
lpfc_nlp_put(ndlp);
218230
return;
219231
}
220232

0 commit comments

Comments
 (0)