| From 7852b0488a00c29076878e13add37c1c4302fd56 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 11 Jun 2020 18:10:43 +0800 |
| Subject: scsi: ufs: Fix imprecise load calculation in devfreq window |
| |
| From: Stanley Chu <stanley.chu@mediatek.com> |
| |
| [ Upstream commit b1bf66d1d5a8fcb54f0e584db5d196ef015b5172 ] |
| |
| The UFS load calculation is based on "total_time" and "busy_time" in a |
| devfreq window. However, the source of time is different for both |
| parameters: "busy_time" is assigned from "jiffies" thus has different |
| accuracy from "total_time" which is assigned from ktime_get(). |
| |
| In addition, the time of window boundary is not exactly the same as the |
| starting busy time in this window if UFS is actually busy in the beginning |
| of the window. A similar accuracy error may also happen for the end of busy |
| time in current window. |
| |
| To guarantee the precision of load calculation, we need to |
| |
| 1. Align time accuracy of both devfreq_dev_status.total_time and |
| devfreq_dev_status.busy_time. For example, use "ktime_get()" directly. |
| |
| 2. Align the following timelines: |
| - The beginning time of devfreq windows |
| - The beginning of busy time in a new window |
| - The end of busy time in the current window |
| |
| Link: https://lore.kernel.org/r/20200611101043.6379-1-stanley.chu@mediatek.com |
| Fixes: a3cd5ec55f6c ("scsi: ufs: add load based scaling of UFS gear") |
| Reviewed-by: Avri Altman <avri.altman@wdc.com> |
| Signed-off-by: Stanley Chu <stanley.chu@mediatek.com> |
| Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/scsi/ufs/ufshcd.c | 18 ++++++++++-------- |
| drivers/scsi/ufs/ufshcd.h | 2 +- |
| 2 files changed, 11 insertions(+), 9 deletions(-) |
| |
| diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c |
| index 7ca32ede5e172..477b6cfff381b 100644 |
| --- a/drivers/scsi/ufs/ufshcd.c |
| +++ b/drivers/scsi/ufs/ufshcd.c |
| @@ -1280,6 +1280,7 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev, |
| unsigned long flags; |
| struct list_head *clk_list = &hba->clk_list_head; |
| struct ufs_clk_info *clki; |
| + ktime_t curr_t; |
| |
| if (!ufshcd_is_clkscaling_supported(hba)) |
| return -EINVAL; |
| @@ -1287,6 +1288,7 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev, |
| memset(stat, 0, sizeof(*stat)); |
| |
| spin_lock_irqsave(hba->host->host_lock, flags); |
| + curr_t = ktime_get(); |
| if (!scaling->window_start_t) |
| goto start_window; |
| |
| @@ -1298,18 +1300,17 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev, |
| */ |
| stat->current_frequency = clki->curr_freq; |
| if (scaling->is_busy_started) |
| - scaling->tot_busy_t += ktime_to_us(ktime_sub(ktime_get(), |
| - scaling->busy_start_t)); |
| + scaling->tot_busy_t += ktime_us_delta(curr_t, |
| + scaling->busy_start_t); |
| |
| - stat->total_time = jiffies_to_usecs((long)jiffies - |
| - (long)scaling->window_start_t); |
| + stat->total_time = ktime_us_delta(curr_t, scaling->window_start_t); |
| stat->busy_time = scaling->tot_busy_t; |
| start_window: |
| - scaling->window_start_t = jiffies; |
| + scaling->window_start_t = curr_t; |
| scaling->tot_busy_t = 0; |
| |
| if (hba->outstanding_reqs) { |
| - scaling->busy_start_t = ktime_get(); |
| + scaling->busy_start_t = curr_t; |
| scaling->is_busy_started = true; |
| } else { |
| scaling->busy_start_t = 0; |
| @@ -1860,6 +1861,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) |
| static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba) |
| { |
| bool queue_resume_work = false; |
| + ktime_t curr_t = ktime_get(); |
| |
| if (!ufshcd_is_clkscaling_supported(hba)) |
| return; |
| @@ -1875,13 +1877,13 @@ static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba) |
| &hba->clk_scaling.resume_work); |
| |
| if (!hba->clk_scaling.window_start_t) { |
| - hba->clk_scaling.window_start_t = jiffies; |
| + hba->clk_scaling.window_start_t = curr_t; |
| hba->clk_scaling.tot_busy_t = 0; |
| hba->clk_scaling.is_busy_started = false; |
| } |
| |
| if (!hba->clk_scaling.is_busy_started) { |
| - hba->clk_scaling.busy_start_t = ktime_get(); |
| + hba->clk_scaling.busy_start_t = curr_t; |
| hba->clk_scaling.is_busy_started = true; |
| } |
| } |
| diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h |
| index 6ffc08ad85f63..2315ecc209272 100644 |
| --- a/drivers/scsi/ufs/ufshcd.h |
| +++ b/drivers/scsi/ufs/ufshcd.h |
| @@ -409,7 +409,7 @@ struct ufs_saved_pwr_info { |
| struct ufs_clk_scaling { |
| int active_reqs; |
| unsigned long tot_busy_t; |
| - unsigned long window_start_t; |
| + ktime_t window_start_t; |
| ktime_t busy_start_t; |
| struct device_attribute enable_attr; |
| struct ufs_saved_pwr_info saved_pwr_info; |
| -- |
| 2.25.1 |
| |