summaryrefslogtreecommitdiff
path: root/drivers/scsi
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2017-01-16 23:06:13 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2017-01-16 23:06:13 -0800
commita51b7f60003e6071703d5e4489dae0f3ab7ae264 (patch)
tree35b788c3f642c30d675b90ea0bf88da9652d24fa /drivers/scsi
parenta532239b3e0cbb2ed3deb98735516a684d724491 (diff)
parent3b284d45b42a96661a489a79e5faa1bb750306bf (diff)
Merge "scsi: ufs: fix deadlock between clock scaling and shutdown"
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/ufs/ufshcd.c43
1 files changed, 33 insertions, 10 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2138e81bb9e9..d46682bb1d84 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -8904,6 +8904,35 @@ static inline void ufshcd_add_sysfs_nodes(struct ufs_hba *hba)
ufshcd_add_spm_lvl_sysfs_nodes(hba);
}
+static void ufshcd_shutdown_clkscaling(struct ufs_hba *hba)
+{
+ bool suspend = false;
+ unsigned long flags;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->clk_scaling.is_allowed) {
+ hba->clk_scaling.is_allowed = false;
+ suspend = true;
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ /**
+ * Scaling may be scheduled before, hence make sure it
+ * doesn't race with shutdown
+ */
+ if (ufshcd_is_clkscaling_supported(hba)) {
+ device_remove_file(hba->dev, &hba->clk_scaling.enable_attr);
+ cancel_work_sync(&hba->clk_scaling.suspend_work);
+ cancel_work_sync(&hba->clk_scaling.resume_work);
+ if (suspend)
+ ufshcd_suspend_clkscaling(hba);
+ }
+
+ /* Unregister so that devfreq_monitor can't race with shutdown */
+ if (hba->devfreq)
+ devfreq_remove_device(hba->devfreq);
+}
+
/**
* ufshcd_shutdown - shutdown routine
* @hba: per adapter instance
@@ -8921,16 +8950,14 @@ int ufshcd_shutdown(struct ufs_hba *hba)
pm_runtime_get_sync(hba->dev);
ufshcd_hold_all(hba);
+ ufshcd_mark_shutdown_ongoing(hba);
+ ufshcd_shutdown_clkscaling(hba);
/**
- * (1) Set state to shutting down
- * (2) Acquire the lock to stop any more requests
- * (3) Suspend clock scaling
- * (4) Wait for all issued requests to complete
+ * (1) Acquire the lock to stop any more requests
+ * (2) Wait for all issued requests to complete
*/
- ufshcd_mark_shutdown_ongoing(hba);
ufshcd_get_write_lock(hba);
ufshcd_scsi_block_requests(hba);
- ufshcd_suspend_clkscaling(hba);
ret = ufshcd_wait_for_doorbell_clr(hba, U64_MAX);
if (ret)
dev_err(hba->dev, "%s: waiting for DB clear: failed: %d\n",
@@ -9373,10 +9400,6 @@ static void ufshcd_clk_scaling_resume_work(struct work_struct *work)
clk_scaling.resume_work);
unsigned long irq_flags;
- /* Let's not resume scaling if shutdown is ongoing */
- if (ufshcd_is_shutdown_ongoing(hba))
- return;
-
spin_lock_irqsave(hba->host->host_lock, irq_flags);
if (!hba->clk_scaling.is_suspended) {
spin_unlock_irqrestore(hba->host->host_lock, irq_flags);