scsi: ufs: core: Fix race between force complete and ISR
authorAlice Chao <alice.chao@mediatek.com>
Tue, 24 Oct 2023 08:43:21 +0000 (16:43 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 25 Oct 2023 02:43:24 +0000 (22:43 -0400)
While error handler force complete command (Thread A) and completion IRQ
raising (Thread B) of the same command, it may cause race condition.

Below is racing step (from 1 to 6):
ufshcd_mcq_compl_pending_transfer (Thread A)
1 if (cmd && !test_bit(SCMD_STATE_COMPLETE, &cmd->state)) {
5 spin_lock_irqsave(&hwq->cq_lock, flags); // wait lock release
set_host_byte(cmd, DID_REQUEUE);
6 ufshcd_release_scsi_cmd(hba, lrbp); // access null pointer
scsi_done(cmd);
spin_unlock_irqrestore(&hwq->cq_lock, flags);
}

ufshcd_mcq_poll_cqe_lock (Thread B)
2 spin_lock_irqsave(&hwq->cq_lock, flags);
 ufshcd_mcq_poll_cqe_nolock()
  ufshcd_compl_one_cqe()
3    ufshcd_release_scsi_cmd() // lrbp->cmd = NULL;
4 spin_unlock_irqrestore(&hwq->cq_lock, flags);

Signed-off-by: Alice Chao <alice.chao@mediatek.com>
Link: https://lore.kernel.org/r/20231024084324.12197-1-alice.chao@mediatek.com
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/ufs/core/ufshcd.c

index 5c4f2643dde69ec5b9550f55e47feacd132e09dd..24aa7a2de9daa33cad0f79cee8ffc3089b045466 100644 (file)
@@ -5615,13 +5615,13 @@ static void ufshcd_mcq_compl_pending_transfer(struct ufs_hba *hba,
                         * For those cmds of which the cqes are not present
                         * in the cq, complete them explicitly.
                         */
+                       spin_lock_irqsave(&hwq->cq_lock, flags);
                        if (cmd && !test_bit(SCMD_STATE_COMPLETE, &cmd->state)) {
-                               spin_lock_irqsave(&hwq->cq_lock, flags);
                                set_host_byte(cmd, DID_REQUEUE);
                                ufshcd_release_scsi_cmd(hba, lrbp);
                                scsi_done(cmd);
-                               spin_unlock_irqrestore(&hwq->cq_lock, flags);
                        }
+                       spin_unlock_irqrestore(&hwq->cq_lock, flags);
                } else {
                        ufshcd_mcq_poll_cqe_lock(hba, hwq);
                }