[v4,12/14] cpuidle: psci: Support CPU hotplug for the hierarchical model

Message ID 20191211154343.29765-13-ulf.hansson@linaro.org
State Superseded
Headers show
Series
  • cpuidle: psci: Support hierarchical CPU arrangement
Related show

Commit Message

Ulf Hansson Dec. 11, 2019, 3:43 p.m.
When the hierarchical CPU topology is used and when a CPU is put offline,
that CPU prevents its PM domain from being powered off, which is because
genpd observes the corresponding attached device as being active from a
runtime PM point of view. Furthermore, any potential master PM domains are
also prevented from being powered off.

To address this limitation, let's add add a new CPU hotplug state
(CPUHP_AP_CPU_PM_STARTING) and register up/down callbacks for it, which
allows us to deal with runtime PM accordingly.

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

---

Changes in v4:
	- Folded in the patch to be part of the series.
	- Rebased on top of earlier changes.

---
 drivers/cpuidle/cpuidle-psci.c | 45 +++++++++++++++++++++++++++++++++-
 include/linux/cpuhotplug.h     |  1 +
 2 files changed, 45 insertions(+), 1 deletion(-)

-- 
2.17.1

Comments

Ulf Hansson Dec. 19, 2019, 3:48 p.m. | #1
On Thu, 19 Dec 2019 at 15:33, Sudeep Holla <sudeep.holla@arm.com> wrote:
>

> On Wed, Dec 11, 2019 at 04:43:41PM +0100, Ulf Hansson wrote:

> > When the hierarchical CPU topology is used and when a CPU is put offline,

> > that CPU prevents its PM domain from being powered off, which is because

> > genpd observes the corresponding attached device as being active from a

> > runtime PM point of view. Furthermore, any potential master PM domains are

> > also prevented from being powered off.

> >

> > To address this limitation, let's add add a new CPU hotplug state

> > (CPUHP_AP_CPU_PM_STARTING) and register up/down callbacks for it, which

> > allows us to deal with runtime PM accordingly.

> >

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

> > ---

> >

> > Changes in v4:

> >       - Folded in the patch to be part of the series.

> >       - Rebased on top of earlier changes.

> >

> > ---

> >  drivers/cpuidle/cpuidle-psci.c | 45 +++++++++++++++++++++++++++++++++-

> >  include/linux/cpuhotplug.h     |  1 +

> >  2 files changed, 45 insertions(+), 1 deletion(-)

> >

> > diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c

> > index 6e7804e697ed..34a89d99bb0f 100644

> > --- a/drivers/cpuidle/cpuidle-psci.c

> > +++ b/drivers/cpuidle/cpuidle-psci.c

> > @@ -8,6 +8,7 @@

> >

> >  #define pr_fmt(fmt) "CPUidle PSCI: " fmt

> >

> > +#include <linux/cpuhotplug.h>

> >  #include <linux/cpuidle.h>

> >  #include <linux/cpumask.h>

> >  #include <linux/cpu_pm.h>

> > @@ -31,6 +32,7 @@ struct psci_cpuidle_data {

> >

> >  static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data);

> >  static DEFINE_PER_CPU(u32, domain_state);

> > +static bool psci_cpuidle_use_cpuhp;

> >

>

> [...]

>

> > +

> > +static void psci_idle_init_cpuhp(void)

> > +{

>

> Can this be made __init along with the boolean psci_cpuidle_use_cpuhp ?


Yeah, make sense!

>

> With that,

> Reviewed-by: Sudeep Holla <sudeep.holla@arm.com>


Thanks!

Kind regards
Uffe

Patch

diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c
index 6e7804e697ed..34a89d99bb0f 100644
--- a/drivers/cpuidle/cpuidle-psci.c
+++ b/drivers/cpuidle/cpuidle-psci.c
@@ -8,6 +8,7 @@ 
 
 #define pr_fmt(fmt) "CPUidle PSCI: " fmt
 
+#include <linux/cpuhotplug.h>
 #include <linux/cpuidle.h>
 #include <linux/cpumask.h>
 #include <linux/cpu_pm.h>
@@ -31,6 +32,7 @@  struct psci_cpuidle_data {
 
 static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data);
 static DEFINE_PER_CPU(u32, domain_state);
+static bool psci_cpuidle_use_cpuhp;
 
 static inline void psci_set_domain_state(u32 state)
 {
@@ -72,6 +74,44 @@  static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
 	return ret;
 }
 
+static int psci_idle_cpuhp_up(unsigned int cpu)
+{
+	struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev);
+
+	if (pd_dev)
+		pm_runtime_get_sync(pd_dev);
+
+	return 0;
+}
+
+static int psci_idle_cpuhp_down(unsigned int cpu)
+{
+	struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev);
+
+	if (pd_dev) {
+		pm_runtime_put_sync(pd_dev);
+		/* Clear domain state to start fresh at next online. */
+		psci_set_domain_state(0);
+	}
+
+	return 0;
+}
+
+static void psci_idle_init_cpuhp(void)
+{
+	int err;
+
+	if (!psci_cpuidle_use_cpuhp)
+		return;
+
+	err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING,
+					"cpuidle/psci:online",
+					psci_idle_cpuhp_up,
+					psci_idle_cpuhp_down);
+	if (err)
+		pr_warn("Failed %d while setup cpuhp state\n", err);
+}
+
 static int psci_enter_idle_state(struct cpuidle_device *dev,
 				struct cpuidle_driver *drv, int idx)
 {
@@ -166,9 +206,11 @@  static int __init psci_dt_cpu_init_idle(struct cpuidle_driver *drv,
 		 * selection of a shared state for the domain, assumes the
 		 * domain states are all deeper states.
 		 */
-		if (data->dev)
+		if (data->dev) {
 			drv->states[state_count - 1].enter =
 				psci_enter_domain_idle_state;
+			psci_cpuidle_use_cpuhp = true;
+		}
 	}
 
 	/* Idle states parsed correctly, store them in the per-cpu struct. */
@@ -289,6 +331,7 @@  static int __init psci_idle_init(void)
 			goto out_fail;
 	}
 
+	psci_idle_init_cpuhp();
 	return 0;
 
 out_fail:
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index e51ee772b9f5..01f04ed6ad92 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -95,6 +95,7 @@  enum cpuhp_state {
 	CPUHP_AP_OFFLINE,
 	CPUHP_AP_SCHED_STARTING,
 	CPUHP_AP_RCUTREE_DYING,
+	CPUHP_AP_CPU_PM_STARTING,
 	CPUHP_AP_IRQ_GIC_STARTING,
 	CPUHP_AP_IRQ_HIP04_STARTING,
 	CPUHP_AP_IRQ_ARMADA_XP_STARTING,