From a24461d3b1dffc4e0d6c3a6b0d36184fcf19338c Mon Sep 17 00:00:00 2001 From: Can Guo Date: Thu, 6 Aug 2020 18:41:56 +0800 Subject: [PATCH] scsi: ufs: Fix some racing problems in ufshcd_shutdown() Currently I/O requests could be still submitted to UFS device while UFS is working on shutdown flow. This may lead to racing as below scenarios and finally system may crash due to unclocked register accesses. To fix this kind of issues, in ufshcd_shutdown(), 1. Use pm_runtime_get_sync() instead of resuming UFS device by ufshcd_runtime_resume() "internally" to let runtime PM framework manage and prevent concurrent runtime operations by incoming I/O requests. 2. Specifically quiesce the SCSI device of UFS Device well known LU so that ufshcd_suspend() can still send the SSU cmd, and remove all the other SCSI devices to block all I/O requests to those SCSI devices. Example of racing scenario: While UFS device is runtime-suspended Thread #1: Executing UFS shutdown flow, e.g., ufshcd_suspend(UFS_SHUTDOWN_PM) Thread #2: Executing runtime resume flow triggered by I/O request, e.g., ufshcd_resume(UFS_RUNTIME_PM) This breaks the assumption that UFS PM flows can not be running concurrently and some unexpected racing behavior may happen. Change-Id: I83345e6b48a832ec7962fc016f4fdf128c9a5bd1 Signed-off-by: Can Guo --- drivers/scsi/ufs/ufshcd.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 5fad6222f75c..d0d88b9d4a71 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4943,6 +4943,10 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) spin_unlock_irqrestore(hba->host->host_lock, flags); } +#if defined(CONFIG_SCSI_UFSHCD_QTI) + return; +#endif + ufshcd_crypto_destroy_rq_keyslot_manager(hba, q); } @@ -8954,6 +8958,9 @@ EXPORT_SYMBOL(ufshcd_runtime_idle); int ufshcd_shutdown(struct ufs_hba *hba) { int ret = 0; +#if defined(CONFIG_SCSI_UFSHCD_QTI) + struct scsi_device *sdev; +#endif if (!hba->is_powered) goto out; @@ -8961,11 +8968,30 @@ int ufshcd_shutdown(struct ufs_hba *hba) if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba)) goto out; +#if defined(CONFIG_SCSI_UFSHCD_QTI) + pm_runtime_get_sync(hba->dev); + + /* + * I/O requests could be still submitted to SCSI devices + * when we are here. Quiesce the scsi device of UFS Device + * well known LU but remove all the other scsi devices. + * After the scsi device is quiesced, only PM requests can + * pass through SCSI layer, which well serves the purpose + * of sending the SSU cmd during ufshcd_suspend(). + */ + shost_for_each_device(sdev, hba->host) { + if (sdev == hba->sdev_ufs_device) + scsi_device_quiesce(sdev); + else + scsi_remove_device(sdev); + } +#else if (pm_runtime_suspended(hba->dev)) { ret = ufshcd_runtime_resume(hba); if (ret) goto out; } +#endif ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM); out: