Thread (17 messages) 17 messages, 3 authors, 2015-06-11
STALE4057d
Revisions (8)
  1. rfc [diff vs current]
  2. rfc [diff vs current]
  3. rfc [diff vs current]
  4. rfc current
  5. rfc [diff vs current]
  6. rfc [diff vs current]
  7. rfc [diff vs current]
  8. rfc [diff vs current]

[PATCH RFC 2/3] PM / Domains: Support atomic PM domains

From: Lina Iyer <hidden>
Date: 2015-06-10 16:13:23
Also in: linux-pm

On Sun, Jun 07 2015 at 03:21 -0600, Krzysztof Kozlowski wrote:
W dniu 05.06.2015 o 07:29, Lina Iyer pisze:
quoted
Power Domains currently support turning on/off only in process context.
This restricts the usage of PM domains to devices and domains that
could be powered on/off in irq disabled contexts as the mutexes used in
GenPD allows for cpu sleep while waiting for locks.
I can find also other use case: currently the power domain with irq_safe
devices is always powered on. With the patch it could be powered off (of
course if the driver/mach code is irq-safe).
Yes, absolutely.
quoted
Genpd inherently provides support for devices, domain hierarchy and can
be used to represent cpu clusters like in ARM's big.Little, where, each
cpu cluster is in its domain, with supporting caches and other
peripheral hardware. Multiple such domains could be part of another
domain. Because mutexes are used to protect and synchronize domain
operations and cpu idle operations are inherently atomic, the use of
genpd is not possible for runtime suspend and resume of the pm domain.
Replacing the locks to spinlocks would allow cpu domain to be be powered
off to save power, when all the cpus are powered off.

However, not all domains can operate in irq-safe contexts and usually
would need to sleep during domain operations. So genpd has to support
both the cases, where the domain is or is not irq-safe. The irq-safe
attribute is therefore domain specific.

To achieve domain specific locking, set the GENPD_FLAG_IRQ_SAFE flag
while defining the domain. This determines if the domain should use a
spinlock instead of a mutex. Locking is abstracted through
genpd_lock_domain() and genpd_unlock_domain() functions that use the
flag to determine the locking to be used for this domain.

The restriction this imposes on the domain hierarchy is that subdomains
and all devices in the hierarchy also be irq-safe. Non irq-safe domains
may continue to have irq-safe devices, but not the other way around.
So an irq-safe device can be put in irq-safe subdomain which can be a
child of non-irq-safe topdomain?
Yes, the container need not be irq-safe but the contained need to be
irqsafe.
quoted
Cc: Ulf Hansson <redacted>
Cc: Rafael J. Wysocki <redacted>
Cc: Kevin Hilman <redacted>
Signed-off-by: Lina Iyer <redacted>
---
 drivers/base/power/domain.c | 200 ++++++++++++++++++++++++++++++++++----------
 include/linux/pm_domain.h   |  11 ++-
Documentation should also be reflected.
Yes, will add.
quoted
 2 files changed, 164 insertions(+), 47 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index dfd7595..8b89d15 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -50,6 +50,71 @@
 static LIST_HEAD(gpd_list);
 static DEFINE_MUTEX(gpd_list_lock);

