From c128400dc563bc5514ed3773730fc282197e08eb Mon Sep 17 00:00:00 2001 From: Venkat Gopalakrishnan Date: Mon, 23 Jan 2017 15:00:09 -0800 Subject: [PATCH 1/2] scsi: ufs: fix ufshcd_hold deadlock If ufs_qcom_testbus_config is called as part of dumping registers inside ufshcd_ungate_work then a ufshcd_hold in this function will deadlock triggering another ungate work and waiting for it to finish. Fix this by making sure the caller already holds the needed locks for clocks and runtime status. Change-Id: I8f4c10d952c8f74c93b991088f5ee1eaf719ca84 Signed-off-by: Venkat Gopalakrishnan --- drivers/scsi/ufs/ufs-qcom-debugfs.c | 13 ++++++++++++- drivers/scsi/ufs/ufs-qcom.c | 9 +++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/ufs/ufs-qcom-debugfs.c b/drivers/scsi/ufs/ufs-qcom-debugfs.c index 8532439c392d..4547a6dbdb23 100644 --- a/drivers/scsi/ufs/ufs-qcom-debugfs.c +++ b/drivers/scsi/ufs/ufs-qcom-debugfs.c @@ -67,6 +67,7 @@ static int ufs_qcom_dbg_testbus_en_read(void *data, u64 *attr_val) static int ufs_qcom_dbg_testbus_en_set(void *data, u64 attr_id) { struct ufs_qcom_host *host = data; + int ret = 0; if (!host) return -EINVAL; @@ -76,7 +77,13 @@ static int ufs_qcom_dbg_testbus_en_set(void *data, u64 attr_id) else host->dbg_print_en &= ~UFS_QCOM_DBG_PRINT_TEST_BUS_EN; - return ufs_qcom_testbus_config(host); + pm_runtime_get_sync(host->hba->dev); + ufshcd_hold(host->hba, false); + ret = ufs_qcom_testbus_config(host); + ufshcd_release(host->hba, false); + pm_runtime_put_sync(host->hba->dev); + + return ret; } DEFINE_SIMPLE_ATTRIBUTE(ufs_qcom_dbg_testbus_en_ops, @@ -142,7 +149,11 @@ static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file, * Sanity check of the {major, minor} tuple is done in the * config function */ + pm_runtime_get_sync(host->hba->dev); + ufshcd_hold(host->hba, false); ret = ufs_qcom_testbus_config(host); + ufshcd_release(host->hba, false); + pm_runtime_put_sync(host->hba->dev); if (!ret) dev_dbg(host->hba->dev, "%s: New configuration: major=%d, minor=%d\n", diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 03b222d8be93..7369478a8c5d 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -2452,6 +2452,11 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) return true; } +/* + * The caller of this function must make sure that the controller + * is out of runtime suspend and appropriate clocks are enabled + * before accessing. + */ int ufs_qcom_testbus_config(struct ufs_qcom_host *host) { int reg; @@ -2522,8 +2527,6 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host) } mask <<= offset; - pm_runtime_get_sync(host->hba->dev); - ufshcd_hold(host->hba, false); ufshcd_rmwl(host->hba, TEST_BUS_SEL, (u32)host->testbus.select_major << 19, REG_UFS_CFG1); @@ -2536,8 +2539,6 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host) * committed before returning. */ mb(); - ufshcd_release(host->hba, false); - pm_runtime_put_sync(host->hba->dev); return 0; } From 98d825a34c0892de0f1311b582f4109a4b78e18a Mon Sep 17 00:00:00 2001 From: Venkat Gopalakrishnan Date: Mon, 23 Jan 2017 17:34:52 -0800 Subject: [PATCH 2/2] scsi: ufs: check for err state when polling for doorbell As part of polling for doorbell during clk scaling, we need to make sure the host is in good state before polling. In case error handler is running at the same time, that could reset the clocks as part of recovery causing unclocked register access when polling for doorbell. Change-Id: I715932e9bffd51956d3a24aa2aec66c2c9a4652b Signed-off-by: Venkat Gopalakrishnan --- drivers/scsi/ufs/ufshcd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d4acc3c911f5..73d298da7885 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4141,17 +4141,17 @@ int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us) ufshcd_hold_all(hba); spin_lock_irqsave(hba->host->host_lock, flags); - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { - ret = -EBUSY; - goto out; - } - /* * Wait for all the outstanding tasks/transfer requests. * Verify by checking the doorbell registers are clear. */ start = ktime_get(); do { + if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { + ret = -EBUSY; + goto out; + } + tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL); tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); if (!tm_doorbell && !tr_doorbell) {