mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
scsi: ufs: core: Serialize AHIT register access between sysfs and host driver
A race exists between ufshcd_auto_hibern8_update() and
ufs_mtk_pwr_change_notify() when both paths concurrently access
REG_AUTO_HIBERNATE_IDLE_TIMER. Additionally, the static local variable used
to save/restore AHIT in the power change path is not re-entrant and may
hold a stale value if a sysfs write races with a gear change.
Introduce ahit_mutex and ahit_disable_depth in struct ufs_hba to
serialize AHIT register access. The depth counter tracks how many
host-driver paths have forced AHIT off; sysfs writes are suppressed
while it is non-zero and the register is restored from hba->ahit
only when the last disabler drops it to zero.
Fixes: f5ca8d0c7a ("scsi: ufs: host: mediatek: Disable auto-hibern8 during power mode changes")
Signed-off-by: Peter Wang <peter.wang@mediatek.com>
Reviewed-by: Chun-Hung Wu <chun-hung.wu@mediatek.com>
Link: https://patch.msgid.link/20260914085148.136429-3-peter.wang@mediatek.com
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
This commit is contained in:
parent
6efee14ca0
commit
7dec5cedf7
|
|
@ -4789,7 +4789,17 @@ void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit)
|
|||
if (!pm_runtime_suspended(&hba->ufs_device_wlun->sdev_gendev)) {
|
||||
ufshcd_rpm_get_sync(hba);
|
||||
ufshcd_hold(hba);
|
||||
ufshcd_configure_auto_hibern8(hba);
|
||||
/*
|
||||
* Serialize with the host driver's AHIT control. Skip the
|
||||
* register write while the driver has AHIT forced off; the
|
||||
* driver programs hba->ahit back when it re-enables it.
|
||||
* Mutex is taken here (not across the rpm calls above) so it
|
||||
* never blocks a resume that the driver's path may need.
|
||||
*/
|
||||
mutex_lock(&hba->ahit_mutex);
|
||||
if (!hba->ahit_disable_depth)
|
||||
ufshcd_configure_auto_hibern8(hba);
|
||||
mutex_unlock(&hba->ahit_mutex);
|
||||
ufshcd_release(hba);
|
||||
ufshcd_rpm_put_sync(hba);
|
||||
}
|
||||
|
|
@ -11318,6 +11328,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
|
|||
|
||||
mutex_init(&hba->wb_mutex);
|
||||
|
||||
mutex_init(&hba->ahit_mutex);
|
||||
|
||||
init_rwsem(&hba->clk_scaling_lock);
|
||||
|
||||
ufshcd_init_clk_gating(hba);
|
||||
|
|
|
|||
|
|
@ -1507,19 +1507,27 @@ static int ufs_mtk_pwr_change_notify(struct ufs_hba *hba,
|
|||
struct ufs_pa_layer_attr *dev_req_params)
|
||||
{
|
||||
int ret = 0;
|
||||
static u32 reg;
|
||||
|
||||
switch (stage) {
|
||||
case PRE_CHANGE:
|
||||
if (ufshcd_is_auto_hibern8_supported(hba)) {
|
||||
reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER);
|
||||
/* Block sysfs AHIT writes while we force AHIT off */
|
||||
mutex_lock(&hba->ahit_mutex);
|
||||
hba->ahit_disable_depth++;
|
||||
mutex_unlock(&hba->ahit_mutex);
|
||||
ufs_mtk_auto_hibern8_disable(hba);
|
||||
}
|
||||
ret = ufs_mtk_pre_pwr_change(hba, dev_req_params);
|
||||
break;
|
||||
case POST_CHANGE:
|
||||
if (ufshcd_is_auto_hibern8_supported(hba))
|
||||
ufshcd_writel(hba, reg, REG_AUTO_HIBERNATE_IDLE_TIMER);
|
||||
if (ufshcd_is_auto_hibern8_supported(hba)) {
|
||||
mutex_lock(&hba->ahit_mutex);
|
||||
/* re-enable only when the last disabler drops off */
|
||||
if (!--hba->ahit_disable_depth)
|
||||
ufshcd_writel(hba, hba->ahit,
|
||||
REG_AUTO_HIBERNATE_IDLE_TIMER);
|
||||
mutex_unlock(&hba->ahit_mutex);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
|
|||
|
|
@ -1132,6 +1132,14 @@ struct ufs_hba {
|
|||
|
||||
/* Auto-Hibernate Idle Timer register value */
|
||||
u32 ahit;
|
||||
/* Serializes AHIT register access between sysfs and the host driver */
|
||||
struct mutex ahit_mutex;
|
||||
/*
|
||||
* Nesting count of host-driver paths that have AHIT forced off.
|
||||
* Non-zero blocks sysfs AHIT writes; AHIT is reprogrammed only when
|
||||
* the last disabler drops it back to zero. Guarded by ahit_mutex.
|
||||
*/
|
||||
int ahit_disable_depth;
|
||||
|
||||
unsigned long outstanding_tasks;
|
||||
spinlock_t outstanding_lock;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user