linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/4] Manage domain power state for hibernate
@ 2022-11-02 14:21 Shawn Guo
  2022-11-02 14:21 ` [PATCH v3 1/4] PM: domains: Drop genpd status manipulation for hibernate restore Shawn Guo
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Shawn Guo @ 2022-11-02 14:21 UTC (permalink / raw)
  To: Rafael J . Wysocki
  Cc: Kevin Hilman, Ulf Hansson, Len Brown, Pavel Machek,
	Greg Kroah-Hartman, linux-pm, linux-kernel, Shawn Guo

The series tries to fix a hardware lockup issue caused by missing domain
power state management from hibernate .freeze[thaw]_noirq hooks.  The
detailed description of the issue can be found in commit log of patch #4.

Changes for v3:
- Add one patch to drop genpd status manipulation for hibernate restore
- Add Ulf's Reviewed-by tag (Thanks Ulf!)

Changes for v2:
- Fix kernel doc of genpd_finish_resume() to use the new function name

Shawn Guo (4):
  PM: domains: Drop genpd status manipulation for hibernate restore
  PM: domains: Pass generic PM noirq hooks to genpd_finish_suspend()
  PM: domains: Consolidate genpd_restore_noirq() and
    genpd_resume_noirq()
  PM: domains: Power off[on] domain in hibernate .freeze[thaw]_noirq
    hook

 drivers/base/power/domain.c | 114 ++++++++++++------------------------
 1 file changed, 36 insertions(+), 78 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v3 1/4] PM: domains: Drop genpd status manipulation for hibernate restore
  2022-11-02 14:21 [PATCH v3 0/4] Manage domain power state for hibernate Shawn Guo
@ 2022-11-02 14:21 ` Shawn Guo
  2022-11-04 15:32   ` Ulf Hansson
  2022-11-02 14:21 ` [PATCH v3 2/4] PM: domains: Pass generic PM noirq hooks to genpd_finish_suspend() Shawn Guo
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: Shawn Guo @ 2022-11-02 14:21 UTC (permalink / raw)
  To: Rafael J . Wysocki
  Cc: Kevin Hilman, Ulf Hansson, Len Brown, Pavel Machek,
	Greg Kroah-Hartman, linux-pm, linux-kernel, Shawn Guo

The genpd status manipulation for hibernate restore has really never
worked as intended.  For example, if the genpd->status was GENPD_STATE_ON,
the parent domain's `sd_count` must have been increased, so it needs to
be adjusted too.  So drop this status manipulation.

Suggested-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
 drivers/base/power/domain.c | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 6471b559230e..97deae1d4e77 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1374,20 +1374,7 @@ static int genpd_restore_noirq(struct device *dev)
 	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	/*
-	 * At this point suspended_count == 0 means we are being run for the
-	 * first time for the given domain in the present cycle.
-	 */
 	genpd_lock(genpd);
-	if (genpd->suspended_count++ == 0) {
-		/*
-		 * The boot kernel might put the domain into arbitrary state,
-		 * so make it appear as powered off to genpd_sync_power_on(),
-		 * so that it tries to power it on in case it was really off.
-		 */
-		genpd->status = GENPD_STATE_OFF;
-	}
-
 	genpd_sync_power_on(genpd, true, 0);
 	genpd_unlock(genpd);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 2/4] PM: domains: Pass generic PM noirq hooks to genpd_finish_suspend()
  2022-11-02 14:21 [PATCH v3 0/4] Manage domain power state for hibernate Shawn Guo
  2022-11-02 14:21 ` [PATCH v3 1/4] PM: domains: Drop genpd status manipulation for hibernate restore Shawn Guo
@ 2022-11-02 14:21 ` Shawn Guo
  2022-11-02 14:21 ` [PATCH v3 3/4] PM: domains: Consolidate genpd_restore_noirq() and genpd_resume_noirq() Shawn Guo
  2022-11-02 14:21 ` [PATCH v3 4/4] PM: domains: Power off[on] domain in hibernate .freeze[thaw]_noirq hook Shawn Guo
  3 siblings, 0 replies; 6+ messages in thread
From: Shawn Guo @ 2022-11-02 14:21 UTC (permalink / raw)
  To: Rafael J . Wysocki
  Cc: Kevin Hilman, Ulf Hansson, Len Brown, Pavel Machek,
	Greg Kroah-Hartman, linux-pm, linux-kernel, Shawn Guo