+static inline int genpd_lock_domain_noirq(struct generic_pm_domain *genpd,
+					unsigned int subclass)
+	__acquires(&genpd->slock)
+{
+	unsigned long flags;
+
+	if (unlikely(subclass > 0))
+		spin_lock_irqsave_nested(&genpd->slock, flags, subclass);
+	else
+		spin_lock_irqsave(&genpd->slock, flags);
+
+	genpd->flags = flags;
+
+	return 0;
+}
+
+static inline int genpd_unlock_domain_noirq(struct generic_pm_domain *genpd)
+	__releases(&genpd->slock)
+{
+	spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags);
+	return 0;
+}
+
+static inline int genpd_lock_domain_irq(struct generic_pm_domain *genpd,
+					unsigned int subclass)
+	__acquires(&genpd->mlock)
+{
+	if (unlikely(subclass > 0))
+		mutex_lock_nested(&genpd->mlock, subclass);
+	else
+		mutex_lock(&genpd->mlock);
+
+	return 0;
+}
+
+static inline int genpd_lock_domain_interruptible_irq(
+				struct generic_pm_domain *genpd)
+	__acquires(&genpd->mlock)
+{
+	return mutex_lock_interruptible(&genpd->mlock);
+}
+
+static inline int genpd_unlock_domain_irq(struct generic_pm_domain *genpd)
+	__releases(&genpd->mlock)
+{
+	mutex_unlock(&genpd->mlock);
+	return 0;
+}
+
+#define genpd_lock_domain(genpd)				\
+	(genpd->irq_safe ? genpd_lock_domain_noirq(genpd, 0)	\
+			: genpd_lock_domain_irq(genpd, 0))
+
+#define genpd_lock_domain_nested(genpd)				\
+	(genpd->irq_safe ? genpd_lock_domain_noirq(genpd, SINGLE_DEPTH_NESTING)\
+			: genpd_lock_domain_irq(genpd, SINGLE_DEPTH_NESTING))
+
+#define genpd_unlock_domain(genpd)				\
+	(genpd->irq_safe ? genpd_unlock_domain_noirq(genpd)	\
+			: genpd_unlock_domain_irq(genpd))
+
+#define genpd_lock_domain_interruptible(genpd)			\
+	(genpd->irq_safe ? genpd_lock_domain_noirq(genpd, 0)	\
+			: genpd_lock_domain_interruptible_irq(genpd))
Why macros? You are not using here benefits of a macro and they are
called just like ordinary functions.
Well, I didnt see a need for a function that might show up in the stack.
But I have no strong preference either way.
You added "domain" prefix but genpd already contains this. genod_lock(),
genpd_lock_nested() etc. should be sufficient, unless there is a
conflict, similar name planned or you plan to lock something else
(genpd_lock_device?).
Sigh. Yes, you are right. Its redundant. Will remove.
quoted
+
 static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
 {
 	struct generic_pm_domain *genpd = NULL, *gpd;
@@ -262,9 +327,9 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)
 {
 	int ret;

-	mutex_lock(&genpd->lock);
+	genpd_lock_domain(genpd);
 	ret = __pm_genpd_poweron(genpd);
-	mutex_unlock(&genpd->lock);
+	genpd_unlock_domain(genpd);
 	return ret;
 }
@@ -326,9 +391,9 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
 		spin_unlock_irq(&dev->power.lock);

 		if (!IS_ERR(genpd)) {
-			mutex_lock(&genpd->lock);
+			genpd_lock_domain(genpd);
 			genpd->max_off_time_changed = true;
-			mutex_unlock(&genpd->lock);
+			genpd_unlock_domain(genpd);
 		}

 		dev = dev->parent;
@@ -387,7 +452,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
 			return -EBUSY;

 		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
-		    || pdd->dev->power.irq_safe))
+			|| (pdd->dev->power.irq_safe && !genpd->irq_safe)))
 			not_suspended++;
 	}
@@ -453,9 +518,9 @@ static void genpd_power_off_work_fn(struct work_struct *work)

 	genpd = container_of(work, struct generic_pm_domain, power_off_work);

-	mutex_lock(&genpd->lock);
+	genpd_lock_domain(genpd);
 	pm_genpd_poweroff(genpd);
Ipm_genpd_poweroff() calls __pm_genpd_save_device() which grabs mutex.
At least in next-20150604 but maybe the patches, which this depends on,
changed it?
Yes. Ulf's patch remvoed that call.
quoted
-	mutex_unlock(&genpd->lock);
+	genpd_unlock_domain(genpd);
 }

 /**
@@ -478,12 +543,8 @@ static int pm_genpd_runtime_suspend(struct device *dev)
 	if (IS_ERR(genpd))
 		return -EINVAL;

-	/*
-	 * We can't allow to power off the PM domain if it holds an irq_safe
-	 * device. That's beacuse we use mutexes to protect data while power
-	 * off and on the PM domain, thus we can't execute in atomic context.
-	 */
-	if (dev->power.irq_safe)
+	/* We can't allow to power off a domain that is also not irq safe. */
+	if (dev->power.irq_safe && !genpd->irq_safe)
 		return -EBUSY;

 	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
