Commit 6fd53da4 authored by Peter Wang's avatar Peter Wang Committed by Martin K. Petersen
Browse files

scsi: ufs: core: Fix abnormal scale up after last cmd finish



When ufshcd_clk_scaling_suspend_work (thread A) running and new command
coming, ufshcd_clk_scaling_start_busy (thread B) may get host_lock after
thread A first time release host_lock. Then thread A second time get
host_lock will set clk_scaling.window_start_t = 0 which scale up clock
abnormal next polling_ms time.  Also inlines another
__ufshcd_suspend_clkscaling calls.

Below is racing step:
1	hba->clk_scaling.suspend_work (Thread A)
	ufshcd_clk_scaling_suspend_work
2		spin_lock_irqsave(hba->host->host_lock, irq_flags);
3		hba->clk_scaling.is_suspended = true;
4		spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
		__ufshcd_suspend_clkscaling
7			spin_lock_irqsave(hba->host->host_lock, flags);
8			hba->clk_scaling.window_start_t = 0;
9			spin_unlock_irqrestore(hba->host->host_lock, flags);

	ufshcd_send_command (Thread B)
		ufshcd_clk_scaling_start_busy
5			spin_lock_irqsave(hba->host->host_lock, flags);
			....
6			spin_unlock_irqrestore(hba->host->host_lock, flags);

Signed-off-by: default avatarPeter Wang <peter.wang@mediatek.com>
Link: https://lore.kernel.org/r/20230831130826.5592-3-peter.wang@mediatek.com


Reviewed-by: default avatarBart Van Assche <bvanassche@acm.org>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 1d969731
Loading
Loading
Loading
Loading
+4 −13
Original line number Diff line number Diff line
@@ -274,7 +274,6 @@ static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
static void ufshcd_resume_clkscaling(struct ufs_hba *hba);
static void ufshcd_suspend_clkscaling(struct ufs_hba *hba);
static void __ufshcd_suspend_clkscaling(struct ufs_hba *hba);
static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up);
static irqreturn_t ufshcd_intr(int irq, void *__hba);
static int ufshcd_change_power_mode(struct ufs_hba *hba,
@@ -1357,9 +1356,10 @@ static void ufshcd_clk_scaling_suspend_work(struct work_struct *work)
		return;
	}
	hba->clk_scaling.is_suspended = true;
	hba->clk_scaling.window_start_t = 0;
	spin_unlock_irqrestore(hba->host->host_lock, irq_flags);

	__ufshcd_suspend_clkscaling(hba);
	devfreq_suspend_device(hba->devfreq);
}

static void ufshcd_clk_scaling_resume_work(struct work_struct *work)
@@ -1536,16 +1536,6 @@ static void ufshcd_devfreq_remove(struct ufs_hba *hba)
	dev_pm_opp_remove(hba->dev, clki->max_freq);
}

static void __ufshcd_suspend_clkscaling(struct ufs_hba *hba)
{
	unsigned long flags;

	devfreq_suspend_device(hba->devfreq);
	spin_lock_irqsave(hba->host->host_lock, flags);
	hba->clk_scaling.window_start_t = 0;
	spin_unlock_irqrestore(hba->host->host_lock, flags);
}

static void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
{
	unsigned long flags;
@@ -1558,11 +1548,12 @@ static void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
	if (!hba->clk_scaling.is_suspended) {
		suspend = true;
		hba->clk_scaling.is_suspended = true;
		hba->clk_scaling.window_start_t = 0;
	}
	spin_unlock_irqrestore(hba->host->host_lock, flags);

	if (suspend)
		__ufshcd_suspend_clkscaling(hba);
		devfreq_suspend_device(hba->devfreq);
}

static void ufshcd_resume_clkscaling(struct ufs_hba *hba)