While argument `poweroff` works fine for genpd_finish_suspend() to handle
distinction between suspend and poweroff, it won't scale if we want to
use it for freeze as well.  Pass generic PM noirq hooks as arguments
instead, so that the function can possibly cover freeze case too.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/base/power/domain.c | 25 +++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 97deae1d4e77..f18b8b1bc17a 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1189,12 +1189,15 @@ static int genpd_prepare(struct device *dev)
  * genpd_finish_suspend - Completion of suspend or hibernation of device in an
  *   I/O pm domain.
  * @dev: Device to suspend.
- * @poweroff: Specifies if this is a poweroff_noirq or suspend_noirq callback.
+ * @suspend_noirq: Generic suspend_noirq callback.
+ * @resume_noirq: Generic resume_noirq callback.
  *
  * Stop the device and remove power from the domain if all devices in it have
  * been stopped.
  */
-static int genpd_finish_suspend(struct device *dev, bool poweroff)
+static int genpd_finish_suspend(struct device *dev,
+				int (*suspend_noirq)(struct device *dev),
+				int (*resume_noirq)(struct device *dev))
 {
 	struct generic_pm_domain *genpd;
 	int ret = 0;
@@ -1203,10 +1206,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
 	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	if (poweroff)
-		ret = pm_generic_poweroff_noirq(dev);
-	else
-		ret = pm_generic_suspend_noirq(dev);
+	ret = suspend_noirq(dev);
 	if (ret)
 		return ret;
 
@@ -1217,10 +1217,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
 	    !pm_runtime_status_suspended(dev)) {
 		ret = genpd_stop_dev(genpd, dev);
 		if (ret) {
-			if (poweroff)
-				pm_generic_restore_noirq(dev);
-			else
-				pm_generic_resume_noirq(dev);
+			resume_noirq(dev);
 			return ret;
 		}
 	}
@@ -1244,7 +1241,9 @@ static int genpd_suspend_noirq(struct device *dev)
 {
 	dev_dbg(dev, "%s()\n", __func__);
 
-	return genpd_finish_suspend(dev, false);
+	return genpd_finish_suspend(dev,
+				    pm_generic_suspend_noirq,
+				    pm_generic_resume_noirq);
 }
 
 /**
@@ -1353,7 +1352,9 @@ static int genpd_poweroff_noirq(struct device *dev)
 {
 	dev_dbg(dev, "%s()\n", __func__);
 
-	return genpd_finish_suspend(dev, true);
+	return genpd_finish_suspend(dev,
+				    pm_generic_poweroff_noirq,
+				    pm_generic_restore_noirq);
 }
 
 /**
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 3/4] PM: domains: Consolidate genpd_restore_noirq() and genpd_resume_noirq()
  2022-11-02 14:21 [PATCH v3 0/4] Manage domain power state for hibernate Shawn Guo
  2022-11-02 14:21 ` [PATCH v3 1/4] PM: domains: Drop genpd status manipulation for hibernate restore Shawn Guo
  2022-11-02 14:21 ` [PATCH v3 2/4] PM: domains: Pass generic PM noirq hooks to genpd_finish_suspend() Shawn Guo
@ 2022-11-02 14:21 ` Shawn Guo
  2022-11-02 14:21 ` [PATCH v3 4/4] PM: domains: Power off[on] domain in hibernate .freeze[thaw]_noirq hook Shawn Guo
  3 siblings, 0 replies; 6+ messages in thread
From: Shawn Guo @ 2022-11-02 14:21 UTC (permalink / raw)
  To: Rafael J . Wysocki
  Cc: Kevin Hilman, Ulf Hansson, Len Brown, Pavel Machek,
	Greg Kroah-Hartman, linux-pm, linux-kernel, Shawn Guo

Most of the logic between genpd_restore_noirq() and genpd_resume_noirq()
are identical.  The suspended_count decrement for restore should be the
right thing to do anyway, considering there is an increment in
genpd_finish_suspend() for hibernation.  So consolidate these two
functions into genpd_finish_resume().

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/base/power/domain.c | 41 +++++++++++++++++--------------------
 1 file changed, 19 insertions(+), 22 deletions(-)

diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index f18b8b1bc17a..7cee9439fd21 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1247,12 +1247,14 @@ static int genpd_suspend_noirq(struct device *dev)
 }
 
 /**
- * genpd_resume_noirq - Start of resume of device in an I/O PM domain.
+ * genpd_finish_resume - Completion of resume of device in an I/O PM domain.
  * @dev: Device to resume.
+ * @resume_noirq: Generic resume_noirq callback.
  *
  * Restore power to the device's PM domain, if necessary, and start the device.
  */
