scsi: ufs: core: Avoid unsafe MMIO reads in ufshcd_mcq_compl_all_cqes_lock()

During MCQ host reset, ufshcd_host_reset_and_restore() stops the host
controller via ufshcd_hba_stop() (HCE = 0) before calling
ufshcd_complete_requests(hba, true) ->
ufshcd_mcq_compl_pending_transfer(hba, true) ->
ufshcd_mcq_force_compl_one() -> ufshcd_mcq_compl_all_cqes_lock().  Because
ufshcd_mcq_force_compl_one() is its sole caller,
ufshcd_mcq_compl_all_cqes_lock() always runs with HCE = 0.

Despite the comment above ufshcd_mcq_compl_all_cqes_lock() stating that
reading CQTPy may not be safe with the controller disabled, the function
still calls ufshcd_mcq_update_cq_tail_slot() at the end of its sweep:

 1. Unsafe CQTPy MMIO read:
    Calling ufshcd_mcq_update_cq_tail_slot() at the end of the sweep reads
    CQTPy over MMIO while HCE = 0, directly contradicting the function's
    documented contract (commit 1373df88d5 ("scsi: ufs: core: Add a
    comment block above ufshcd_mcq_compl_all_cqes_lock()")) that reading
    CQTPy may not be safe with the controller disabled.

 2. Spurious error logs on empty slots:
    Sweeping all max_entries slots visits empty entries where
    command_desc_base_addr is 0, causing ufshcd_mcq_process_cqe() to log
    unguarded dev_err(hba->dev, "Abnormal CQ entry!\n") messages.

Fix both issues in ufshcd_mcq_compl_all_cqes_lock():

 - Remove the ufshcd_mcq_update_cq_tail_slot() call and the redundant
   hwq->cq_head_slot = hwq->cq_tail_slot assignment without replacement.
   The two indices are already equal after the sweep: they are equal when
   the sweep starts, since ufshcd_mcq_poll_cqe_lock() consumes entries
   until cq_head_slot reaches cq_tail_slot, and the sweep advances
   cq_head_slot by exactly one full ring. Both indices are also
   reinitialized before the queue is reused.

 - Extract ufshcd_mcq_compl_cqe() and invoke it only on non-empty slots
   during full-ring sweeps, keeping "Abnormal CQ entry!" logging strictly
   for unexpected empty entries in ufshcd_mcq_poll_cqe_lock().

Fixes: ab248643d3 ("scsi: ufs: core: Add error handling for MCQ mode")
Cc: stable@vger.kernel.org
Reviewed-by: Peter Wang <peter.wang@mediatek.com>
Signed-off-by: Stanley Jhu <stanleyjhu@google.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Link: https://patch.msgid.link/20260928035816.1294326-2-stanleyjhu@google.com
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
This commit is contained in:
Stanley Jhu 2026-09-28 11:58:15 +08:00 committed by Martin K. Petersen (Oracle)
parent 27c0f718d3
commit 375f3a5691

View File

@ -312,20 +312,24 @@ static int ufshcd_mcq_get_tag(struct ufs_hba *hba, struct cq_entry *cqe)
UFSHCD_NUM_RESERVED;
}
static void ufshcd_mcq_compl_cqe(struct ufs_hba *hba, struct cq_entry *cqe)
{
int tag = ufshcd_mcq_get_tag(hba, cqe);
ufshcd_compl_one_cqe(hba, tag, cqe);
/* After processing the CQE, mark it as an empty (invalid) entry. */
cqe->command_desc_base_addr = 0;
}
static void ufshcd_mcq_process_cqe(struct ufs_hba *hba,
struct ufs_hw_queue *hwq)
{
struct cq_entry *cqe = ufshcd_mcq_cur_cqe(hwq);
if (cqe->command_desc_base_addr) {
int tag = ufshcd_mcq_get_tag(hba, cqe);
ufshcd_compl_one_cqe(hba, tag, cqe);
/* After processed the cqe, mark it empty (invalid) entry */
cqe->command_desc_base_addr = 0;
} else {
if (cqe->command_desc_base_addr)
ufshcd_mcq_compl_cqe(hba, cqe);
else
dev_err(hba->dev, "Abnormal CQ entry!\n");
}
}
/*
@ -333,7 +337,7 @@ static void ufshcd_mcq_process_cqe(struct ufs_hba *hba,
* controller disabled (HCE = 0). Reading host controller registers, e.g. the
* CQ tail pointer (CQTPy), may not be safe with the host controller disabled.
* Hence, iterate over all completion queue entries. This won't result in
* double completions because ufshcd_mcq_process_cqe() clears a CQE after it
* double completions because ufshcd_mcq_compl_cqe() clears a CQE after it
* has been processed.
*/
void ufshcd_mcq_compl_all_cqes_lock(struct ufs_hba *hba,
@ -344,13 +348,13 @@ void ufshcd_mcq_compl_all_cqes_lock(struct ufs_hba *hba,
spin_lock_irqsave(&hwq->cq_lock, flags);
while (entries > 0) {
ufshcd_mcq_process_cqe(hba, hwq);
struct cq_entry *cqe = ufshcd_mcq_cur_cqe(hwq);
if (cqe->command_desc_base_addr)
ufshcd_mcq_compl_cqe(hba, cqe);
ufshcd_mcq_inc_cq_head_slot(hwq);
entries--;
}
ufshcd_mcq_update_cq_tail_slot(hwq);
hwq->cq_head_slot = hwq->cq_tail_slot;
spin_unlock_irqrestore(&hwq->cq_lock, flags);
}