public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
From: Subhash Jadavani <subhashj@codeaurora.org>
To: Michal Potomski <michalx.potomski@intel.com>
Cc: linux-scsi@vger.kernel.org, vinholikatti@gmail.com,
	martin.petersen@oracle.com, jejb@linux.vnet.ibm.com,
	szymonx.mielczarek@intel.com, linux-scsi-owner@vger.kernel.org
Subject: Re: [PATCH] scsi: ufs: Fix Runtime PM
Date: Tue, 21 Nov 2017 11:45:22 -0800	[thread overview]
Message-ID: <182dc19cbdb877bd0f8dfd2d870c1a0f@codeaurora.org> (raw)
In-Reply-To: <20171113091426.15573-1-michalx.potomski@intel.com>



On 2017-11-13 01:14, Michal Potomski wrote:
> From: Michał Potomski <michalx.potomski@intel.com>
> 
> 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 <michalx.potomski@intel.com>
> ---
>  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)

Why do we need to check this? Isn't this check race condition prone?
> +		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);


why do we need to hold PM reference count here? I thought as ufs host is 
parent of scsi_host/scsi_device, scsi would make sure that UFS host is 
runtime resumed before any access to it.

>  	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);


scsi_sysfs_add_sdev() calls scsi_autopm_put_device() so not sure why 
this is required here?

> 
>  	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);

This may add lot of latencies on resume so not sure we want to change 
the default.

> 
>  	/* 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

-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

  reply	other threads:[~2017-11-21 19:45 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-11-13  9:14 [PATCH] scsi: ufs: Fix Runtime PM Michal Potomski
2017-11-21 19:45 ` Subhash Jadavani [this message]
2017-11-22 10:06   ` Potomski, MichalX
  -- strict thread matches above, loose matches on Subject: below --
2017-11-09  9:16 Michal Potomski
2017-11-10 12:57 ` kbuild test robot
2017-11-10 16:38 ` kbuild test robot
2017-11-13  8:47 ` Avri Altman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=182dc19cbdb877bd0f8dfd2d870c1a0f@codeaurora.org \
    --to=subhashj@codeaurora.org \
    --cc=jejb@linux.vnet.ibm.com \
    --cc=linux-scsi-owner@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=martin.petersen@oracle.com \
    --cc=michalx.potomski@intel.com \
    --cc=szymonx.mielczarek@intel.com \
    --cc=vinholikatti@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox