Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a24461d3 authored by Can Guo's avatar Can Guo
Browse files

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: default avatarCan Guo <cang@codeaurora.org>
parent 0e2097ce
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -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: