[PATCH v11 21/24] scsi: ufs: mediatek: Back up idle timer in per-instance struct
sashiko-bot at kernel.org
sashiko-bot at kernel.org
Tue Sep 1 09:33:03 PDT 2026
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] A failed power mode change will permanently disable the auto-hibern8 timer due to brittle backup/restore logic.
- [High] Concurrent sysfs write to `auto_hibern8` races with power mode changes, circumventing hardware constraints and losing user configuration.
--
commit dec217a616e37a6d6c6074f353ace1fc743596e2
Author: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
scsi: ufs: mediatek: Back up idle timer in per-instance struct
The MediaTek UFS driver uses a function-scope static variable to back up
a hardware register across a power change in the
ufs_mtk_pwr_change_notify function. This is dangerous, as it's only
correct if only ever one instance of the driver is loaded, which isn't
true if there's more than one device on a SoC that needs it, or it
otherwise gets loaded a second time.
> diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
> index f405f511f41fd..4782fdf54866b 100644
> --- a/drivers/ufs/host/ufs-mediatek.c
> +++ b/drivers/ufs/host/ufs-mediatek.c
> @@ -1441,27 +1441,24 @@ static int ufs_mtk_pwr_change_notify(struct ufs_hba *hba,
> enum ufs_notify_change_status stage,
> struct ufs_pa_layer_attr *dev_req_params)
> {
> - int ret = 0;
> - static u32 reg;
> + struct ufs_mtk_host *host = ufshcd_get_variant(hba);
>
> switch (stage) {
> case PRE_CHANGE:
> if (ufshcd_is_auto_hibern8_supported(hba)) {
> - reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER);
> + host->ahit = ufshcd_readl(
> + hba, REG_AUTO_HIBERNATE_IDLE_TIMER);
> ufs_mtk_auto_hibern8_disable(hba);
> }
[Severity: High]
This isn't a bug introduced by this patch, but does this brittle caching logic
risk permanently disabling the auto-hibernate feature?
In ufshcd_change_power_mode(), PRE_CHANGE is called first, which reads the
hardware timer value and disables it. If the subsequent
ufshcd_dme_change_power_mode() fails (e.g., due to a UIC command timeout),
POST_CHANGE is bypassed.
This leaves the hardware timer physically disabled (0).
On the next power mode change attempt, PRE_CHANGE reads the now-disabled
value (0) and overwrites host->ahit with 0. When POST_CHANGE eventually
succeeds, it restores 0, permanently disabling the feature until a reboot.
Could we instead restore from the core driver's hba->ahit, which reliably
tracks the configured value, rather than caching the physical hardware state
here?
> - ret = ufs_mtk_pre_pwr_change(hba, dev_req_params);
> - break;
> + return ufs_mtk_pre_pwr_change(hba, dev_req_params);
> case POST_CHANGE:
> if (ufshcd_is_auto_hibern8_supported(hba))
> - ufshcd_writel(hba, reg, REG_AUTO_HIBERNATE_IDLE_TIMER);
> - break;
> - default:
> - ret = -EINVAL;
> - break;
> + ufshcd_writel(hba, host->ahit,
> + REG_AUTO_HIBERNATE_IDLE_TIMER);
[Severity: High]
This is a pre-existing issue, but does blindly restoring this cached value risk
losing user configuration and violating hardware constraints during a race
with sysfs?
When ufshcd_devfreq_scale() changes power modes, it holds clk_scaling_lock and
scan_mutex, but does not acquire hba->host_sem. Concurrently, a sysfs write to
the auto_hibern8 node invokes auto_hibern8_store(), which updates the hardware
register under hba->host_sem but without holding clk_scaling_lock.
If a sysfs write occurs immediately after PRE_CHANGE disabled the
auto-hibernate timer, the timer is re-enabled during
ufshcd_dme_change_power_mode(), violating the constraint this function is
trying to enforce.
Furthermore, when POST_CHANGE executes, it overwrites the register with the
stale host->ahit value, completely undoing the user's sysfs configuration.
> + return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260901-mt8196-ufs-v11-0-dbd007704490@collabora.com?part=21
More information about the linux-phy
mailing list