Commit c1d0299d authored by Justin Tee's avatar Justin Tee Committed by Ye Bin
Browse files

scsi: lpfc: Release hbalock before calling lpfc_worker_wake_up()

mainline inclusion
from mainline-v6.9-rc2
commit ded20192dff31c91cef2a04f7e20e60e9bb887d3
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9U211
CVE: CVE-2024-36924

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ded20192dff31c91cef2a04f7e20e60e9bb887d3



--------------------------------

lpfc_worker_wake_up() calls the lpfc_work_done() routine, which takes the
hbalock.  Thus, lpfc_worker_wake_up() should not be called while holding the
hbalock to avoid potential deadlock.

Signed-off-by: default avatarJustin Tee <justin.tee@broadcom.com>
Link: https://lore.kernel.org/r/20240305200503.57317-7-justintee8345@gmail.com


Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
Conflicts:
	drivers/scsi/lpfc/lpfc_hbadisc.c
[fix context diff]
Signed-off-by: default avatarYe Bin <yebin@huaweicloud.com>
parent c96c2b4f
Loading
Loading
Loading
Loading
+10 −10
Original line number Diff line number Diff line
@@ -3636,23 +3636,23 @@ lpfc_els_retry_delay(struct timer_list *t)
	unsigned long flags;
	struct lpfc_work_evt  *evtp = &ndlp->els_retry_evt;

	/* Hold a node reference for outstanding queued work */
	if (!lpfc_nlp_get(ndlp))
		return;

	spin_lock_irqsave(&phba->hbalock, flags);
	if (!list_empty(&evtp->evt_listp)) {
		spin_unlock_irqrestore(&phba->hbalock, flags);
		lpfc_nlp_put(ndlp);
		return;
	}

	/* We need to hold the node by incrementing the reference
	 * count until the queued work is done
	 */
	evtp->evt_arg1  = lpfc_nlp_get(ndlp);
	if (evtp->evt_arg1) {
	evtp->evt_arg1 = ndlp;
	evtp->evt = LPFC_EVT_ELS_RETRY;
	list_add_tail(&evtp->evt_listp, &phba->work_list);
		lpfc_worker_wake_up(phba);
	}
	spin_unlock_irqrestore(&phba->hbalock, flags);
	return;

	lpfc_worker_wake_up(phba);
}

/**
+2 −0
Original line number Diff line number Diff line
@@ -184,7 +184,9 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
	if (evtp->evt_arg1) {
		evtp->evt = LPFC_EVT_DEV_LOSS;
		list_add_tail(&evtp->evt_listp, &phba->work_list);
		spin_unlock_irqrestore(&phba->hbalock, iflags);
		lpfc_worker_wake_up(phba);
		return;
	}
	spin_unlock_irqrestore(&phba->hbalock, iflags);

+7 −7
Original line number Diff line number Diff line
@@ -1100,9 +1100,9 @@ lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
	empty = list_empty(&phba->active_rrq_list);
	list_add_tail(&rrq->list, &phba->active_rrq_list);
	phba->hba_flag |= HBA_RRQ_ACTIVE;
	spin_unlock_irqrestore(&phba->hbalock, iflags);
	if (empty)
		lpfc_worker_wake_up(phba);
	spin_unlock_irqrestore(&phba->hbalock, iflags);
	return 0;
out:
	spin_unlock_irqrestore(&phba->hbalock, iflags);
@@ -10379,18 +10379,18 @@ lpfc_sli_post_recovery_event(struct lpfc_hba *phba,
	unsigned long iflags;
	struct lpfc_work_evt  *evtp = &ndlp->recovery_evt;
	/* Hold a node reference for outstanding queued work */
	if (!lpfc_nlp_get(ndlp))
		return;
	spin_lock_irqsave(&phba->hbalock, iflags);
	if (!list_empty(&evtp->evt_listp)) {
		spin_unlock_irqrestore(&phba->hbalock, iflags);
		lpfc_nlp_put(ndlp);
		return;
	}
	/* Incrementing the reference count until the queued work is done. */
	evtp->evt_arg1  = lpfc_nlp_get(ndlp);
	if (!evtp->evt_arg1) {
		spin_unlock_irqrestore(&phba->hbalock, iflags);
		return;
	}
	evtp->evt_arg1 = ndlp;
	evtp->evt = LPFC_EVT_RECOVER_PORT;
	list_add_tail(&evtp->evt_listp, &phba->work_list);
	spin_unlock_irqrestore(&phba->hbalock, iflags);