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:
parent
47149e58f4
commit
3a3b24ef29
2 changed files with 35 additions and 17 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue