scsi: ufs: core: Enable power management for wlun

During runtime-suspend of ufs host, the SCSI devices are already suspended
and so are the queues associated with them. However, the ufs host sends SSU
(START_STOP_UNIT) to the wlun during runtime-suspend.

During the process blk_queue_enter() checks if the queue is not in suspended
state. If so, it waits for the queue to resume, and never comes out of
it. Commit 52abca64fd ("scsi: block: Do not accept any requests while
suspended") adds the check to see if the queue is in suspended state in
blk_queue_enter().

Call trace:
 __switch_to+0x174/0x2c4
 __schedule+0x478/0x764
 schedule+0x9c/0xe0
 blk_queue_enter+0x158/0x228
 blk_mq_alloc_request+0x40/0xa4
 blk_get_request+0x2c/0x70
 __scsi_execute+0x60/0x1c4
 ufshcd_set_dev_pwr_mode+0x124/0x1e4
 ufshcd_suspend+0x208/0x83c
 ufshcd_runtime_suspend+0x40/0x154
 ufshcd_pltfrm_runtime_suspend+0x14/0x20
 pm_generic_runtime_suspend+0x28/0x3c
 __rpm_callback+0x80/0x2a4
 rpm_suspend+0x308/0x614
 rpm_idle+0x158/0x228
 pm_runtime_work+0x84/0xac
 process_one_work+0x1f0/0x470
 worker_thread+0x26c/0x4c8
 kthread+0x13c/0x320
 ret_from_fork+0x10/0x18

Fix this by registering ufs device wlun as a SCSI driver and registering it
for block runtime-pm. Also make this a supplier for all other LUNs. This
way the wlun device suspends after all the consumers and resumes after HBA
resumes. This also registers a new SCSI driver for rpmb wlun. This new
driver is mostly used to clear rpmb uac.

[mkp: resolve merge conflict with 5.13-rc1 and fix doc warning]

Fixed smatch warnings:
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

Link: https://lore.kernel.org/r/4662c462e79e3e7f541f54f88f8993f421026d83.1619223249.git.asutoshd@codeaurora.org
Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
Co-developed-by: Can Guo <cang@codeaurora.org>
Signed-off-by: Can Guo <cang@codeaurora.org>
Signed-off-by: Asutosh Das <asutoshd@codeaurora.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Asutosh Das 2021-04-23 17:20:16 -07:00 committed by Martin K. Petersen
parent ed26297d14
commit b294ff3e34
13 changed files with 573 additions and 255 deletions

View File

@ -323,6 +323,8 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
.runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
.runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_resume = ufshcd_pltfrm_runtime_resume,
.runtime_idle = ufshcd_pltfrm_runtime_idle, .runtime_idle = ufshcd_pltfrm_runtime_idle,
.prepare = ufshcd_suspend_prepare,
.complete = ufshcd_resume_complete,
}; };
static struct platform_driver cdns_ufs_pltfrm_driver = { static struct platform_driver cdns_ufs_pltfrm_driver = {

View File

@ -148,6 +148,8 @@ static const struct dev_pm_ops tc_dwc_g210_pci_pm_ops = {
.runtime_suspend = tc_dwc_g210_pci_runtime_suspend, .runtime_suspend = tc_dwc_g210_pci_runtime_suspend,
.runtime_resume = tc_dwc_g210_pci_runtime_resume, .runtime_resume = tc_dwc_g210_pci_runtime_resume,
.runtime_idle = tc_dwc_g210_pci_runtime_idle, .runtime_idle = tc_dwc_g210_pci_runtime_idle,
.prepare = ufshcd_suspend_prepare,
.complete = ufshcd_resume_complete,
}; };
static const struct pci_device_id tc_dwc_g210_pci_tbl[] = { static const struct pci_device_id tc_dwc_g210_pci_tbl[] = {

View File

@ -13,7 +13,7 @@ void __init ufs_debugfs_init(void)
ufs_debugfs_root = debugfs_create_dir("ufshcd", NULL); ufs_debugfs_root = debugfs_create_dir("ufshcd", NULL);
} }
void __exit ufs_debugfs_exit(void) void ufs_debugfs_exit(void)
{ {
debugfs_remove_recursive(ufs_debugfs_root); debugfs_remove_recursive(ufs_debugfs_root);
} }
@ -60,14 +60,14 @@ __acquires(&hba->host_sem)
up(&hba->host_sem); up(&hba->host_sem);
return -EBUSY; return -EBUSY;
} }
pm_runtime_get_sync(hba->dev); ufshcd_rpm_get_sync(hba);
return 0; return 0;
} }
static void ufs_debugfs_put_user_access(struct ufs_hba *hba) static void ufs_debugfs_put_user_access(struct ufs_hba *hba)
__releases(&hba->host_sem) __releases(&hba->host_sem)
{ {
pm_runtime_put_sync(hba->dev); ufshcd_rpm_put_sync(hba);
up(&hba->host_sem); up(&hba->host_sem);
} }

View File

@ -9,7 +9,7 @@ struct ufs_hba;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
void __init ufs_debugfs_init(void); void __init ufs_debugfs_init(void);
void __exit ufs_debugfs_exit(void); void ufs_debugfs_exit(void);
void ufs_debugfs_hba_init(struct ufs_hba *hba); void ufs_debugfs_hba_init(struct ufs_hba *hba);
void ufs_debugfs_hba_exit(struct ufs_hba *hba); void ufs_debugfs_hba_exit(struct ufs_hba *hba);
void ufs_debugfs_exception_event(struct ufs_hba *hba, u16 status); void ufs_debugfs_exception_event(struct ufs_hba *hba, u16 status);

View File

@ -1267,6 +1267,8 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = {
.runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
.runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_resume = ufshcd_pltfrm_runtime_resume,
.runtime_idle = ufshcd_pltfrm_runtime_idle, .runtime_idle = ufshcd_pltfrm_runtime_idle,
.prepare = ufshcd_suspend_prepare,
.complete = ufshcd_resume_complete,
}; };
static struct platform_driver exynos_ufs_pltform = { static struct platform_driver exynos_ufs_pltform = {

View File

@ -574,6 +574,8 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = {
.runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
.runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_resume = ufshcd_pltfrm_runtime_resume,
.runtime_idle = ufshcd_pltfrm_runtime_idle, .runtime_idle = ufshcd_pltfrm_runtime_idle,
.prepare = ufshcd_suspend_prepare,
.complete = ufshcd_resume_complete,
}; };
static struct platform_driver ufs_hisi_pltform = { static struct platform_driver ufs_hisi_pltform = {

View File

@ -810,12 +810,10 @@ static int ufs_mtk_post_link(struct ufs_hba *hba)
/* enable unipro clock gating feature */ /* enable unipro clock gating feature */
ufs_mtk_cfg_unipro_cg(hba, true); ufs_mtk_cfg_unipro_cg(hba, true);
/* configure auto-hibern8 timer to 10ms */ /* will be configured during probe hba */
if (ufshcd_is_auto_hibern8_supported(hba)) { if (ufshcd_is_auto_hibern8_supported(hba))
ufshcd_auto_hibern8_update(hba, hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 10) |
FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 10) | FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3);
FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3));
}
ufs_mtk_setup_clk_gating(hba); ufs_mtk_setup_clk_gating(hba);
@ -1097,6 +1095,8 @@ static const struct dev_pm_ops ufs_mtk_pm_ops = {
.runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
.runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_resume = ufshcd_pltfrm_runtime_resume,
.runtime_idle = ufshcd_pltfrm_runtime_idle, .runtime_idle = ufshcd_pltfrm_runtime_idle,
.prepare = ufshcd_suspend_prepare,
.complete = ufshcd_resume_complete,
}; };
static struct platform_driver ufs_mtk_pltform = { static struct platform_driver ufs_mtk_pltform = {

View File

@ -1551,6 +1551,8 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = {
.runtime_suspend = ufshcd_pltfrm_runtime_suspend, .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
.runtime_resume = ufshcd_pltfrm_runtime_resume, .runtime_resume = ufshcd_pltfrm_runtime_resume,
.runtime_idle = ufshcd_pltfrm_runtime_idle, .runtime_idle = ufshcd_pltfrm_runtime_idle,
.prepare = ufshcd_suspend_prepare,
.complete = ufshcd_resume_complete,
}; };
static struct platform_driver ufs_qcom_pltform = { static struct platform_driver ufs_qcom_pltform = {

View File

@ -97,7 +97,7 @@ static int ufs_bsg_request(struct bsg_job *job)
bsg_reply->reply_payload_rcv_len = 0; bsg_reply->reply_payload_rcv_len = 0;
pm_runtime_get_sync(hba->dev); ufshcd_rpm_get_sync(hba);
msgcode = bsg_request->msgcode; msgcode = bsg_request->msgcode;
switch (msgcode) { switch (msgcode) {
@ -106,7 +106,7 @@ static int ufs_bsg_request(struct bsg_job *job)
ret = ufs_bsg_alloc_desc_buffer(hba, job, &desc_buff, ret = ufs_bsg_alloc_desc_buffer(hba, job, &desc_buff,
&desc_len, desc_op); &desc_len, desc_op);
if (ret) { if (ret) {
pm_runtime_put_sync(hba->dev); ufshcd_rpm_put_sync(hba);
goto out; goto out;
} }
@ -138,7 +138,7 @@ static int ufs_bsg_request(struct bsg_job *job)
break; break;
} }
pm_runtime_put_sync(hba->dev); ufshcd_rpm_put_sync(hba);
if (!desc_buff) if (!desc_buff)
goto out; goto out;

View File

@ -410,29 +410,6 @@ static int ufshcd_pci_resume(struct device *dev)
return ufshcd_system_resume(dev_get_drvdata(dev)); return ufshcd_system_resume(dev_get_drvdata(dev));
} }
/**
* ufshcd_pci_poweroff - suspend-to-disk poweroff function
* @dev: pointer to PCI device handle
*
* Returns 0 if successful
* Returns non-zero otherwise
*/
static int ufshcd_pci_poweroff(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
int spm_lvl = hba->spm_lvl;
int ret;
/*
* For poweroff we need to set the UFS device to PowerDown mode.
* Force spm_lvl to ensure that.
*/
hba->spm_lvl = 5;
ret = ufshcd_system_suspend(hba);
hba->spm_lvl = spm_lvl;
return ret;
}
#endif /* !CONFIG_PM_SLEEP */ #endif /* !CONFIG_PM_SLEEP */
#ifdef CONFIG_PM #ifdef CONFIG_PM
@ -533,17 +510,14 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
} }
static const struct dev_pm_ops ufshcd_pci_pm_ops = { static const struct dev_pm_ops ufshcd_pci_pm_ops = {
#ifdef CONFIG_PM_SLEEP
.suspend = ufshcd_pci_suspend,
.resume = ufshcd_pci_resume,
.freeze = ufshcd_pci_suspend,
.thaw = ufshcd_pci_resume,
.poweroff = ufshcd_pci_poweroff,
.restore = ufshcd_pci_resume,
#endif
SET_RUNTIME_PM_OPS(ufshcd_pci_runtime_suspend, SET_RUNTIME_PM_OPS(ufshcd_pci_runtime_suspend,
ufshcd_pci_runtime_resume, ufshcd_pci_runtime_resume,
ufshcd_pci_runtime_idle) ufshcd_pci_runtime_idle)
SET_SYSTEM_SLEEP_PM_OPS(ufshcd_pci_suspend, ufshcd_pci_resume)
#ifdef CONFIG_PM_SLEEP
.prepare = ufshcd_suspend_prepare,
.complete = ufshcd_resume_complete,
#endif
}; };
static const struct pci_device_id ufshcd_pci_tbl[] = { static const struct pci_device_id ufshcd_pci_tbl[] = {

File diff suppressed because it is too large Load Diff

View File

@ -72,6 +72,8 @@ enum ufs_event_type {
UFS_EVT_LINK_STARTUP_FAIL, UFS_EVT_LINK_STARTUP_FAIL,
UFS_EVT_RESUME_ERR, UFS_EVT_RESUME_ERR,
UFS_EVT_SUSPEND_ERR, UFS_EVT_SUSPEND_ERR,
UFS_EVT_WL_SUSP_ERR,
UFS_EVT_WL_RES_ERR,
/* abnormal events */ /* abnormal events */
UFS_EVT_DEV_RESET, UFS_EVT_DEV_RESET,
@ -807,6 +809,7 @@ struct ufs_hba {
struct list_head clk_list_head; struct list_head clk_list_head;
bool wlun_dev_clr_ua; bool wlun_dev_clr_ua;
bool wlun_rpmb_clr_ua;
/* Number of requests aborts */ /* Number of requests aborts */
int req_abort_count; int req_abort_count;
@ -846,6 +849,9 @@ struct ufs_hba {
struct delayed_work debugfs_ee_work; struct delayed_work debugfs_ee_work;
u32 debugfs_ee_rate_limit_ms; u32 debugfs_ee_rate_limit_ms;
#endif #endif
u32 luns_avail;
bool complete_put;
bool rpmb_complete_put;
}; };
/* Returns true if clocks can be gated. Otherwise false */ /* Returns true if clocks can be gated. Otherwise false */
@ -1105,6 +1111,8 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba,
enum query_opcode desc_op); enum query_opcode desc_op);
int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable); int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable);
int ufshcd_suspend_prepare(struct device *dev);
void ufshcd_resume_complete(struct device *dev);
/* Wrapper functions for safely calling variant operations */ /* Wrapper functions for safely calling variant operations */
static inline const char *ufshcd_get_var_name(struct ufs_hba *hba) static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)
@ -1309,4 +1317,29 @@ static inline int ufshcd_update_ee_usr_mask(struct ufs_hba *hba,
&hba->ee_drv_mask, set, clr); &hba->ee_drv_mask, set, clr);
} }
static inline int ufshcd_rpm_get_sync(struct ufs_hba *hba)
{
return pm_runtime_get_sync(&hba->sdev_ufs_device->sdev_gendev);
}
static inline int ufshcd_rpm_put_sync(struct ufs_hba *hba)
{
return pm_runtime_put_sync(&hba->sdev_ufs_device->sdev_gendev);
}
static inline int ufshcd_rpm_put(struct ufs_hba *hba)
{
return pm_runtime_put(&hba->sdev_ufs_device->sdev_gendev);
}
static inline int ufshcd_rpmb_rpm_get_sync(struct ufs_hba *hba)
{
return pm_runtime_get_sync(&hba->sdev_rpmb->sdev_gendev);
}
static inline int ufshcd_rpmb_rpm_put(struct ufs_hba *hba)
{
return pm_runtime_put(&hba->sdev_rpmb->sdev_gendev);
}
#endif /* End of Header */ #endif /* End of Header */

View File

@ -246,6 +246,26 @@ DEFINE_EVENT(ufshcd_template, ufshcd_init,
int dev_state, int link_state), int dev_state, int link_state),
TP_ARGS(dev_name, err, usecs, dev_state, link_state)); TP_ARGS(dev_name, err, usecs, dev_state, link_state));
DEFINE_EVENT(ufshcd_template, ufshcd_wl_suspend,
TP_PROTO(const char *dev_name, int err, s64 usecs,
int dev_state, int link_state),
TP_ARGS(dev_name, err, usecs, dev_state, link_state));
DEFINE_EVENT(ufshcd_template, ufshcd_wl_resume,
TP_PROTO(const char *dev_name, int err, s64 usecs,
int dev_state, int link_state),
TP_ARGS(dev_name, err, usecs, dev_state, link_state));
DEFINE_EVENT(ufshcd_template, ufshcd_wl_runtime_suspend,
TP_PROTO(const char *dev_name, int err, s64 usecs,
int dev_state, int link_state),
TP_ARGS(dev_name, err, usecs, dev_state, link_state));
DEFINE_EVENT(ufshcd_template, ufshcd_wl_runtime_resume,
TP_PROTO(const char *dev_name, int err, s64 usecs,
int dev_state, int link_state),
TP_ARGS(dev_name, err, usecs, dev_state, link_state));
TRACE_EVENT(ufshcd_command, TRACE_EVENT(ufshcd_command,
TP_PROTO(const char *dev_name, enum ufs_trace_str_t str_t, TP_PROTO(const char *dev_name, enum ufs_trace_str_t str_t,
unsigned int tag, u32 doorbell, int transfer_len, u32 intr, unsigned int tag, u32 doorbell, int transfer_len, u32 intr,