mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
27c0f718d3
commit
375f3a5691
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user