diff options
| author | Subhash Jadavani <subhashj@codeaurora.org> | 2016-01-18 13:10:32 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 21:25:42 -0700 |
| commit | 90e939642217543245ae5016621d10f4ddd1e948 (patch) | |
| tree | f616ddacc171c3fb9f657fd79aa0be5ca1894dca | |
| parent | fb86dfdbb0a2b7f98e3ac3055d30734ac8140772 (diff) | |
scsi: ufs: fix deadlock between clock scaling and clock ungating work
There's a deadlock between clock scaling and clock ungating work if hibern8
exit fails while ungating clocks.
ufshcd_exec_dev_cmd() in clock ungating work blocks at taking
"clk_scaling_lock" unless clock scaling work completes which had taken
"clock_scaling_lock" and it will never be released hence this is a
deadlock.
Clock ungating context:
----------------------
-000|__switch_to()
-001|context_switch(inline)
-001|__schedule()
-002|schedule()
-003|rwsem_down_read_failed()
-004|down_read()
-005|ufshcd_get_dev_cmd_tag(inline)
-005|ufshcd_exec_dev_cmd()
-006|ufshcd_verify_dev_init()
-007|ufshcd_probe_hba()
-008|ufshcd_host_reset_and_restore()
-009|ufshcd_link_recovery()
-010|ufshcd_uic_hibern8_exit()
-011|ufshcd_ungate_work()
-012|static_key_count(inline)
-012|static_key_false(inline)
-012|trace_workqueue_execute_end(inline)
-012|process_one_work()
-013|process_scheduled_works(inline)
-013|worker_thread()
-014|kthread()
-015|ret_from_fork(asm)
-->|exception
-016|NSX:0xF0440E59300(asm)
---|end of frame
Clock scaling context:
----------------------
-000|__switch_to()
-001|context_switch(inline)
-001|__schedule()
-002|schedule()
-003|schedule_timeout()
-004|do_wait_for_common(inline)
-004|__wait_for_common(inline)
-004|wait_for_common()
-005|wait_for_completion()
-006|flush_work()
-007|ufshcd_hold()
-008|ufshcd_hold_all()
-009|ufshcd_wait_for_doorbell_clr()
-010|ufshcd_clock_scaling_prepare(inline)
-010|ufshcd_devfreq_scale()
-011|ufshcd_devfreq_target()
-012|update_devfreq()
-013|devfreq_monitor()
-014|static_key_count(inline)
-014|static_key_false(inline)
-014|trace_workqueue_execute_end(inline)
-014|process_one_work()
-015|worker_thread()
-016|kthread()
-017|ret_from_fork(asm)
-->|exception
-018|NSR:0x2A714C(asm)
---|end of frame
This change is fixing this by moving ufshcd_hold_all() in
ufshcd_devfreq_scale() to the beginning of the function so that
clk_scaling_lock is acquired only after clock ungating completes.
CRs-Fixed: 963407
Change-Id: I83788c3212baeab31cf1bf877ca0aaf9005ca661
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 16 |
1 files changed, 9 insertions, 7 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 740509c34b0a..b2a16e465246 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -8589,17 +8589,18 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up) { int ret = 0; + /* let's not get into low power until clock scaling is completed */ + ufshcd_hold_all(hba); + ret = ufshcd_clock_scaling_prepare(hba); if (ret) - return ret; + goto out; - /* let's not get into low power until clock scaling is completed */ - ufshcd_hold_all(hba); /* scale down the gear before scaling down clocks */ if (!scale_up) { ret = ufshcd_scale_gear(hba, false); if (ret) - goto out; + goto clk_scaling_unprepare; } ret = ufshcd_scale_clks(hba, scale_up); @@ -8611,7 +8612,7 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up) ret = ufshcd_scale_gear(hba, true); if (ret) { ufshcd_scale_clks(hba, false); - goto out; + goto clk_scaling_unprepare; } } @@ -8625,13 +8626,14 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up) hba->clk_gating.delay_ms_pwr_save; } - goto out; + goto clk_scaling_unprepare; scale_up_gear: if (!scale_up) ufshcd_scale_gear(hba, true); -out: +clk_scaling_unprepare: ufshcd_clock_scaling_unprepare(hba); +out: ufshcd_release_all(hba); return ret; } |