@@ -500,11 +561,19 @@ static int pm_genpd_runtime_suspend(struct device *dev)
 		return ret;
 	}

-	mutex_lock(&genpd->lock);
+	/*
+	 * If power.irq_safe is set, this routine will be run with interrupts
+	 * off, so suspend only if the power domain is irq_safe.
+	 */
+	if (dev->power.irq_safe && !genpd->irq_safe)
+		return 0;
+
+	genpd_lock_domain(genpd);
+
 	genpd->in_progress++;
 	pm_genpd_poweroff(genpd);
 	genpd->in_progress--;
-	mutex_unlock(&genpd->lock);
+	genpd_unlock_domain(genpd);

 	return 0;
 }
@@ -528,13 +597,16 @@ static int pm_genpd_runtime_resume(struct device *dev)
 	if (IS_ERR(genpd))
 		return -EINVAL;

-	/* If power.irq_safe, the PM domain is never powered off. */
-	if (dev->power.irq_safe)
+	/*
+	 * If power.irq_safe and domain is not, then
+	 * the PM domain is never powered off.
+	 */
+	if (dev->power.irq_safe && !genpd->irq_safe)
 		return genpd_start_dev_no_timing(genpd, dev);

-	mutex_lock(&genpd->lock);
+	genpd_lock_domain(genpd);
 	ret = __pm_genpd_poweron(genpd);
-	mutex_unlock(&genpd->lock);
+	genpd_unlock_domain(genpd);

 	if (ret)
 		return ret;
@@ -729,14 +801,14 @@ static int pm_genpd_prepare(struct device *dev)
 	if (resume_needed(dev, genpd))
 		pm_runtime_resume(dev);

-	mutex_lock(&genpd->lock);
+	genpd_lock_domain(genpd);

 	if (genpd->prepared_count++ == 0) {
 		genpd->suspended_count = 0;
 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
 	}

-	mutex_unlock(&genpd->lock);
+	genpd_unlock_domain(genpd);

 	if (genpd->suspend_power_off) {
 		pm_runtime_put_noidle(dev);
@@ -754,12 +826,12 @@ static int pm_genpd_prepare(struct device *dev)

 	ret = pm_generic_prepare(dev);
 	if (ret) {
-		mutex_lock(&genpd->lock);
+		genpd_lock_domain(genpd);

 		if (--genpd->prepared_count == 0)
 			genpd->suspend_power_off = false;

-		mutex_unlock(&genpd->lock);
+		genpd_unlock_domain(genpd);
 		pm_runtime_enable(dev);
 	}
@@ -1116,13 +1188,13 @@ static void pm_genpd_complete(struct device *dev)
 	if (IS_ERR(genpd))
 		return;

-	mutex_lock(&genpd->lock);
+	genpd_lock_domain(genpd);

 	run_complete = !genpd->suspend_power_off;
 	if (--genpd->prepared_count == 0)
 		genpd->suspend_power_off = false;

-	mutex_unlock(&genpd->lock);
+	genpd_unlock_domain(genpd);

 	if (run_complete) {
 		pm_generic_complete(dev);
@@ -1266,11 +1338,18 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
 		return -EINVAL;

+	/* Devices in an IRQ safe PM Domain have to be irq safe too */
Why? Can you add this information here? Previously there was a reason in
case of irq_safe devices which you removed leaving only policy.
Sorry, your question is not clear to me.
I believe this is a new requirement that enforces the contained devices
of an irq-safe domain to be irq-safe as well.

Thanks for your review.

-- Lina
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help