-static int genpd_resume_noirq(struct device *dev)
+static int genpd_finish_resume(struct device *dev,
+			       int (*resume_noirq)(struct device *dev))
 {
 	struct generic_pm_domain *genpd;
 	int ret;
@@ -1264,7 +1266,7 @@ static int genpd_resume_noirq(struct device *dev)
 		return -EINVAL;
 
 	if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
-		return pm_generic_resume_noirq(dev);
+		return resume_noirq(dev);
 
 	genpd_lock(genpd);
 	genpd_sync_power_on(genpd, true, 0);
@@ -1281,6 +1283,19 @@ static int genpd_resume_noirq(struct device *dev)
 	return pm_generic_resume_noirq(dev);
 }
 
+/**
+ * genpd_resume_noirq - Start of resume of device in an I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Restore power to the device's PM domain, if necessary, and start the device.
+ */
+static int genpd_resume_noirq(struct device *dev)
+{
+	dev_dbg(dev, "%s()\n", __func__);
+
+	return genpd_finish_resume(dev, pm_generic_resume_noirq);
+}
+
 /**
  * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
  * @dev: Device to freeze.
@@ -1366,27 +1381,9 @@ static int genpd_poweroff_noirq(struct device *dev)
  */
 static int genpd_restore_noirq(struct device *dev)
 {
-	struct generic_pm_domain *genpd;
-	int ret = 0;
-
 	dev_dbg(dev, "%s()\n", __func__);
 
-	genpd = dev_to_genpd(dev);
-	if (IS_ERR(genpd))
-		return -EINVAL;
-
-	genpd_lock(genpd);
-	genpd_sync_power_on(genpd, true, 0);
-	genpd_unlock(genpd);
-
-	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
-	    !pm_runtime_status_suspended(dev)) {
-		ret = genpd_start_dev(genpd, dev);
-		if (ret)
-			return ret;
-	}
-
-	return pm_generic_restore_noirq(dev);
+	return genpd_finish_resume(dev, pm_generic_restore_noirq);
 }
 
 /**
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 4/4] PM: domains: Power off[on] domain in hibernate .freeze[thaw]_noirq hook
  2022-11-02 14:21 [PATCH v3 0/4] Manage domain power state for hibernate Shawn Guo
                   ` (2 preceding siblings ...)
  2022-11-02 14:21 ` [PATCH v3 3/4] PM: domains: Consolidate genpd_restore_noirq() and genpd_resume_noirq() Shawn Guo
@ 2022-11-02 14:21 ` Shawn Guo
  3 siblings, 0 replies; 6+ messages in thread
From: Shawn Guo @ 2022-11-02 14:21 UTC (permalink / raw)
  To: Rafael J . Wysocki
  Cc: Kevin Hilman, Ulf Hansson, Len Brown, Pavel Machek,
	Greg Kroah-Hartman, linux-pm, linux-kernel, Shawn Guo

On platforms which use SHUTDOWN as hibernation mode, the genpd noirq
hooks will be called like below.

    genpd_freeze_noirq()         genpd_restore_noirq()
          ↓                            ↑
    Create snapshot image        Restore target kernel
          ↓                            ↑
    genpd_thaw_noirq()           genpd_freeze_noirq()
          ↓                            ↑
    Write snapshot image         Read snapshot image
          ↓                            ↑
    power_down()                 Kernel boot

As of today suspend hooks genpd_suspend[resume]_noirq() manages domain
on/off state, but hibernate hooks genpd_freeze[thaw]_noirq() doesn't.
This results in a different behavior of domain power state between suspend
and hibernate freeze, i.e. domain is powered off for the former while on
for the later.  It causes a problem on platforms like i.MX where the
domain needs to be powered on/off by calling clock and regulator interface.
When the platform restores from hibernation, the domain is off in hardware
and genpd_restore_noirq() tries to power it on, but will never succeed
because software state of domain (clock and regulator) is left on from the
last hibernate freeze, so kernel thinks that clock and regulator are
enabled while they are actually not turned on in hardware.  The
consequence would be that devices in the power domain will access
registers without clock or power, and cause hardware lockup.

Power off[on] domain in hibernate .freeze[thaw]_noirq hook for reasons:

- Align the behavior between suspend and hibernate freeze.
- Have power state of domains stay in sync between hardware and software
  for hibernate freeze, and thus fix the lockup issue seen on i.MX
  platform.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/base/power/domain.c | 35 ++++-------------------------------
 1 file changed, 4 insertions(+), 31 deletions(-)

diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 7cee9439fd21..2f0787d5101b 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1307,24 +1307,11 @@ static int genpd_resume_noirq(struct device *dev)
  */
 static int genpd_freeze_noirq(struct device *dev)
 {
-	const struct generic_pm_domain *genpd;
-	int ret = 0;
-
 	dev_dbg(dev, "%s()\n", __func__);
 
-	genpd = dev_to_genpd(dev);
-	if (IS_ERR(genpd))
-		return -EINVAL;
-
-	ret = pm_generic_freeze_noirq(dev);
-	if (ret)
-		return ret;
-
-	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
-	    !pm_runtime_status_suspended(dev))
-		ret = genpd_stop_dev(genpd, dev);
-
-	return ret;
+	return genpd_finish_suspend(dev,
+				    pm_generic_freeze_noirq,
+				    pm_generic_thaw_noirq);
 }
 
 /**
@@ -1336,23 +1323,9 @@ static int genpd_freeze_noirq(struct device *dev)
  */
 static int genpd_thaw_noirq(struct device *dev)
 {
-	const struct generic_pm_domain *genpd;
-	int ret = 0;
-
 	dev_dbg(dev, "%s()\n", __func__);
 
-	genpd = dev_to_genpd(dev);
-	if (IS_ERR(genpd))
-		return -EINVAL;
-
-	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
-	    !pm_runtime_status_suspended(dev)) {
-		ret = genpd_start_dev(genpd, dev);
-		if (ret)
-			return ret;
-	}
-
-	return pm_generic_thaw_noirq(dev);
+	return genpd_finish_resume(dev, pm_generic_thaw_noirq);
 }
 
 /**
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 1/4] PM: domains: Drop genpd status manipulation for hibernate restore
  2022-11-02 14:21 ` [PATCH v3 1/4] PM: domains: Drop genpd status manipulation for hibernate restore Shawn Guo
@ 2022-11-04 15:32   ` Ulf Hansson
  0 siblings, 0 replies; 6+ messages in thread
From: Ulf Hansson @ 2022-11-04 15:32 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Rafael J . Wysocki, Kevin Hilman, Len Brown, Pavel Machek,
	Greg Kroah-Hartman, linux-pm, linux-kernel

On Wed, 2 Nov 2022 at 15:21, Shawn Guo <shawn.guo@linaro.org> wrote:
>
> The genpd status manipulation for hibernate restore has really never
> worked as intended.  For example, if the genpd->status was GENPD_STATE_ON,
> the parent domain's `sd_count` must have been increased, so it needs to
> be adjusted too.  So drop this status manipulation.
>
> Suggested-by: Ulf Hansson <ulf.hansson@linaro.org>
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>

Note that, I was trying to understand a little more about the
background to the below code, although it's not entirely easy to
browse the git history around this.

Unless Rafael thinks there are reasons to keep the code as is, I
wouldn't mind seeing it go away. So:

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>

Kind regards
Uffe

> ---
>  drivers/base/power/domain.c | 13 -------------
>  1 file changed, 13 deletions(-)
>
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index 6471b559230e..97deae1d4e77 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -1374,20 +1374,7 @@ static int genpd_restore_noirq(struct device *dev)
>         if (IS_ERR(genpd))
>                 return -EINVAL;
>
> -       /*
> -        * At this point suspended_count == 0 means we are being run for the
> -        * first time for the given domain in the present cycle.
> -        */
>         genpd_lock(genpd);
> -       if (genpd->suspended_count++ == 0) {
> -               /*
> -                * The boot kernel might put the domain into arbitrary state,
> -                * so make it appear as powered off to genpd_sync_power_on(),
> -                * so that it tries to power it on in case it was really off.
> -                */
> -               genpd->status = GENPD_STATE_OFF;
> -       }
> -
>         genpd_sync_power_on(genpd, true, 0);
>         genpd_unlock(genpd);
>
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-11-04 15:33 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-02 14:21 [PATCH v3 0/4] Manage domain power state for hibernate Shawn Guo
2022-11-02 14:21 ` [PATCH v3 1/4] PM: domains: Drop genpd status manipulation for hibernate restore Shawn Guo
2022-11-04 15:32   ` Ulf Hansson
2022-11-02 14:21 ` [PATCH v3 2/4] PM: domains: Pass generic PM noirq hooks to genpd_finish_suspend() Shawn Guo
2022-11-02 14:21 ` [PATCH v3 3/4] PM: domains: Consolidate genpd_restore_noirq() and genpd_resume_noirq() Shawn Guo
2022-11-02 14:21 ` [PATCH v3 4/4] PM: domains: Power off[on] domain in hibernate .freeze[thaw]_noirq hook Shawn Guo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).