diff options
| author | Sayali Lokhande <sayalil@codeaurora.org> | 2017-07-06 12:03:10 +0530 |
|---|---|---|
| committer | Sayali Lokhande <sayalil@codeaurora.org> | 2017-07-06 16:14:44 +0530 |
| commit | 01d3b1fce14c8768892d45308679ad73f288773d (patch) | |
| tree | 3ad8a0421cca965603be8f94edab9cd103af2fe8 | |
| parent | 0f6cf457b2bdcd335b710a5c5f27f234da076696 (diff) | |
scsi: ufs: Serialise ufs clock gating and ungating
On ufs based targets we observed several unclocked register access
issues due to race conditions between clock gating and ungating.
Sequence of events causing race looks like:
[1]ungate_work_scheduled = (tv64 = 27288.795244106),REQ_CLKS_ON
[2]gating_work_execute_end = (tv64 = 27288.795948169),REQ_CLKS_ON
[3]last intr status = (tv64 = 27288.801560512)
[4]ungate_work_execute_start=(tv64 = 27288.801725304),REQ_CLKS_ON
[5]clk_rel ctx=XFR_REQ_COMPL)(tv64 = 27288.801950460),REQ_CLKS_OFF
[6]gating_work_scheduled = (tv64 = 27288.812817231),REQ_CLKS_OFF
[7]gating_work_execute_start=(tv64 = 27288.813704106),REQ_CLKS_OFF
[8]clk_hold ctx = QUEUE_CMD) (tv64 = 27288.821010200)
[9](WRITE)issue_time_stamp = (tv64 = 27288.821059366),gating in progress!
[10]ungate_work_execute_end =(tv64 = 27288.821251866),CLKS_ON
[11]Gladiator Error Detected = 27288.830788
Here clock gating work[6] is scheduled while ungating[4] is in progress
thus causing unclocked register access when request is issued[9].
This change is to avoid such race condition by using single
threaded workqueue for both gate and ungate work.
Change-Id: I710ff0dbe59df0c1eb903b18555b6184cb298fa3
Signed-off-by: Sayali Lokhande <sayalil@codeaurora.org>
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 14 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 2 |
2 files changed, 9 insertions, 7 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index a2f2cc0c2c51..c23023f43d30 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1483,7 +1483,7 @@ start: hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); - queue_work(hba->clk_gating.ungating_workq, + queue_work(hba->clk_gating.clk_gating_workq, &hba->clk_gating.ungate_work); /* * fall through to check if we should wait for this @@ -1751,7 +1751,8 @@ static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler( struct ufs_hba *hba = container_of(timer, struct ufs_hba, clk_gating.gate_hrtimer); - schedule_work(&hba->clk_gating.gate_work); + queue_work(hba->clk_gating.clk_gating_workq, + &hba->clk_gating.gate_work); return HRTIMER_NORESTART; } @@ -1759,7 +1760,7 @@ static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler( static void ufshcd_init_clk_gating(struct ufs_hba *hba) { struct ufs_clk_gating *gating = &hba->clk_gating; - char wq_name[sizeof("ufs_clk_ungating_00")]; + char wq_name[sizeof("ufs_clk_gating_00")]; hba->clk_gating.state = CLKS_ON; @@ -1788,9 +1789,10 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) hrtimer_init(&gating->gate_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); gating->gate_hrtimer.function = ufshcd_clkgate_hrtimer_handler; - snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_ungating_%d", + snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_gating_%d", hba->host->host_no); - hba->clk_gating.ungating_workq = create_singlethread_workqueue(wq_name); + hba->clk_gating.clk_gating_workq = + create_singlethread_workqueue(wq_name); gating->is_enabled = true; @@ -1854,7 +1856,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) device_remove_file(hba->dev, &hba->clk_gating.enable_attr); ufshcd_cancel_gate_work(hba); cancel_work_sync(&hba->clk_gating.ungate_work); - destroy_workqueue(hba->clk_gating.ungating_workq); + destroy_workqueue(hba->clk_gating.clk_gating_workq); } static void ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba, u32 delay) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index d66205ff9f5d..da3ad78d3405 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -431,7 +431,7 @@ struct ufs_clk_gating { struct device_attribute enable_attr; bool is_enabled; int active_reqs; - struct workqueue_struct *ungating_workq; + struct workqueue_struct *clk_gating_workq; }; /* Hibern8 state */ |
