BACKPORT: FROMGIT: scsi: ufs: Protect PM ops and err_handler from user access through sysfs

User layer may access sysfs nodes when system PM ops or error handling is
running. This can cause various problems. Rename eh_sem to host_sem and use
it to protect PM ops and error handling from user layer intervention.

Link: https://lore.kernel.org/r/1610594010-7254-3-git-send-email-cang@codeaurora.org
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Acked-by: Avri Altman <avri.altman@wdc.com>
Signed-off-by: Can Guo <cang@codeaurora.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

Bug: 189457922
(cherry picked from commit 9cd20d3f47
git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git 5.14/scsi-queue)
[Can Guo: Resolved minor conflict in ufshcd.c]
Change-Id: I705627ce4c78849f8c21f96cb98e8f66a562c183
Signed-off-by: Can Guo <cang@codeaurora.org>
This commit is contained in:
Can Guo 2021-01-13 19:13:28 -08:00 committed by Todd Kjos
commit 3a3b24ef29
2 changed files with 35 additions and 17 deletions

View file

@ -1517,11 +1517,17 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev,
{
struct ufs_hba *hba = dev_get_drvdata(dev);
u32 value;
int err;
int err = 0;
if (kstrtou32(buf, 0, &value))
return -EINVAL;
down(&hba->host_sem);
if (!ufshcd_is_user_access_allowed(hba)) {
err = -EBUSY;
goto out;
}
value = !!value;
if (value == hba->clk_scaling.is_allowed)
goto out;
@ -1547,7 +1553,8 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev,
ufshcd_release(hba);
pm_runtime_put_sync(hba->dev);
out:
return count;
up(&hba->host_sem);
return err ? err : count;
}
static void ufshcd_clkscaling_init_sysfs(struct ufs_hba *hba)
@ -5758,9 +5765,10 @@ static void ufshcd_err_handling_unprepare(struct ufs_hba *hba)
static inline bool ufshcd_err_handling_should_stop(struct ufs_hba *hba)
{
return (!hba->is_powered || hba->ufshcd_state == UFSHCD_STATE_ERROR ||
return (!hba->is_powered || hba->shutting_down ||
hba->ufshcd_state == UFSHCD_STATE_ERROR ||
(!(hba->saved_err || hba->saved_uic_err || hba->force_reset ||
ufshcd_is_link_broken(hba))));
ufshcd_is_link_broken(hba))));
}
#ifdef CONFIG_PM
@ -5830,13 +5838,13 @@ static void ufshcd_err_handler(struct work_struct *work)
hba = container_of(work, struct ufs_hba, eh_work);
down(&hba->eh_sem);
down(&hba->host_sem);
spin_lock_irqsave(hba->host->host_lock, flags);
if (ufshcd_err_handling_should_stop(hba)) {
if (hba->ufshcd_state != UFSHCD_STATE_ERROR)
hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
spin_unlock_irqrestore(hba->host->host_lock, flags);
up(&hba->eh_sem);
up(&hba->host_sem);
return;
}
ufshcd_set_eh_in_progress(hba);
@ -6005,7 +6013,7 @@ skip_err_handling:
spin_unlock_irqrestore(hba->host->host_lock, flags);
ufshcd_scsi_unblock_requests(hba);
ufshcd_err_handling_unprepare(hba);
up(&hba->eh_sem);
up(&hba->host_sem);
if (!err && needs_reset)
ufshcd_clear_ua_wluns(hba);
@ -7897,10 +7905,10 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
struct ufs_hba *hba = (struct ufs_hba *)data;
int ret;
down(&hba->eh_sem);
down(&hba->host_sem);
/* Initialize hba, detect and initialize UFS device */
ret = ufshcd_probe_hba(hba, true);
up(&hba->eh_sem);
up(&hba->host_sem);
if (ret)
goto out;
@ -8914,7 +8922,7 @@ int ufshcd_system_suspend(struct ufs_hba *hba)
return 0;
}
down(&hba->eh_sem);
down(&hba->host_sem);
if (!hba->is_powered)
return 0;
@ -8951,7 +8959,7 @@ out:
if (!ret)
hba->is_sys_suspended = true;
else
up(&hba->eh_sem);
up(&hba->host_sem);
return ret;
}
EXPORT_SYMBOL(ufshcd_system_suspend);
@ -8973,7 +8981,7 @@ int ufshcd_system_resume(struct ufs_hba *hba)
if (unlikely(early_suspend)) {
early_suspend = false;
down(&hba->eh_sem);
down(&hba->host_sem);
}
if (!hba->is_powered || pm_runtime_suspended(hba->dev))
@ -8990,7 +8998,7 @@ out:
hba->curr_dev_pwr_mode, hba->uic_link_state);
if (!ret)
hba->is_sys_suspended = false;
up(&hba->eh_sem);
up(&hba->host_sem);
return ret;
}
EXPORT_SYMBOL(ufshcd_system_resume);
@ -9082,7 +9090,10 @@ int ufshcd_shutdown(struct ufs_hba *hba)
{
int ret = 0;
down(&hba->eh_sem);
down(&hba->host_sem);
hba->shutting_down = true;
up(&hba->host_sem);
if (!hba->is_powered)
goto out;
@ -9096,7 +9107,6 @@ out:
if (ret)
dev_err(hba->dev, "%s failed, err %d\n", __func__, ret);
hba->is_powered = false;
up(&hba->eh_sem);
/* allow force shutdown even in case of errors */
return 0;
}
@ -9292,7 +9302,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
INIT_WORK(&hba->eh_work, ufshcd_err_handler);
INIT_WORK(&hba->eeh_work, ufshcd_exception_event_handler);
sema_init(&hba->eh_sem, 1);
sema_init(&hba->host_sem, 1);
/* Initialize UIC command mutex */
mutex_init(&hba->uic_cmd_mutex);

View file

@ -675,6 +675,8 @@ struct ufs_hba_variant_params {
* @intr_mask: Interrupt Mask Bits
* @ee_ctrl_mask: Exception event control mask
* @is_powered: flag to check if HBA is powered
* @shutting_down: flag to check if shutdown has been invoked
* @host_sem: semaphore used to serialize concurrent contexts
* @eh_wq: Workqueue that eh_work works on
* @eh_work: Worker to handle UFS errors that require s/w attention
* @eeh_work: Worker to handle exception events
@ -772,7 +774,8 @@ struct ufs_hba {
u32 intr_mask;
u16 ee_ctrl_mask;
bool is_powered;
struct semaphore eh_sem;
bool shutting_down;
struct semaphore host_sem;
/* Work Queues */
struct workqueue_struct *eh_wq;
@ -899,6 +902,11 @@ static inline bool ufshcd_is_wb_allowed(struct ufs_hba *hba)
return hba->caps & UFSHCD_CAP_WB_EN;
}
static inline bool ufshcd_is_user_access_allowed(struct ufs_hba *hba)
{
return !hba->shutting_down;
}
#define ufshcd_writel(hba, val, reg) \
writel((val), (hba)->mmio_base + (reg))
#define ufshcd_readl(hba, reg) \