From patchwork Mon Nov 13 09:14:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Potomski, MichalX" X-Patchwork-Id: 10055301 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 2C4EB60365 for ; Mon, 13 Nov 2017 09:12:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2995C29180 for ; Mon, 13 Nov 2017 09:12:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1D5D729197; Mon, 13 Nov 2017 09:12:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 664F729180 for ; Mon, 13 Nov 2017 09:12:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752265AbdKMJMN (ORCPT ); Mon, 13 Nov 2017 04:12:13 -0500 Received: from mga14.intel.com ([192.55.52.115]:65082 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752196AbdKMJML (ORCPT ); Mon, 13 Nov 2017 04:12:11 -0500 Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 13 Nov 2017 01:12:10 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.44,388,1505804400"; d="scan'208";a="1315385" Received: from mpotom-tieto.ir.intel.com ([10.103.116.128]) by FMSMGA003.fm.intel.com with ESMTP; 13 Nov 2017 01:12:08 -0800 From: Michal Potomski To: linux-scsi@vger.kernel.org Cc: vinholikatti@gmail.com, martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, subhashj@codeaurora.org, szymonx.mielczarek@intel.com Subject: [PATCH] scsi: ufs: Fix Runtime PM Date: Mon, 13 Nov 2017 10:14:26 +0100 Message-Id: <20171113091426.15573-1-michalx.potomski@intel.com> X-Mailer: git-send-email 2.13.0.67.g10c78a1 MIME-Version: 1.0 Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Michał Potomski Recent testing of Runtime PM for UFS has shown it's not working as intended. To acheive fully working Runtime PM, first we have to put back scsi_device autopm reference counter. Existing implementation was prone to races and not working for tranfsers to sleeping devices. This commit aims to fix this to acheive fully working environment. Due to runtime PM being previously disabled by not putting scsi_device autopm counter, the Runtime PM is set to be forbidden as default, to not cause problems with hosts and devices, which do not fully support different Device and Link states. It can be still enabled through sysFS power/control attribute. Changes since v1: - Fix compilation error for certain kernel configs, - Move pm_mark_last_busy(), only to relevant context. Signed-off-by: Michał Potomski --- drivers/scsi/ufs/ufshcd-pci.c | 4 ++- drivers/scsi/ufs/ufshcd.c | 64 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 925b0ec7ec54..4fffb1123876 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -184,8 +184,10 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) } pci_set_drvdata(pdev, hba); + pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); - pm_runtime_allow(&pdev->dev); + pm_runtime_forbid(&pdev->dev); return 0; } diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 011c3369082c..7ff7f51afe26 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -240,6 +240,37 @@ static inline bool ufshcd_valid_tag(struct ufs_hba *hba, int tag) return tag >= 0 && tag < hba->nutrs; } +#ifdef CONFIG_PM +static void ufshcd_pm_runtime_get(struct device *dev) +{ + // Don't trigger PM events while in transition states + if (dev->power.runtime_status == RPM_RESUMING || + dev->power.runtime_status == RPM_SUSPENDING) + pm_runtime_get_noresume(dev); + else + pm_runtime_get_sync(dev); +} + +static void ufshcd_pm_runtime_put(struct device *dev) +{ + // Don't trigger PM events while in transition states + if (dev->power.runtime_status == RPM_RESUMING || + dev->power.runtime_status == RPM_SUSPENDING) + pm_runtime_put_noidle(dev); + else { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } +} +#else +static void ufshcd_pm_runtime_get(struct device *dev) +{ +} +static void ufshcd_pm_runtime_put(struct device *dev) +{ +} +#endif + static inline int ufshcd_enable_irq(struct ufs_hba *hba) { int ret = 0; @@ -1345,7 +1376,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, if (value == hba->clk_scaling.is_allowed) goto out; - pm_runtime_get_sync(hba->dev); + ufshcd_pm_runtime_get(hba->dev); ufshcd_hold(hba, false); cancel_work_sync(&hba->clk_scaling.suspend_work); @@ -1364,7 +1395,7 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev, } ufshcd_release(hba); - pm_runtime_put_sync(hba->dev); + ufshcd_pm_runtime_put(hba->dev); out: return count; } @@ -1948,6 +1979,7 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) unsigned long flags; ufshcd_hold(hba, false); + ufshcd_pm_runtime_get(hba->dev); mutex_lock(&hba->uic_cmd_mutex); ufshcd_add_delay_before_dme_cmd(hba); @@ -1959,6 +1991,7 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) mutex_unlock(&hba->uic_cmd_mutex); + ufshcd_pm_runtime_put(hba->dev); ufshcd_release(hba); return ret; } @@ -2345,6 +2378,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) clear_bit_unlock(tag, &hba->lrb_in_use); goto out; } + ufshcd_pm_runtime_get(hba->dev); WARN_ON(hba->clk_gating.state != CLKS_ON); lrbp = &hba->lrb[tag]; @@ -3555,6 +3589,7 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) int ret; bool reenable_intr = false; + ufshcd_pm_runtime_get(hba->dev); mutex_lock(&hba->uic_cmd_mutex); init_completion(&uic_async_done); ufshcd_add_delay_before_dme_cmd(hba); @@ -3609,6 +3644,7 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) ufshcd_enable_intr(hba, UIC_COMMAND_COMPL); spin_unlock_irqrestore(hba->host->host_lock, flags); mutex_unlock(&hba->uic_cmd_mutex); + ufshcd_pm_runtime_put(hba->dev); return ret; } @@ -4386,6 +4422,7 @@ static int ufshcd_slave_configure(struct scsi_device *sdev) blk_queue_update_dma_pad(q, PRDT_DATA_BYTE_COUNT_PAD - 1); blk_queue_max_segment_size(q, PRDT_DATA_BYTE_COUNT_MAX); + scsi_autopm_put_device(sdev); return 0; } @@ -4622,6 +4659,7 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, /* Do not touch lrbp after scsi done */ cmd->scsi_done(cmd); __ufshcd_release(hba); + ufshcd_pm_runtime_put(hba->dev); } else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE || lrbp->command_type == UTP_CMD_TYPE_UFS_STORAGE) { if (hba->dev_cmd.complete) { @@ -4951,7 +4989,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) u32 status = 0; hba = container_of(work, struct ufs_hba, eeh_work); - pm_runtime_get_sync(hba->dev); + ufshcd_pm_runtime_get(hba->dev); err = ufshcd_get_ee_status(hba, &status); if (err) { dev_err(hba->dev, "%s: failed to get exception status %d\n", @@ -4965,7 +5003,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) ufshcd_bkops_exception_event_handler(hba); out: - pm_runtime_put_sync(hba->dev); + ufshcd_pm_runtime_put(hba->dev); return; } @@ -5065,7 +5103,7 @@ static void ufshcd_err_handler(struct work_struct *work) hba = container_of(work, struct ufs_hba, eh_work); - pm_runtime_get_sync(hba->dev); + ufshcd_pm_runtime_get(hba->dev); ufshcd_hold(hba, false); spin_lock_irqsave(hba->host->host_lock, flags); @@ -5177,7 +5215,7 @@ static void ufshcd_err_handler(struct work_struct *work) spin_unlock_irqrestore(hba->host->host_lock, flags); scsi_unblock_requests(hba->host); ufshcd_release(hba); - pm_runtime_put_sync(hba->dev); + ufshcd_pm_runtime_put(hba->dev); } static void ufshcd_update_uic_reg_hist(struct ufs_uic_err_reg_hist *reg_hist, @@ -6425,7 +6463,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) } scsi_scan_host(hba->host); - pm_runtime_put_sync(hba->dev); + ufshcd_pm_runtime_put(hba->dev); } if (!hba->is_init_prefetch) @@ -6437,7 +6475,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) * present, turn off the power/clocks etc. */ if (ret && !ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress) { - pm_runtime_put_sync(hba->dev); + ufshcd_pm_runtime_put(hba->dev); ufshcd_hba_exit(hba); } @@ -7747,6 +7785,8 @@ EXPORT_SYMBOL(ufshcd_shutdown); void ufshcd_remove(struct ufs_hba *hba) { ufshcd_remove_sysfs_nodes(hba); + // Resume if suspended for sync + ufshcd_pm_runtime_get(hba->dev); scsi_remove_host(hba->host); /* disable interrupts */ ufshcd_disable_intr(hba, hba->intr_mask); @@ -7756,6 +7796,8 @@ void ufshcd_remove(struct ufs_hba *hba) if (ufshcd_is_clkscaling_supported(hba)) device_remove_file(hba->dev, &hba->clk_scaling.enable_attr); ufshcd_hba_exit(hba); + // Final sync finished - put it back + ufshcd_pm_runtime_put(hba->dev); } EXPORT_SYMBOL_GPL(ufshcd_remove); @@ -7978,11 +8020,11 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE); hba->spm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state( - UFS_SLEEP_PWR_MODE, - UIC_LINK_HIBERN8_STATE); + UFS_POWERDOWN_PWR_MODE, + UIC_LINK_OFF_STATE); /* Hold auto suspend until async scan completes */ - pm_runtime_get_sync(dev); + ufshcd_pm_runtime_get(dev); /* * We are assuming that device wasn't put in sleep/power-down