All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] PM: Generic PM domains and device PM QoS
@ 2011-08-30 22:17 ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:17 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

Hi,

This patchset illustrates how device PM QoS may be used along with
PM domains in my view.

Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
suitable for 3.2, unless somebody hates them, but patches [4-5/5] are
total RFC.  They haven't been tested, only compiled, so the use of them
is not encouraged (they may kill your dog or make your cat go wild, or
do something equally nasty, so beware).  Their purpose is to illustrate
an idea that I'd like to discuss at the PM miniconference during the
LPC.

[1/5] - Split device PM domain data into a "base" and additional fields
        (one need_restore field at the moment, but the subsequent patches
        add more fields).

[2/5] - Make runtime PM always release power.lock if power.irq_safe is set.

[3/5] - Add function for reading device PM QoS values safely.

[4/5] - Add governor function for stopping devices.

[5/5] - Add generic PM domain power off governor function.

Thanks,
Rafael


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

* [PATCH 0/5] PM: Generic PM domains and device PM QoS
@ 2011-08-30 22:17 ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:17 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

Hi,

This patchset illustrates how device PM QoS may be used along with
PM domains in my view.

Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
suitable for 3.2, unless somebody hates them, but patches [4-5/5] are
total RFC.  They haven't been tested, only compiled, so the use of them
is not encouraged (they may kill your dog or make your cat go wild, or
do something equally nasty, so beware).  Their purpose is to illustrate
an idea that I'd like to discuss at the PM miniconference during the
LPC.

[1/5] - Split device PM domain data into a "base" and additional fields
        (one need_restore field at the moment, but the subsequent patches
        add more fields).

[2/5] - Make runtime PM always release power.lock if power.irq_safe is set.

[3/5] - Add function for reading device PM QoS values safely.

[4/5] - Add governor function for stopping devices.

[5/5] - Add generic PM domain power off governor function.

Thanks,
Rafael


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

* [PATCH 1/5] PM / Domains: Split device PM domain data into base and need_restore
  2011-08-30 22:17 ` Rafael J. Wysocki
@ 2011-08-30 22:18   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:18 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

The struct pm_domain_data data type is defined in such a way that
adding new fields specific to the generic PM domains code will
require include/linux/pm.h to be modified.  As a result, data types
used only by the generic PM domains code will be defined in two
headers, although they all should be defined in pm_domain.h and
pm.h will need to include more headers, which won't be very nice.

For this reason change the definition of struct pm_subsys_data
so that its domain_data member is a pointer, which will allow
struct pm_domain_data to be subclassed by various PM domains
implementations.  Remove the need_restore member from
struct pm_domain_data and make the generic PM domains code
subclass it by adding the need_restore member to the new data type.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |   28 +++++++++++++++++++---------
 include/linux/pm.h          |    3 +--
 include/linux/pm_domain.h   |   10 ++++++++++
 3 files changed, 30 insertions(+), 11 deletions(-)

Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -433,7 +433,6 @@ struct wakeup_source;
 struct pm_domain_data {
 	struct list_head list_node;
 	struct device *dev;
-	bool need_restore;
 };
 
 struct pm_subsys_data {
@@ -443,7 +442,7 @@ struct pm_subsys_data {
 	struct list_head clock_list;
 #endif
 #ifdef CONFIG_PM_GENERIC_DOMAINS
-	struct pm_domain_data domain_data;
+	struct pm_domain_data *domain_data;
 #endif
 };
 
Index: linux/include/linux/pm_domain.h
=================================--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -62,6 +62,16 @@ struct gpd_link {
 	struct list_head slave_node;
 };
 
+struct generic_pm_domain_data {
+	struct pm_domain_data base;
+	bool need_restore;
+};
+
+static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data *pdd)
+{
+	return container_of(pdd, struct generic_pm_domain_data, base);
+}
+
 #ifdef CONFIG_PM_GENERIC_DOMAINS
 extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
 			       struct device *dev);
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -188,11 +188,12 @@ static int __pm_genpd_save_device(struct
 				  struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 	int ret = 0;
 
-	if (pdd->need_restore)
+	if (gpd_data->need_restore)
 		return 0;
 
 	mutex_unlock(&genpd->lock);
@@ -210,7 +211,7 @@ static int __pm_genpd_save_device(struct
 	mutex_lock(&genpd->lock);
 
 	if (!ret)
-		pdd->need_restore = true;
+		gpd_data->need_restore = true;
 
 	return ret;
 }
@@ -224,10 +225,11 @@ static void __pm_genpd_restore_device(st
 				      struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 
-	if (!pdd->need_restore)
+	if (!gpd_data->need_restore)
 		return;
 
 	mutex_unlock(&genpd->lock);
@@ -244,7 +246,7 @@ static void __pm_genpd_restore_device(st
 
 	mutex_lock(&genpd->lock);
 
-	pdd->need_restore = false;
+	gpd_data->need_restore = false;
 }
 
 /**
@@ -493,7 +495,7 @@ static int pm_genpd_runtime_resume(struc
 		mutex_lock(&genpd->lock);
 	}
 	finish_wait(&genpd->status_wait_queue, &wait);
-	__pm_genpd_restore_device(&dev->power.subsys_data->domain_data, genpd);
+	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
 	genpd->resume_count--;
 	genpd_set_active(genpd);
 	wake_up_all(&genpd->status_wait_queue);
@@ -1080,6 +1082,7 @@ static void pm_genpd_complete(struct dev
  */
 int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
 {
+	struct generic_pm_domain_data *gpd_data;
 	struct pm_domain_data *pdd;
 	int ret = 0;
 
@@ -1106,14 +1109,20 @@ int pm_genpd_add_device(struct generic_p
 			goto out;
 		}
 
+	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
+	if (!gpd_data) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
 	genpd->device_count++;
 
 	dev->pm_domain = &genpd->domain;
 	dev_pm_get_subsys_data(dev);
-	pdd = &dev->power.subsys_data->domain_data;
-	pdd->dev = dev;
-	pdd->need_restore = false;
-	list_add_tail(&pdd->list_node, &genpd->dev_list);
+	dev->power.subsys_data->domain_data = &gpd_data->base;
+	gpd_data->base.dev = dev;
+	gpd_data->need_restore = false;
+	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
 
  out:
 	genpd_release_lock(genpd);
@@ -1152,6 +1161,7 @@ int pm_genpd_remove_device(struct generi
 		pdd->dev = NULL;
 		dev_pm_put_subsys_data(dev);
 		dev->pm_domain = NULL;
+		kfree(to_gpd_data(pdd));
 
 		genpd->device_count--;
 


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

* [PATCH 1/5] PM / Domains: Split device PM domain data into base and need_restore
@ 2011-08-30 22:18   ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:18 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

The struct pm_domain_data data type is defined in such a way that
adding new fields specific to the generic PM domains code will
require include/linux/pm.h to be modified.  As a result, data types
used only by the generic PM domains code will be defined in two
headers, although they all should be defined in pm_domain.h and
pm.h will need to include more headers, which won't be very nice.

For this reason change the definition of struct pm_subsys_data
so that its domain_data member is a pointer, which will allow
struct pm_domain_data to be subclassed by various PM domains
implementations.  Remove the need_restore member from
struct pm_domain_data and make the generic PM domains code
subclass it by adding the need_restore member to the new data type.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |   28 +++++++++++++++++++---------
 include/linux/pm.h          |    3 +--
 include/linux/pm_domain.h   |   10 ++++++++++
 3 files changed, 30 insertions(+), 11 deletions(-)

Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -433,7 +433,6 @@ struct wakeup_source;
 struct pm_domain_data {
 	struct list_head list_node;
 	struct device *dev;
-	bool need_restore;
 };
 
 struct pm_subsys_data {
@@ -443,7 +442,7 @@ struct pm_subsys_data {
 	struct list_head clock_list;
 #endif
 #ifdef CONFIG_PM_GENERIC_DOMAINS
-	struct pm_domain_data domain_data;
+	struct pm_domain_data *domain_data;
 #endif
 };
 
Index: linux/include/linux/pm_domain.h
===================================================================
--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -62,6 +62,16 @@ struct gpd_link {
 	struct list_head slave_node;
 };
 
+struct generic_pm_domain_data {
+	struct pm_domain_data base;
+	bool need_restore;
+};
+
+static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data *pdd)
+{
+	return container_of(pdd, struct generic_pm_domain_data, base);
+}
+
 #ifdef CONFIG_PM_GENERIC_DOMAINS
 extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
 			       struct device *dev);
Index: linux/drivers/base/power/domain.c
===================================================================
--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -188,11 +188,12 @@ static int __pm_genpd_save_device(struct
 				  struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 	int ret = 0;
 
-	if (pdd->need_restore)
+	if (gpd_data->need_restore)
 		return 0;
 
 	mutex_unlock(&genpd->lock);
@@ -210,7 +211,7 @@ static int __pm_genpd_save_device(struct
 	mutex_lock(&genpd->lock);
 
 	if (!ret)
-		pdd->need_restore = true;
+		gpd_data->need_restore = true;
 
 	return ret;
 }
@@ -224,10 +225,11 @@ static void __pm_genpd_restore_device(st
 				      struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 
-	if (!pdd->need_restore)
+	if (!gpd_data->need_restore)
 		return;
 
 	mutex_unlock(&genpd->lock);
@@ -244,7 +246,7 @@ static void __pm_genpd_restore_device(st
 
 	mutex_lock(&genpd->lock);
 
-	pdd->need_restore = false;
+	gpd_data->need_restore = false;
 }
 
 /**
@@ -493,7 +495,7 @@ static int pm_genpd_runtime_resume(struc
 		mutex_lock(&genpd->lock);
 	}
 	finish_wait(&genpd->status_wait_queue, &wait);
-	__pm_genpd_restore_device(&dev->power.subsys_data->domain_data, genpd);
+	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
 	genpd->resume_count--;
 	genpd_set_active(genpd);
 	wake_up_all(&genpd->status_wait_queue);
@@ -1080,6 +1082,7 @@ static void pm_genpd_complete(struct dev
  */
 int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
 {
+	struct generic_pm_domain_data *gpd_data;
 	struct pm_domain_data *pdd;
 	int ret = 0;
 
@@ -1106,14 +1109,20 @@ int pm_genpd_add_device(struct generic_p
 			goto out;
 		}
 
+	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
+	if (!gpd_data) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
 	genpd->device_count++;
 
 	dev->pm_domain = &genpd->domain;
 	dev_pm_get_subsys_data(dev);
-	pdd = &dev->power.subsys_data->domain_data;
-	pdd->dev = dev;
-	pdd->need_restore = false;
-	list_add_tail(&pdd->list_node, &genpd->dev_list);
+	dev->power.subsys_data->domain_data = &gpd_data->base;
+	gpd_data->base.dev = dev;
+	gpd_data->need_restore = false;
+	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
 
  out:
 	genpd_release_lock(genpd);
@@ -1152,6 +1161,7 @@ int pm_genpd_remove_device(struct generi
 		pdd->dev = NULL;
 		dev_pm_put_subsys_data(dev);
 		dev->pm_domain = NULL;
+		kfree(to_gpd_data(pdd));
 
 		genpd->device_count--;
 


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

* [PATCH 1/5] PM / Domains: Split device PM domain data into base and need_restore
  2011-08-30 22:17 ` Rafael J. Wysocki
  (?)
@ 2011-08-30 22:18 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:18 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: LKML, Linux-sh list

From: Rafael J. Wysocki <rjw@sisk.pl>

The struct pm_domain_data data type is defined in such a way that
adding new fields specific to the generic PM domains code will
require include/linux/pm.h to be modified.  As a result, data types
used only by the generic PM domains code will be defined in two
headers, although they all should be defined in pm_domain.h and
pm.h will need to include more headers, which won't be very nice.

For this reason change the definition of struct pm_subsys_data
so that its domain_data member is a pointer, which will allow
struct pm_domain_data to be subclassed by various PM domains
implementations.  Remove the need_restore member from
struct pm_domain_data and make the generic PM domains code
subclass it by adding the need_restore member to the new data type.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |   28 +++++++++++++++++++---------
 include/linux/pm.h          |    3 +--
 include/linux/pm_domain.h   |   10 ++++++++++
 3 files changed, 30 insertions(+), 11 deletions(-)

Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -433,7 +433,6 @@ struct wakeup_source;
 struct pm_domain_data {
 	struct list_head list_node;
 	struct device *dev;
-	bool need_restore;
 };
 
 struct pm_subsys_data {
@@ -443,7 +442,7 @@ struct pm_subsys_data {
 	struct list_head clock_list;
 #endif
 #ifdef CONFIG_PM_GENERIC_DOMAINS
-	struct pm_domain_data domain_data;
+	struct pm_domain_data *domain_data;
 #endif
 };
 
Index: linux/include/linux/pm_domain.h
===================================================================
--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -62,6 +62,16 @@ struct gpd_link {
 	struct list_head slave_node;
 };
 
+struct generic_pm_domain_data {
+	struct pm_domain_data base;
+	bool need_restore;
+};
+
+static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data *pdd)
+{
+	return container_of(pdd, struct generic_pm_domain_data, base);
+}
+
 #ifdef CONFIG_PM_GENERIC_DOMAINS
 extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
 			       struct device *dev);
Index: linux/drivers/base/power/domain.c
===================================================================
--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -188,11 +188,12 @@ static int __pm_genpd_save_device(struct
 				  struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 	int ret = 0;
 
-	if (pdd->need_restore)
+	if (gpd_data->need_restore)
 		return 0;
 
 	mutex_unlock(&genpd->lock);
@@ -210,7 +211,7 @@ static int __pm_genpd_save_device(struct
 	mutex_lock(&genpd->lock);
 
 	if (!ret)
-		pdd->need_restore = true;
+		gpd_data->need_restore = true;
 
 	return ret;
 }
@@ -224,10 +225,11 @@ static void __pm_genpd_restore_device(st
 				      struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 
-	if (!pdd->need_restore)
+	if (!gpd_data->need_restore)
 		return;
 
 	mutex_unlock(&genpd->lock);
@@ -244,7 +246,7 @@ static void __pm_genpd_restore_device(st
 
 	mutex_lock(&genpd->lock);
 
-	pdd->need_restore = false;
+	gpd_data->need_restore = false;
 }
 
 /**
@@ -493,7 +495,7 @@ static int pm_genpd_runtime_resume(struc
 		mutex_lock(&genpd->lock);
 	}
 	finish_wait(&genpd->status_wait_queue, &wait);
-	__pm_genpd_restore_device(&dev->power.subsys_data->domain_data, genpd);
+	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
 	genpd->resume_count--;
 	genpd_set_active(genpd);
 	wake_up_all(&genpd->status_wait_queue);
@@ -1080,6 +1082,7 @@ static void pm_genpd_complete(struct dev
  */
 int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
 {
+	struct generic_pm_domain_data *gpd_data;
 	struct pm_domain_data *pdd;
 	int ret = 0;
 
@@ -1106,14 +1109,20 @@ int pm_genpd_add_device(struct generic_p
 			goto out;
 		}
 
+	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
+	if (!gpd_data) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
 	genpd->device_count++;
 
 	dev->pm_domain = &genpd->domain;
 	dev_pm_get_subsys_data(dev);
-	pdd = &dev->power.subsys_data->domain_data;
-	pdd->dev = dev;
-	pdd->need_restore = false;
-	list_add_tail(&pdd->list_node, &genpd->dev_list);
+	dev->power.subsys_data->domain_data = &gpd_data->base;
+	gpd_data->base.dev = dev;
+	gpd_data->need_restore = false;
+	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
 
  out:
 	genpd_release_lock(genpd);
@@ -1152,6 +1161,7 @@ int pm_genpd_remove_device(struct generi
 		pdd->dev = NULL;
 		dev_pm_put_subsys_data(dev);
 		dev->pm_domain = NULL;
+		kfree(to_gpd_data(pdd));
 
 		genpd->device_count--;
 

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

* [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
  2011-08-30 22:17 ` Rafael J. Wysocki
@ 2011-08-30 22:20   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:20 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

The rpm_suspend() and rpm_resume() routines execute subsystem or PM
domain callbacks under power.lock if power.irq_safe is set for the
given device.  This is inconsistent with that rpm_idle() does after
commit 02b2677 (PM / Runtime: Allow _put_sync() from
interrupts-disabled context) and is problematic for subsystems and PM
domains wanting to use power.lock for synchronization in their
runtime PM callbacks.  For this reason, make runtime PM core functions
always release power.lock before invoking subsystem or PM domain
callbacks.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/runtime.c |   50 ++++++++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

Index: linux/drivers/base/power/runtime.c
=================================--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
 }
 
 /**
+ * __rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	int retval;
+
+	if (dev->power.irq_safe)
+		spin_unlock(&dev->power.lock);
+	else
+		spin_unlock_irq(&dev->power.lock);
+
+	retval = cb(dev);
+
+	if (dev->power.irq_safe)
+		spin_lock(&dev->power.lock);
+	else
+		spin_lock_irq(&dev->power.lock);
+
+	return retval;
+}
+
+/**
  * rpm_idle - Notify device bus type if the device can be suspended.
  * @dev: Device to notify the bus type about.
  * @rpmflags: Flag bits.
@@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
 	else
 		callback = NULL;
 
-	if (callback) {
-		if (dev->power.irq_safe)
-			spin_unlock(&dev->power.lock);
-		else
-			spin_unlock_irq(&dev->power.lock);
-
-		callback(dev);
-
-		if (dev->power.irq_safe)
-			spin_lock(&dev->power.lock);
-		else
-			spin_lock_irq(&dev->power.lock);
-	}
+	if (callback)
+		__rpm_callback(callback, dev);
 
 	dev->power.idle_notification = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
  * @dev: Device to run the callback for.
  */
 static int rpm_callback(int (*cb)(struct device *), struct device *dev)
-	__releases(&dev->power.lock) __acquires(&dev->power.lock)
 {
 	int retval;
 
 	if (!cb)
 		return -ENOSYS;
 
-	if (dev->power.irq_safe) {
-		retval = cb(dev);
-	} else {
-		spin_unlock_irq(&dev->power.lock);
-
-		retval = cb(dev);
+	retval = __rpm_callback(cb, dev);
 
-		spin_lock_irq(&dev->power.lock);
-	}
 	dev->power.runtime_error = retval;
 	return retval != -EACCES ? retval : -EIO;
 }


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

* [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
@ 2011-08-30 22:20   ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:20 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

The rpm_suspend() and rpm_resume() routines execute subsystem or PM
domain callbacks under power.lock if power.irq_safe is set for the
given device.  This is inconsistent with that rpm_idle() does after
commit 02b2677 (PM / Runtime: Allow _put_sync() from
interrupts-disabled context) and is problematic for subsystems and PM
domains wanting to use power.lock for synchronization in their
runtime PM callbacks.  For this reason, make runtime PM core functions
always release power.lock before invoking subsystem or PM domain
callbacks.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/runtime.c |   50 ++++++++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

Index: linux/drivers/base/power/runtime.c
===================================================================
--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
 }
 
 /**
+ * __rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	int retval;
+
+	if (dev->power.irq_safe)
+		spin_unlock(&dev->power.lock);
+	else
+		spin_unlock_irq(&dev->power.lock);
+
+	retval = cb(dev);
+
+	if (dev->power.irq_safe)
+		spin_lock(&dev->power.lock);
+	else
+		spin_lock_irq(&dev->power.lock);
+
+	return retval;
+}
+
+/**
  * rpm_idle - Notify device bus type if the device can be suspended.
  * @dev: Device to notify the bus type about.
  * @rpmflags: Flag bits.
@@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
 	else
 		callback = NULL;
 
-	if (callback) {
-		if (dev->power.irq_safe)
-			spin_unlock(&dev->power.lock);
-		else
-			spin_unlock_irq(&dev->power.lock);
-
-		callback(dev);
-
-		if (dev->power.irq_safe)
-			spin_lock(&dev->power.lock);
-		else
-			spin_lock_irq(&dev->power.lock);
-	}
+	if (callback)
+		__rpm_callback(callback, dev);
 
 	dev->power.idle_notification = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
  * @dev: Device to run the callback for.
  */
 static int rpm_callback(int (*cb)(struct device *), struct device *dev)
-	__releases(&dev->power.lock) __acquires(&dev->power.lock)
 {
 	int retval;
 
 	if (!cb)
 		return -ENOSYS;
 
-	if (dev->power.irq_safe) {
-		retval = cb(dev);
-	} else {
-		spin_unlock_irq(&dev->power.lock);
-
-		retval = cb(dev);
+	retval = __rpm_callback(cb, dev);
 
-		spin_lock_irq(&dev->power.lock);
-	}
 	dev->power.runtime_error = retval;
 	return retval != -EACCES ? retval : -EIO;
 }


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

* [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
  2011-08-30 22:17 ` Rafael J. Wysocki
                   ` (3 preceding siblings ...)
  (?)
@ 2011-08-30 22:20 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:20 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: LKML, Linux-sh list

From: Rafael J. Wysocki <rjw@sisk.pl>

The rpm_suspend() and rpm_resume() routines execute subsystem or PM
domain callbacks under power.lock if power.irq_safe is set for the
given device.  This is inconsistent with that rpm_idle() does after
commit 02b2677 (PM / Runtime: Allow _put_sync() from
interrupts-disabled context) and is problematic for subsystems and PM
domains wanting to use power.lock for synchronization in their
runtime PM callbacks.  For this reason, make runtime PM core functions
always release power.lock before invoking subsystem or PM domain
callbacks.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/runtime.c |   50 ++++++++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

Index: linux/drivers/base/power/runtime.c
===================================================================
--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
 }
 
 /**
+ * __rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	int retval;
+
+	if (dev->power.irq_safe)
+		spin_unlock(&dev->power.lock);
+	else
+		spin_unlock_irq(&dev->power.lock);
+
+	retval = cb(dev);
+
+	if (dev->power.irq_safe)
+		spin_lock(&dev->power.lock);
+	else
+		spin_lock_irq(&dev->power.lock);
+
+	return retval;
+}
+
+/**
  * rpm_idle - Notify device bus type if the device can be suspended.
  * @dev: Device to notify the bus type about.
  * @rpmflags: Flag bits.
@@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
 	else
 		callback = NULL;
 
-	if (callback) {
-		if (dev->power.irq_safe)
-			spin_unlock(&dev->power.lock);
-		else
-			spin_unlock_irq(&dev->power.lock);
-
-		callback(dev);
-
-		if (dev->power.irq_safe)
-			spin_lock(&dev->power.lock);
-		else
-			spin_lock_irq(&dev->power.lock);
-	}
+	if (callback)
+		__rpm_callback(callback, dev);
 
 	dev->power.idle_notification = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
  * @dev: Device to run the callback for.
  */
 static int rpm_callback(int (*cb)(struct device *), struct device *dev)
-	__releases(&dev->power.lock) __acquires(&dev->power.lock)
 {
 	int retval;
 
 	if (!cb)
 		return -ENOSYS;
 
-	if (dev->power.irq_safe) {
-		retval = cb(dev);
-	} else {
-		spin_unlock_irq(&dev->power.lock);
-
-		retval = cb(dev);
+	retval = __rpm_callback(cb, dev);
 
-		spin_lock_irq(&dev->power.lock);
-	}
 	dev->power.runtime_error = retval;
 	return retval != -EACCES ? retval : -EIO;
 }

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

* [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-08-30 22:17 ` Rafael J. Wysocki
@ 2011-08-30 22:21   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:21 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 101 insertions(+), 81 deletions(-)

Index: linux/drivers/base/power/qos.c
=================================--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,13 +283,13 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
 	} else {
 		/* Return if the device has been removed */
-		ret = -ENODEV;
+		ret = -EINVAL;
 	}
 
 	mutex_unlock(&dev_pm_qos_mtx);
@@ -293,7 +324,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +354,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +382,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
=================================--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
=================================--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);


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

* [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
@ 2011-08-30 22:21   ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:21 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 101 insertions(+), 81 deletions(-)

Index: linux/drivers/base/power/qos.c
===================================================================
--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,13 +283,13 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
 	} else {
 		/* Return if the device has been removed */
-		ret = -ENODEV;
+		ret = -EINVAL;
 	}
 
 	mutex_unlock(&dev_pm_qos_mtx);
@@ -293,7 +324,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +354,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +382,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
===================================================================
--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
===================================================================
--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);


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

* [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-08-30 22:17 ` Rafael J. Wysocki
                   ` (4 preceding siblings ...)
  (?)
@ 2011-08-30 22:21 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:21 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: LKML, Linux-sh list

From: Rafael J. Wysocki <rjw@sisk.pl>

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 101 insertions(+), 81 deletions(-)

Index: linux/drivers/base/power/qos.c
===================================================================
--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,13 +283,13 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
 	} else {
 		/* Return if the device has been removed */
-		ret = -ENODEV;
+		ret = -EINVAL;
 	}
 
 	mutex_unlock(&dev_pm_qos_mtx);
@@ -293,7 +324,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +354,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +382,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
===================================================================
--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
===================================================================
--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);

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

* [RFC][PATCH 4/5] PM / Domains: Add device stop governor function
  2011-08-30 22:17 ` Rafael J. Wysocki
@ 2011-08-30 22:21   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:21 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

Add a function deciding whether or not devices should be
stopped in pm_genpd_runtime_suspend() depending on their
PM QoS values.

---
 arch/arm/mach-shmobile/pm-sh7372.c   |    2 -
 drivers/base/power/Makefile          |    2 -
 drivers/base/power/domain.c          |   35 ++++++++++++++++++-----
 drivers/base/power/domain_governor.c |   40 ++++++++++++++++++++++++++
 include/linux/pm_domain.h            |   52 ++++++++++++++++++++++++++++++-----
 5 files changed, 115 insertions(+), 16 deletions(-)

Index: linux/include/linux/pm_domain.h
=================================--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -21,6 +21,7 @@ enum gpd_status {
 
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
+	bool (*stop_ok)(struct device *dev);
 };
 
 struct generic_pm_domain {
@@ -62,8 +63,13 @@ struct gpd_link {
 	struct list_head slave_node;
 };
 
+struct gpd_gov_dev_data {
+	s64 break_even_ns;
+};
+
 struct generic_pm_domain_data {
 	struct pm_domain_data base;
+	struct gpd_gov_dev_data *gov_data;
 	bool need_restore;
 };
 
@@ -73,18 +79,47 @@ static inline struct generic_pm_domain_d
 }
 
 #ifdef CONFIG_PM_GENERIC_DOMAINS
-extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
-			       struct device *dev);
+extern struct dev_power_governor default_qos_governor;
+
+extern struct generic_pm_domain *dev_to_genpd(struct device *dev);
+extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
+				 struct device *dev,
+				 struct gpd_gov_dev_data *gov_data);
+
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return __pm_genpd_add_device(genpd, dev, NULL);
+}
+
 extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
 				  struct device *dev);
 extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
 				  struct generic_pm_domain *new_subdomain);
 extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
 				     struct generic_pm_domain *target);
-extern void pm_genpd_init(struct generic_pm_domain *genpd,
-			  struct dev_power_governor *gov, bool is_off);
+extern void __pm_genpd_init(struct generic_pm_domain *genpd,
+			    struct dev_power_governor *gov, bool is_off);
+
+static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off)
+{
+	__pm_genpd_init(genpd, &default_qos_governor, is_off);
+}
+
 extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
+
 #else
+
+static inline struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+	return ERR_PTR(-ENOSYS);
+}
+static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd,
+					struct device *dev,
+					struct gpd_gov_dev_data *gov_data)
+{
+	return -ENOSYS;
+}
 static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
 				      struct device *dev)
 {
@@ -105,8 +140,13 @@ static inline int pm_genpd_remove_subdom
 {
 	return -ENOSYS;
 }
-static inline void pm_genpd_init(struct generic_pm_domain *genpd,
-				 struct dev_power_governor *gov, bool is_off) {}
+static inline void __pm_genpd_init(struct generic_pm_domain *genpd,
+				   struct dev_power_governor *gov, bool is_off)
+{
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off)
+{
+}
 static inline int pm_genpd_poweron(struct generic_pm_domain *genpd)
 {
 	return -ENOSYS;
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static DEFINE_MUTEX(gpd_list_lock);
 
 #ifdef CONFIG_PM
 
-static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+struct generic_pm_domain *dev_to_genpd(struct device *dev)
 {
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
@@ -403,6 +403,22 @@ static void genpd_power_off_work_fn(stru
 }
 
 /**
+ * genpd_stop_dev - Stop a given device if that's beneficial.
+ * @genpd: PM domain the device belongs to.
+ * @dev: Device to stop.
+ */
+static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
+{
+	bool (*stop_ok)(struct device *dev);
+
+	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
+	if (stop_ok && !stop_ok(dev))
+		return -EBUSY;
+
+	return genpd->stop_device(dev);
+}
+
+/**
  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
  * @dev: Device to suspend.
  *
@@ -423,7 +439,7 @@ static int pm_genpd_runtime_suspend(stru
 	might_sleep_if(!genpd->dev_irq_safe);
 
 	if (genpd->stop_device) {
-		int ret = genpd->stop_device(dev);
+		int ret = genpd_stop_dev(genpd, dev);
 		if (ret)
 			return ret;
 	}
@@ -495,7 +511,7 @@ static int pm_genpd_runtime_resume(struc
 		mutex_lock(&genpd->lock);
 	}
 	finish_wait(&genpd->status_wait_queue, &wait);
-	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
+	__pm_genpd_restore_device(dev_to_psd(dev)->domain_data, genpd);
 	genpd->resume_count--;
 	genpd_set_active(genpd);
 	wake_up_all(&genpd->status_wait_queue);
@@ -1076,11 +1092,13 @@ static void pm_genpd_complete(struct dev
 #endif /* CONFIG_PM_SLEEP */
 
 /**
- * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * __pm_genpd_add_device - Add a device to an I/O PM domain.
  * @genpd: PM domain to add the device to.
  * @dev: Device to be added.
+ * @gov_data: Set of PM QoS parameters to attach to the device.
  */
-int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
+			  struct gpd_gov_dev_data *gov_data)
 {
 	struct generic_pm_domain_data *gpd_data;
 	struct pm_domain_data *pdd;
@@ -1123,6 +1141,7 @@ int pm_genpd_add_device(struct generic_p
 	gpd_data->base.dev = dev;
 	gpd_data->need_restore = false;
 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
+	gpd_data->gov_data = gov_data;
 
  out:
 	genpd_release_lock(genpd);
@@ -1280,13 +1299,13 @@ int pm_genpd_remove_subdomain(struct gen
 }
 
 /**
- * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * __pm_genpd_init - Initialize a generic I/O PM domain object.
  * @genpd: PM domain object to initialize.
  * @gov: PM domain governor to associate with the domain (may be NULL).
  * @is_off: Initial value of the domain's power_is_off field.
  */
-void pm_genpd_init(struct generic_pm_domain *genpd,
-		   struct dev_power_governor *gov, bool is_off)
+void __pm_genpd_init(struct generic_pm_domain *genpd,
+		     struct dev_power_governor *gov, bool is_off)
 {
 	if (IS_ERR_OR_NULL(genpd))
 		return;
Index: linux/drivers/base/power/Makefile
=================================--- linux.orig/drivers/base/power/Makefile
+++ linux/drivers/base/power/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_PM_SLEEP)	+= main.o wakeup.
 obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 obj-$(CONFIG_PM_OPP)	+= opp.o
-obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o
+obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o domain_governor.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
Index: linux/drivers/base/power/domain_governor.c
=================================--- /dev/null
+++ linux/drivers/base/power/domain_governor.c
@@ -0,0 +1,40 @@
+/*
+ * drivers/base/power/domain_governor.c - Governors for device PM domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_qos.h>
+
+static bool default_stop_ok(struct device *dev)
+{
+	struct gpd_gov_dev_data *gov_data;
+	s64 constraint_ns;
+	s32 constraint;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	gov_data = to_gpd_data(dev_to_psd(dev)->domain_data)->gov_data;
+	if (!gov_data)
+		return true;
+
+	constraint = dev_pm_qos_read_value(dev);
+	if (constraint < 0)
+		return false;
+	else if (constraint = 0) /* 0 means "don't care" */
+		return true;
+
+	constraint_ns = constraint;
+	constraint_ns *= NSEC_PER_USEC;
+
+	return constraint_ns > gov_data->break_even_ns;
+}
+
+struct dev_power_governor default_qos_governor = {
+	.stop_ok = default_stop_ok,
+};
Index: linux/arch/arm/mach-shmobile/pm-sh7372.c
=================================--- linux.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux/arch/arm/mach-shmobile/pm-sh7372.c
@@ -100,7 +100,7 @@ void sh7372_init_pm_domain(struct sh7372
 {
 	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
 
-	pm_genpd_init(genpd, NULL, false);
+	pm_genpd_init(genpd, false);
 	genpd->stop_device = pm_clk_suspend;
 	genpd->start_device = pm_clk_resume;
 	genpd->dev_irq_safe = true;


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

* [RFC][PATCH 4/5] PM / Domains: Add device stop governor function
@ 2011-08-30 22:21   ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:21 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

Add a function deciding whether or not devices should be
stopped in pm_genpd_runtime_suspend() depending on their
PM QoS values.

---
 arch/arm/mach-shmobile/pm-sh7372.c   |    2 -
 drivers/base/power/Makefile          |    2 -
 drivers/base/power/domain.c          |   35 ++++++++++++++++++-----
 drivers/base/power/domain_governor.c |   40 ++++++++++++++++++++++++++
 include/linux/pm_domain.h            |   52 ++++++++++++++++++++++++++++++-----
 5 files changed, 115 insertions(+), 16 deletions(-)

Index: linux/include/linux/pm_domain.h
===================================================================
--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -21,6 +21,7 @@ enum gpd_status {
 
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
+	bool (*stop_ok)(struct device *dev);
 };
 
 struct generic_pm_domain {
@@ -62,8 +63,13 @@ struct gpd_link {
 	struct list_head slave_node;
 };
 
+struct gpd_gov_dev_data {
+	s64 break_even_ns;
+};
+
 struct generic_pm_domain_data {
 	struct pm_domain_data base;
+	struct gpd_gov_dev_data *gov_data;
 	bool need_restore;
 };
 
@@ -73,18 +79,47 @@ static inline struct generic_pm_domain_d
 }
 
 #ifdef CONFIG_PM_GENERIC_DOMAINS
-extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
-			       struct device *dev);
+extern struct dev_power_governor default_qos_governor;
+
+extern struct generic_pm_domain *dev_to_genpd(struct device *dev);
+extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
+				 struct device *dev,
+				 struct gpd_gov_dev_data *gov_data);
+
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return __pm_genpd_add_device(genpd, dev, NULL);
+}
+
 extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
 				  struct device *dev);
 extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
 				  struct generic_pm_domain *new_subdomain);
 extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
 				     struct generic_pm_domain *target);
-extern void pm_genpd_init(struct generic_pm_domain *genpd,
-			  struct dev_power_governor *gov, bool is_off);
+extern void __pm_genpd_init(struct generic_pm_domain *genpd,
+			    struct dev_power_governor *gov, bool is_off);
+
+static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off)
+{
+	__pm_genpd_init(genpd, &default_qos_governor, is_off);
+}
+
 extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
+
 #else
+
+static inline struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+	return ERR_PTR(-ENOSYS);
+}
+static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd,
+					struct device *dev,
+					struct gpd_gov_dev_data *gov_data)
+{
+	return -ENOSYS;
+}
 static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
 				      struct device *dev)
 {
@@ -105,8 +140,13 @@ static inline int pm_genpd_remove_subdom
 {
 	return -ENOSYS;
 }
-static inline void pm_genpd_init(struct generic_pm_domain *genpd,
-				 struct dev_power_governor *gov, bool is_off) {}
+static inline void __pm_genpd_init(struct generic_pm_domain *genpd,
+				   struct dev_power_governor *gov, bool is_off)
+{
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off)
+{
+}
 static inline int pm_genpd_poweron(struct generic_pm_domain *genpd)
 {
 	return -ENOSYS;
Index: linux/drivers/base/power/domain.c
===================================================================
--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static DEFINE_MUTEX(gpd_list_lock);
 
 #ifdef CONFIG_PM
 
-static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+struct generic_pm_domain *dev_to_genpd(struct device *dev)
 {
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
@@ -403,6 +403,22 @@ static void genpd_power_off_work_fn(stru
 }
 
 /**
+ * genpd_stop_dev - Stop a given device if that's beneficial.
+ * @genpd: PM domain the device belongs to.
+ * @dev: Device to stop.
+ */
+static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
+{
+	bool (*stop_ok)(struct device *dev);
+
+	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
+	if (stop_ok && !stop_ok(dev))
+		return -EBUSY;
+
+	return genpd->stop_device(dev);
+}
+
+/**
  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
  * @dev: Device to suspend.
  *
@@ -423,7 +439,7 @@ static int pm_genpd_runtime_suspend(stru
 	might_sleep_if(!genpd->dev_irq_safe);
 
 	if (genpd->stop_device) {
-		int ret = genpd->stop_device(dev);
+		int ret = genpd_stop_dev(genpd, dev);
 		if (ret)
 			return ret;
 	}
@@ -495,7 +511,7 @@ static int pm_genpd_runtime_resume(struc
 		mutex_lock(&genpd->lock);
 	}
 	finish_wait(&genpd->status_wait_queue, &wait);
-	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
+	__pm_genpd_restore_device(dev_to_psd(dev)->domain_data, genpd);
 	genpd->resume_count--;
 	genpd_set_active(genpd);
 	wake_up_all(&genpd->status_wait_queue);
@@ -1076,11 +1092,13 @@ static void pm_genpd_complete(struct dev
 #endif /* CONFIG_PM_SLEEP */
 
 /**
- * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * __pm_genpd_add_device - Add a device to an I/O PM domain.
  * @genpd: PM domain to add the device to.
  * @dev: Device to be added.
+ * @gov_data: Set of PM QoS parameters to attach to the device.
  */
-int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
+			  struct gpd_gov_dev_data *gov_data)
 {
 	struct generic_pm_domain_data *gpd_data;
 	struct pm_domain_data *pdd;
@@ -1123,6 +1141,7 @@ int pm_genpd_add_device(struct generic_p
 	gpd_data->base.dev = dev;
 	gpd_data->need_restore = false;
 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
+	gpd_data->gov_data = gov_data;
 
  out:
 	genpd_release_lock(genpd);
@@ -1280,13 +1299,13 @@ int pm_genpd_remove_subdomain(struct gen
 }
 
 /**
- * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * __pm_genpd_init - Initialize a generic I/O PM domain object.
  * @genpd: PM domain object to initialize.
  * @gov: PM domain governor to associate with the domain (may be NULL).
  * @is_off: Initial value of the domain's power_is_off field.
  */
-void pm_genpd_init(struct generic_pm_domain *genpd,
-		   struct dev_power_governor *gov, bool is_off)
+void __pm_genpd_init(struct generic_pm_domain *genpd,
+		     struct dev_power_governor *gov, bool is_off)
 {
 	if (IS_ERR_OR_NULL(genpd))
 		return;
Index: linux/drivers/base/power/Makefile
===================================================================
--- linux.orig/drivers/base/power/Makefile
+++ linux/drivers/base/power/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_PM_SLEEP)	+= main.o wakeup.
 obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 obj-$(CONFIG_PM_OPP)	+= opp.o
-obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o
+obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o domain_governor.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
Index: linux/drivers/base/power/domain_governor.c
===================================================================
--- /dev/null
+++ linux/drivers/base/power/domain_governor.c
@@ -0,0 +1,40 @@
+/*
+ * drivers/base/power/domain_governor.c - Governors for device PM domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_qos.h>
+
+static bool default_stop_ok(struct device *dev)
+{
+	struct gpd_gov_dev_data *gov_data;
+	s64 constraint_ns;
+	s32 constraint;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	gov_data = to_gpd_data(dev_to_psd(dev)->domain_data)->gov_data;
+	if (!gov_data)
+		return true;
+
+	constraint = dev_pm_qos_read_value(dev);
+	if (constraint < 0)
+		return false;
+	else if (constraint == 0) /* 0 means "don't care" */
+		return true;
+
+	constraint_ns = constraint;
+	constraint_ns *= NSEC_PER_USEC;
+
+	return constraint_ns > gov_data->break_even_ns;
+}
+
+struct dev_power_governor default_qos_governor = {
+	.stop_ok = default_stop_ok,
+};
Index: linux/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux/arch/arm/mach-shmobile/pm-sh7372.c
@@ -100,7 +100,7 @@ void sh7372_init_pm_domain(struct sh7372
 {
 	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
 
-	pm_genpd_init(genpd, NULL, false);
+	pm_genpd_init(genpd, false);
 	genpd->stop_device = pm_clk_suspend;
 	genpd->start_device = pm_clk_resume;
 	genpd->dev_irq_safe = true;


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

* [RFC][PATCH 4/5] PM / Domains: Add device stop governor function
  2011-08-30 22:17 ` Rafael J. Wysocki
                   ` (6 preceding siblings ...)
  (?)
@ 2011-08-30 22:21 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:21 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: LKML, Linux-sh list

From: Rafael J. Wysocki <rjw@sisk.pl>

Add a function deciding whether or not devices should be
stopped in pm_genpd_runtime_suspend() depending on their
PM QoS values.

---
 arch/arm/mach-shmobile/pm-sh7372.c   |    2 -
 drivers/base/power/Makefile          |    2 -
 drivers/base/power/domain.c          |   35 ++++++++++++++++++-----
 drivers/base/power/domain_governor.c |   40 ++++++++++++++++++++++++++
 include/linux/pm_domain.h            |   52 ++++++++++++++++++++++++++++++-----
 5 files changed, 115 insertions(+), 16 deletions(-)

Index: linux/include/linux/pm_domain.h
===================================================================
--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -21,6 +21,7 @@ enum gpd_status {
 
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
+	bool (*stop_ok)(struct device *dev);
 };
 
 struct generic_pm_domain {
@@ -62,8 +63,13 @@ struct gpd_link {
 	struct list_head slave_node;
 };
 
+struct gpd_gov_dev_data {
+	s64 break_even_ns;
+};
+
 struct generic_pm_domain_data {
 	struct pm_domain_data base;
+	struct gpd_gov_dev_data *gov_data;
 	bool need_restore;
 };
 
@@ -73,18 +79,47 @@ static inline struct generic_pm_domain_d
 }
 
 #ifdef CONFIG_PM_GENERIC_DOMAINS
-extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
-			       struct device *dev);
+extern struct dev_power_governor default_qos_governor;
+
+extern struct generic_pm_domain *dev_to_genpd(struct device *dev);
+extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
+				 struct device *dev,
+				 struct gpd_gov_dev_data *gov_data);
+
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return __pm_genpd_add_device(genpd, dev, NULL);
+}
+
 extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
 				  struct device *dev);
 extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
 				  struct generic_pm_domain *new_subdomain);
 extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
 				     struct generic_pm_domain *target);
-extern void pm_genpd_init(struct generic_pm_domain *genpd,
-			  struct dev_power_governor *gov, bool is_off);
+extern void __pm_genpd_init(struct generic_pm_domain *genpd,
+			    struct dev_power_governor *gov, bool is_off);
+
+static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off)
+{
+	__pm_genpd_init(genpd, &default_qos_governor, is_off);
+}
+
 extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
+
 #else
+
+static inline struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+	return ERR_PTR(-ENOSYS);
+}
+static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd,
+					struct device *dev,
+					struct gpd_gov_dev_data *gov_data)
+{
+	return -ENOSYS;
+}
 static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
 				      struct device *dev)
 {
@@ -105,8 +140,13 @@ static inline int pm_genpd_remove_subdom
 {
 	return -ENOSYS;
 }
-static inline void pm_genpd_init(struct generic_pm_domain *genpd,
-				 struct dev_power_governor *gov, bool is_off) {}
+static inline void __pm_genpd_init(struct generic_pm_domain *genpd,
+				   struct dev_power_governor *gov, bool is_off)
+{
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off)
+{
+}
 static inline int pm_genpd_poweron(struct generic_pm_domain *genpd)
 {
 	return -ENOSYS;
Index: linux/drivers/base/power/domain.c
===================================================================
--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static DEFINE_MUTEX(gpd_list_lock);
 
 #ifdef CONFIG_PM
 
-static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+struct generic_pm_domain *dev_to_genpd(struct device *dev)
 {
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
@@ -403,6 +403,22 @@ static void genpd_power_off_work_fn(stru
 }
 
 /**
+ * genpd_stop_dev - Stop a given device if that's beneficial.
+ * @genpd: PM domain the device belongs to.
+ * @dev: Device to stop.
+ */
+static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
+{
+	bool (*stop_ok)(struct device *dev);
+
+	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
+	if (stop_ok && !stop_ok(dev))
+		return -EBUSY;
+
+	return genpd->stop_device(dev);
+}
+
+/**
  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
  * @dev: Device to suspend.
  *
@@ -423,7 +439,7 @@ static int pm_genpd_runtime_suspend(stru
 	might_sleep_if(!genpd->dev_irq_safe);
 
 	if (genpd->stop_device) {
-		int ret = genpd->stop_device(dev);
+		int ret = genpd_stop_dev(genpd, dev);
 		if (ret)
 			return ret;
 	}
@@ -495,7 +511,7 @@ static int pm_genpd_runtime_resume(struc
 		mutex_lock(&genpd->lock);
 	}
 	finish_wait(&genpd->status_wait_queue, &wait);
-	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
+	__pm_genpd_restore_device(dev_to_psd(dev)->domain_data, genpd);
 	genpd->resume_count--;
 	genpd_set_active(genpd);
 	wake_up_all(&genpd->status_wait_queue);
@@ -1076,11 +1092,13 @@ static void pm_genpd_complete(struct dev
 #endif /* CONFIG_PM_SLEEP */
 
 /**
- * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * __pm_genpd_add_device - Add a device to an I/O PM domain.
  * @genpd: PM domain to add the device to.
  * @dev: Device to be added.
+ * @gov_data: Set of PM QoS parameters to attach to the device.
  */
-int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
+			  struct gpd_gov_dev_data *gov_data)
 {
 	struct generic_pm_domain_data *gpd_data;
 	struct pm_domain_data *pdd;
@@ -1123,6 +1141,7 @@ int pm_genpd_add_device(struct generic_p
 	gpd_data->base.dev = dev;
 	gpd_data->need_restore = false;
 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
+	gpd_data->gov_data = gov_data;
 
  out:
 	genpd_release_lock(genpd);
@@ -1280,13 +1299,13 @@ int pm_genpd_remove_subdomain(struct gen
 }
 
 /**
- * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * __pm_genpd_init - Initialize a generic I/O PM domain object.
  * @genpd: PM domain object to initialize.
  * @gov: PM domain governor to associate with the domain (may be NULL).
  * @is_off: Initial value of the domain's power_is_off field.
  */
-void pm_genpd_init(struct generic_pm_domain *genpd,
-		   struct dev_power_governor *gov, bool is_off)
+void __pm_genpd_init(struct generic_pm_domain *genpd,
+		     struct dev_power_governor *gov, bool is_off)
 {
 	if (IS_ERR_OR_NULL(genpd))
 		return;
Index: linux/drivers/base/power/Makefile
===================================================================
--- linux.orig/drivers/base/power/Makefile
+++ linux/drivers/base/power/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_PM_SLEEP)	+= main.o wakeup.
 obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 obj-$(CONFIG_PM_OPP)	+= opp.o
-obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o
+obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o domain_governor.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
Index: linux/drivers/base/power/domain_governor.c
===================================================================
--- /dev/null
+++ linux/drivers/base/power/domain_governor.c
@@ -0,0 +1,40 @@
+/*
+ * drivers/base/power/domain_governor.c - Governors for device PM domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_qos.h>
+
+static bool default_stop_ok(struct device *dev)
+{
+	struct gpd_gov_dev_data *gov_data;
+	s64 constraint_ns;
+	s32 constraint;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	gov_data = to_gpd_data(dev_to_psd(dev)->domain_data)->gov_data;
+	if (!gov_data)
+		return true;
+
+	constraint = dev_pm_qos_read_value(dev);
+	if (constraint < 0)
+		return false;
+	else if (constraint == 0) /* 0 means "don't care" */
+		return true;
+
+	constraint_ns = constraint;
+	constraint_ns *= NSEC_PER_USEC;
+
+	return constraint_ns > gov_data->break_even_ns;
+}
+
+struct dev_power_governor default_qos_governor = {
+	.stop_ok = default_stop_ok,
+};
Index: linux/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux/arch/arm/mach-shmobile/pm-sh7372.c
@@ -100,7 +100,7 @@ void sh7372_init_pm_domain(struct sh7372
 {
 	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
 
-	pm_genpd_init(genpd, NULL, false);
+	pm_genpd_init(genpd, false);
 	genpd->stop_device = pm_clk_suspend;
 	genpd->start_device = pm_clk_resume;
 	genpd->dev_irq_safe = true;

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

* [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
  2011-08-30 22:17 ` Rafael J. Wysocki
@ 2011-08-30 22:22   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:22 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

Add a function deciding whether or not a given PM domain should
be powered off on the basis of that domain's devices' PM QoS
constraints.

---
 drivers/base/power/domain_governor.c |   96 +++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h            |    7 ++
 2 files changed, 103 insertions(+)

Index: linux/include/linux/pm_domain.h
=================================--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -49,6 +49,10 @@ struct generic_pm_domain {
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 	bool (*active_wakeup)(struct device *dev);
+	ktime_t power_off_latency;
+	ktime_t power_on_latency;
+	s64 break_even_ns;
+	s64 min_delta_ns;
 };
 
 static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
@@ -64,6 +68,9 @@ struct gpd_link {
 };
 
 struct gpd_gov_dev_data {
+	ktime_t start_latency;
+	ktime_t suspend_latency;
+	ktime_t resume_latency;
 	s64 break_even_ns;
 };
 
Index: linux/drivers/base/power/domain_governor.c
=================================--- linux.orig/drivers/base/power/domain_governor.c
+++ linux/drivers/base/power/domain_governor.c
@@ -35,6 +35,102 @@ static bool default_stop_ok(struct devic
 	return constraint_ns > gov_data->break_even_ns;
 }
 
+/* This routine must be executed under the PM domain's lock. */
+static bool default_power_down_ok(struct dev_pm_domain *pd)
+{
+	struct generic_pm_domain *genpd = pd_to_genpd(pd);
+	struct gpd_link *link;
+	struct pm_domain_data *pdd;
+	ktime_t off_time, on_time;
+	s64 delta_ns, min_delta_ns;
+
+	on_time = genpd->power_on_latency;
+	/* Check if slave domains can be off for enough time. */
+	delta_ns = ktime_to_ns(ktime_add(genpd->power_off_latency, on_time));
+	min_delta_ns = 0;
+	/* All slave domains have been powered off at this point. */
+	list_for_each_entry(link, &genpd->master_links, master_node) {
+		if (delta_ns > link->slave->min_delta_ns)
+			return false;
+
+		delta_ns = link->slave->min_delta_ns - delta_ns;
+		if (delta_ns < min_delta_ns)
+			min_delta_ns = delta_ns;
+	}
+
+	genpd->min_delta_ns = min_delta_ns;
+
+	/* Compute the total time needed to power off the domain. */
+	off_time = ktime_set(0, 0);
+	/* All devices have been stopped at this point. */
+	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+		struct gpd_gov_dev_data *gov_data;
+
+		if (!pdd->dev->driver)
+			continue;
+
+		gov_data = to_gpd_data(pdd)->gov_data;
+		if (!gov_data)
+			continue;
+
+		off_time = ktime_add(off_time, gov_data->suspend_latency);
+	}
+	off_time = ktime_add(off_time, genpd->power_off_latency);
+
+	/*
+	 * For each device in the domain compute the difference between the
+	 * QoS value and the total time required to bring the device back
+	 * assuming that the domain will be powered off and compute the minimum
+	 * of those.
+	 */
+	min_delta_ns = 0;
+	on_time = ktime_add(on_time, off_time);
+	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+		struct gpd_gov_dev_data *gov_data;
+		struct device *dev = pdd->dev;
+		ktime_t dev_up_time;
+		s32 constraint;
+		s64 constraint_ns;
+
+		if (!dev->driver)
+			continue;
+
+		gov_data = to_gpd_data(pdd)->gov_data;
+		if (gov_data) {
+			dev_up_time = ktime_add(on_time,
+						gov_data->resume_latency);
+			dev_up_time = ktime_add(dev_up_time,
+						gov_data->start_latency);
+		} else {
+			dev_up_time = on_time;
+		}
+
+		constraint = dev_pm_qos_read_value(dev);
+		if (constraint < 0)
+			return false;
+		else if (constraint = 0) /* 0 means "don't care" */
+			continue;
+
+		constraint_ns = constraint;
+		constraint_ns *= NSEC_PER_USEC;
+		delta_ns = constraint_ns - ktime_to_ns(dev_up_time);
+		if (min_delta_ns > delta_ns)
+			min_delta_ns = delta_ns;
+	}
+
+	/* Compare the computed delta with the break even value. */
+	if (min_delta_ns < genpd->break_even_ns)
+		return false;
+
+	/* Store the computed value for the masters to use. */
+	if (genpd->min_delta_ns > min_delta_ns)
+		genpd->min_delta_ns = min_delta_ns;
+
+	/* The domain can be powered off. */
+	return true;
+}
+
 struct dev_power_governor default_qos_governor = {
 	.stop_ok = default_stop_ok,
+	.power_down_ok = default_power_down_ok,
 };


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

* [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
@ 2011-08-30 22:22   ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:22 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet

From: Rafael J. Wysocki <rjw@sisk.pl>

Add a function deciding whether or not a given PM domain should
be powered off on the basis of that domain's devices' PM QoS
constraints.

---
 drivers/base/power/domain_governor.c |   96 +++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h            |    7 ++
 2 files changed, 103 insertions(+)

Index: linux/include/linux/pm_domain.h
===================================================================
--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -49,6 +49,10 @@ struct generic_pm_domain {
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 	bool (*active_wakeup)(struct device *dev);
+	ktime_t power_off_latency;
+	ktime_t power_on_latency;
+	s64 break_even_ns;
+	s64 min_delta_ns;
 };
 
 static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
@@ -64,6 +68,9 @@ struct gpd_link {
 };
 
 struct gpd_gov_dev_data {
+	ktime_t start_latency;
+	ktime_t suspend_latency;
+	ktime_t resume_latency;
 	s64 break_even_ns;
 };
 
Index: linux/drivers/base/power/domain_governor.c
===================================================================
--- linux.orig/drivers/base/power/domain_governor.c
+++ linux/drivers/base/power/domain_governor.c
@@ -35,6 +35,102 @@ static bool default_stop_ok(struct devic
 	return constraint_ns > gov_data->break_even_ns;
 }
 
+/* This routine must be executed under the PM domain's lock. */
+static bool default_power_down_ok(struct dev_pm_domain *pd)
+{
+	struct generic_pm_domain *genpd = pd_to_genpd(pd);
+	struct gpd_link *link;
+	struct pm_domain_data *pdd;
+	ktime_t off_time, on_time;
+	s64 delta_ns, min_delta_ns;
+
+	on_time = genpd->power_on_latency;
+	/* Check if slave domains can be off for enough time. */
+	delta_ns = ktime_to_ns(ktime_add(genpd->power_off_latency, on_time));
+	min_delta_ns = 0;
+	/* All slave domains have been powered off at this point. */
+	list_for_each_entry(link, &genpd->master_links, master_node) {
+		if (delta_ns > link->slave->min_delta_ns)
+			return false;
+
+		delta_ns = link->slave->min_delta_ns - delta_ns;
+		if (delta_ns < min_delta_ns)
+			min_delta_ns = delta_ns;
+	}
+
+	genpd->min_delta_ns = min_delta_ns;
+
+	/* Compute the total time needed to power off the domain. */
+	off_time = ktime_set(0, 0);
+	/* All devices have been stopped at this point. */
+	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+		struct gpd_gov_dev_data *gov_data;
+
+		if (!pdd->dev->driver)
+			continue;
+
+		gov_data = to_gpd_data(pdd)->gov_data;
+		if (!gov_data)
+			continue;
+
+		off_time = ktime_add(off_time, gov_data->suspend_latency);
+	}
+	off_time = ktime_add(off_time, genpd->power_off_latency);
+
+	/*
+	 * For each device in the domain compute the difference between the
+	 * QoS value and the total time required to bring the device back
+	 * assuming that the domain will be powered off and compute the minimum
+	 * of those.
+	 */
+	min_delta_ns = 0;
+	on_time = ktime_add(on_time, off_time);
+	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+		struct gpd_gov_dev_data *gov_data;
+		struct device *dev = pdd->dev;
+		ktime_t dev_up_time;
+		s32 constraint;
+		s64 constraint_ns;
+
+		if (!dev->driver)
+			continue;
+
+		gov_data = to_gpd_data(pdd)->gov_data;
+		if (gov_data) {
+			dev_up_time = ktime_add(on_time,
+						gov_data->resume_latency);
+			dev_up_time = ktime_add(dev_up_time,
+						gov_data->start_latency);
+		} else {
+			dev_up_time = on_time;
+		}
+
+		constraint = dev_pm_qos_read_value(dev);
+		if (constraint < 0)
+			return false;
+		else if (constraint == 0) /* 0 means "don't care" */
+			continue;
+
+		constraint_ns = constraint;
+		constraint_ns *= NSEC_PER_USEC;
+		delta_ns = constraint_ns - ktime_to_ns(dev_up_time);
+		if (min_delta_ns > delta_ns)
+			min_delta_ns = delta_ns;
+	}
+
+	/* Compare the computed delta with the break even value. */
+	if (min_delta_ns < genpd->break_even_ns)
+		return false;
+
+	/* Store the computed value for the masters to use. */
+	if (genpd->min_delta_ns > min_delta_ns)
+		genpd->min_delta_ns = min_delta_ns;
+
+	/* The domain can be powered off. */
+	return true;
+}
+
 struct dev_power_governor default_qos_governor = {
 	.stop_ok = default_stop_ok,
+	.power_down_ok = default_power_down_ok,
 };


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

* [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
  2011-08-30 22:17 ` Rafael J. Wysocki
                   ` (8 preceding siblings ...)
  (?)
@ 2011-08-30 22:22 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-08-30 22:22 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: LKML, Linux-sh list

From: Rafael J. Wysocki <rjw@sisk.pl>

Add a function deciding whether or not a given PM domain should
be powered off on the basis of that domain's devices' PM QoS
constraints.

---
 drivers/base/power/domain_governor.c |   96 +++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h            |    7 ++
 2 files changed, 103 insertions(+)

Index: linux/include/linux/pm_domain.h
===================================================================
--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -49,6 +49,10 @@ struct generic_pm_domain {
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 	bool (*active_wakeup)(struct device *dev);
+	ktime_t power_off_latency;
+	ktime_t power_on_latency;
+	s64 break_even_ns;
+	s64 min_delta_ns;
 };
 
 static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
@@ -64,6 +68,9 @@ struct gpd_link {
 };
 
 struct gpd_gov_dev_data {
+	ktime_t start_latency;
+	ktime_t suspend_latency;
+	ktime_t resume_latency;
 	s64 break_even_ns;
 };
 
Index: linux/drivers/base/power/domain_governor.c
===================================================================
--- linux.orig/drivers/base/power/domain_governor.c
+++ linux/drivers/base/power/domain_governor.c
@@ -35,6 +35,102 @@ static bool default_stop_ok(struct devic
 	return constraint_ns > gov_data->break_even_ns;
 }
 
+/* This routine must be executed under the PM domain's lock. */
+static bool default_power_down_ok(struct dev_pm_domain *pd)
+{
+	struct generic_pm_domain *genpd = pd_to_genpd(pd);
+	struct gpd_link *link;
+	struct pm_domain_data *pdd;
+	ktime_t off_time, on_time;
+	s64 delta_ns, min_delta_ns;
+
+	on_time = genpd->power_on_latency;
+	/* Check if slave domains can be off for enough time. */
+	delta_ns = ktime_to_ns(ktime_add(genpd->power_off_latency, on_time));
+	min_delta_ns = 0;
+	/* All slave domains have been powered off at this point. */
+	list_for_each_entry(link, &genpd->master_links, master_node) {
+		if (delta_ns > link->slave->min_delta_ns)
+			return false;
+
+		delta_ns = link->slave->min_delta_ns - delta_ns;
+		if (delta_ns < min_delta_ns)
+			min_delta_ns = delta_ns;
+	}
+
+	genpd->min_delta_ns = min_delta_ns;
+
+	/* Compute the total time needed to power off the domain. */
+	off_time = ktime_set(0, 0);
+	/* All devices have been stopped at this point. */
+	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+		struct gpd_gov_dev_data *gov_data;
+
+		if (!pdd->dev->driver)
+			continue;
+
+		gov_data = to_gpd_data(pdd)->gov_data;
+		if (!gov_data)
+			continue;
+
+		off_time = ktime_add(off_time, gov_data->suspend_latency);
+	}
+	off_time = ktime_add(off_time, genpd->power_off_latency);
+
+	/*
+	 * For each device in the domain compute the difference between the
+	 * QoS value and the total time required to bring the device back
+	 * assuming that the domain will be powered off and compute the minimum
+	 * of those.
+	 */
+	min_delta_ns = 0;
+	on_time = ktime_add(on_time, off_time);
+	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+		struct gpd_gov_dev_data *gov_data;
+		struct device *dev = pdd->dev;
+		ktime_t dev_up_time;
+		s32 constraint;
+		s64 constraint_ns;
+
+		if (!dev->driver)
+			continue;
+
+		gov_data = to_gpd_data(pdd)->gov_data;
+		if (gov_data) {
+			dev_up_time = ktime_add(on_time,
+						gov_data->resume_latency);
+			dev_up_time = ktime_add(dev_up_time,
+						gov_data->start_latency);
+		} else {
+			dev_up_time = on_time;
+		}
+
+		constraint = dev_pm_qos_read_value(dev);
+		if (constraint < 0)
+			return false;
+		else if (constraint == 0) /* 0 means "don't care" */
+			continue;
+
+		constraint_ns = constraint;
+		constraint_ns *= NSEC_PER_USEC;
+		delta_ns = constraint_ns - ktime_to_ns(dev_up_time);
+		if (min_delta_ns > delta_ns)
+			min_delta_ns = delta_ns;
+	}
+
+	/* Compare the computed delta with the break even value. */
+	if (min_delta_ns < genpd->break_even_ns)
+		return false;
+
+	/* Store the computed value for the masters to use. */
+	if (genpd->min_delta_ns > min_delta_ns)
+		genpd->min_delta_ns = min_delta_ns;
+
+	/* The domain can be powered off. */
+	return true;
+}
+
 struct dev_power_governor default_qos_governor = {
 	.stop_ok = default_stop_ok,
+	.power_down_ok = default_power_down_ok,
 };

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-08-30 22:21   ` Rafael J. Wysocki
@ 2011-09-01 15:13     ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-01 15:13 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi Rafael,

On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
Ok.

> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so it has to use device_pm_lock()
> and the device PM QoS initialization and destruction should be done
> under device_pm_lock() as well.
Ok that makes sense.
The constraints_state field can be replaced by a combination of
dev->power.constraints and list_empty(&dev->power.entry), which makes
the code more compact and less redundant.

>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/base/power/main.c |    4 -
>  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
>  include/linux/pm.h        |    8 --
>  include/linux/pm_qos.h    |    3
>  4 files changed, 101 insertions(+), 81 deletions(-)
>
> Index: linux/drivers/base/power/qos.c
> =================================> --- linux.orig/drivers/base/power/qos.c
> +++ linux/drivers/base/power/qos.c
> @@ -30,15 +30,6 @@
...

>
> @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
>  *
>  * Returns 1 if the aggregated constraint value has changed,
>  * 0 if the aggregated constraint value has not changed,
> - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> - * removed from the system
> + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> + * to allocate for data structures.
Why not use -ENODEV in case there is no device?

>  */
>  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>                           s32 value)
> @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
>                return -EINVAL;
>        }
>
> -       mutex_lock(&dev_pm_qos_mtx);
>        req->dev = dev;
>
> -       /* Return if the device has been removed */
> -       if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
> -               ret = -ENODEV;
> -               goto out;
> -       }
> +       device_pm_lock();
> +       mutex_lock(&dev_pm_qos_mtx);
>
> -       /*
> -        * Allocate the constraints data on the first call to add_request,
> -        * i.e. only if the data is not already allocated and if the device has
> -        * not been removed
> -        */
> -       if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
> -               ret = dev_pm_qos_constraints_allocate(dev);
> +       if (dev->power.constraints) {
> +               device_pm_unlock();
> +       } else {
> +               if (list_empty(&dev->power.entry)) {
> +                       /* The device has been removed from the system. */
> +                       device_pm_unlock();
> +                       goto out;
0 is silently returned in case the device has been removed. Is that
the intention?

> +               } else {
> +                       device_pm_unlock();
> +                       /*
> +                        * Allocate the constraints data on the first call to
> +                        * add_request, i.e. only if the data is not already
> +                        * allocated and if the device has not been removed.
> +                        */
> +                       ret = dev_pm_qos_constraints_allocate(dev);
> +               }
> +       }
>
>        if (!ret)
>                ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
>
> -out:
> + out:
>        mutex_unlock(&dev_pm_qos_mtx);
> +
>        return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
...

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
@ 2011-09-01 15:13     ` Jean Pihet
  0 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-01 15:13 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi Rafael,

On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
Ok.

> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so it has to use device_pm_lock()
> and the device PM QoS initialization and destruction should be done
> under device_pm_lock() as well.
Ok that makes sense.
The constraints_state field can be replaced by a combination of
dev->power.constraints and list_empty(&dev->power.entry), which makes
the code more compact and less redundant.

>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/base/power/main.c |    4 -
>  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
>  include/linux/pm.h        |    8 --
>  include/linux/pm_qos.h    |    3
>  4 files changed, 101 insertions(+), 81 deletions(-)
>
> Index: linux/drivers/base/power/qos.c
> ===================================================================
> --- linux.orig/drivers/base/power/qos.c
> +++ linux/drivers/base/power/qos.c
> @@ -30,15 +30,6 @@
...

>
> @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
>  *
>  * Returns 1 if the aggregated constraint value has changed,
>  * 0 if the aggregated constraint value has not changed,
> - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> - * removed from the system
> + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> + * to allocate for data structures.
Why not use -ENODEV in case there is no device?

>  */
>  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>                           s32 value)
> @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
>                return -EINVAL;
>        }
>
> -       mutex_lock(&dev_pm_qos_mtx);
>        req->dev = dev;
>
> -       /* Return if the device has been removed */
> -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> -               ret = -ENODEV;
> -               goto out;
> -       }
> +       device_pm_lock();
> +       mutex_lock(&dev_pm_qos_mtx);
>
> -       /*
> -        * Allocate the constraints data on the first call to add_request,
> -        * i.e. only if the data is not already allocated and if the device has
> -        * not been removed
> -        */
> -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> -               ret = dev_pm_qos_constraints_allocate(dev);
> +       if (dev->power.constraints) {
> +               device_pm_unlock();
> +       } else {
> +               if (list_empty(&dev->power.entry)) {
> +                       /* The device has been removed from the system. */
> +                       device_pm_unlock();
> +                       goto out;
0 is silently returned in case the device has been removed. Is that
the intention?

> +               } else {
> +                       device_pm_unlock();
> +                       /*
> +                        * Allocate the constraints data on the first call to
> +                        * add_request, i.e. only if the data is not already
> +                        * allocated and if the device has not been removed.
> +                        */
> +                       ret = dev_pm_qos_constraints_allocate(dev);
> +               }
> +       }
>
>        if (!ret)
>                ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
>
> -out:
> + out:
>        mutex_unlock(&dev_pm_qos_mtx);
> +
>        return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
...

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-08-30 22:21   ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-09-01 15:13   ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-01 15:13 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM mailing list, LKML, Linux-sh list

Hi Rafael,

On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
Ok.

> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so it has to use device_pm_lock()
> and the device PM QoS initialization and destruction should be done
> under device_pm_lock() as well.
Ok that makes sense.
The constraints_state field can be replaced by a combination of
dev->power.constraints and list_empty(&dev->power.entry), which makes
the code more compact and less redundant.

>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/base/power/main.c |    4 -
>  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
>  include/linux/pm.h        |    8 --
>  include/linux/pm_qos.h    |    3
>  4 files changed, 101 insertions(+), 81 deletions(-)
>
> Index: linux/drivers/base/power/qos.c
> ===================================================================
> --- linux.orig/drivers/base/power/qos.c
> +++ linux/drivers/base/power/qos.c
> @@ -30,15 +30,6 @@
...

>
> @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
>  *
>  * Returns 1 if the aggregated constraint value has changed,
>  * 0 if the aggregated constraint value has not changed,
> - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> - * removed from the system
> + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> + * to allocate for data structures.
Why not use -ENODEV in case there is no device?

>  */
>  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>                           s32 value)
> @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
>                return -EINVAL;
>        }
>
> -       mutex_lock(&dev_pm_qos_mtx);
>        req->dev = dev;
>
> -       /* Return if the device has been removed */
> -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> -               ret = -ENODEV;
> -               goto out;
> -       }
> +       device_pm_lock();
> +       mutex_lock(&dev_pm_qos_mtx);
>
> -       /*
> -        * Allocate the constraints data on the first call to add_request,
> -        * i.e. only if the data is not already allocated and if the device has
> -        * not been removed
> -        */
> -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> -               ret = dev_pm_qos_constraints_allocate(dev);
> +       if (dev->power.constraints) {
> +               device_pm_unlock();
> +       } else {
> +               if (list_empty(&dev->power.entry)) {
> +                       /* The device has been removed from the system. */
> +                       device_pm_unlock();
> +                       goto out;
0 is silently returned in case the device has been removed. Is that
the intention?

> +               } else {
> +                       device_pm_unlock();
> +                       /*
> +                        * Allocate the constraints data on the first call to
> +                        * add_request, i.e. only if the data is not already
> +                        * allocated and if the device has not been removed.
> +                        */
> +                       ret = dev_pm_qos_constraints_allocate(dev);
> +               }
> +       }
>
>        if (!ret)
>                ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
>
> -out:
> + out:
>        mutex_unlock(&dev_pm_qos_mtx);
> +
>        return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
...

Thanks,
Jean

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

* Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
  2011-08-30 22:22   ` Rafael J. Wysocki
@ 2011-09-01 15:17     ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-01 15:17 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Rafael,

On Wed, Aug 31, 2011 at 12:22 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Add a function deciding whether or not a given PM domain should
> be powered off on the basis of that domain's devices' PM QoS
> constraints.
>
> ---
>  drivers/base/power/domain_governor.c |   96 +++++++++++++++++++++++++++++++++++
>  include/linux/pm_domain.h            |    7 ++
>  2 files changed, 103 insertions(+)
>
> Index: linux/include/linux/pm_domain.h
> =================================> --- linux.orig/include/linux/pm_domain.h
> +++ linux/include/linux/pm_domain.h
> @@ -49,6 +49,10 @@ struct generic_pm_domain {
>        int (*start_device)(struct device *dev);
>        int (*stop_device)(struct device *dev);
>        bool (*active_wakeup)(struct device *dev);
> +       ktime_t power_off_latency;
> +       ktime_t power_on_latency;
> +       s64 break_even_ns;
> +       s64 min_delta_ns;
How are those values populated? Is there a mechanism that dynamically
updates the values?

>  };
>
>  static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
> @@ -64,6 +68,9 @@ struct gpd_link {
>  };
>
>  struct gpd_gov_dev_data {
> +       ktime_t start_latency;
> +       ktime_t suspend_latency;
> +       ktime_t resume_latency;
Same question here.

>        s64 break_even_ns;
>  };
>
...

Regards,
Jean

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

* Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
@ 2011-09-01 15:17     ` Jean Pihet
  0 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-01 15:17 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Rafael,

On Wed, Aug 31, 2011 at 12:22 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Add a function deciding whether or not a given PM domain should
> be powered off on the basis of that domain's devices' PM QoS
> constraints.
>
> ---
>  drivers/base/power/domain_governor.c |   96 +++++++++++++++++++++++++++++++++++
>  include/linux/pm_domain.h            |    7 ++
>  2 files changed, 103 insertions(+)
>
> Index: linux/include/linux/pm_domain.h
> ===================================================================
> --- linux.orig/include/linux/pm_domain.h
> +++ linux/include/linux/pm_domain.h
> @@ -49,6 +49,10 @@ struct generic_pm_domain {
>        int (*start_device)(struct device *dev);
>        int (*stop_device)(struct device *dev);
>        bool (*active_wakeup)(struct device *dev);
> +       ktime_t power_off_latency;
> +       ktime_t power_on_latency;
> +       s64 break_even_ns;
> +       s64 min_delta_ns;
How are those values populated? Is there a mechanism that dynamically
updates the values?

>  };
>
>  static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
> @@ -64,6 +68,9 @@ struct gpd_link {
>  };
>
>  struct gpd_gov_dev_data {
> +       ktime_t start_latency;
> +       ktime_t suspend_latency;
> +       ktime_t resume_latency;
Same question here.

>        s64 break_even_ns;
>  };
>
...

Regards,
Jean

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

* Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
  2011-08-30 22:22   ` Rafael J. Wysocki
  (?)
@ 2011-09-01 15:17   ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-01 15:17 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM mailing list, LKML, Linux-sh list

Rafael,

On Wed, Aug 31, 2011 at 12:22 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Add a function deciding whether or not a given PM domain should
> be powered off on the basis of that domain's devices' PM QoS
> constraints.
>
> ---
>  drivers/base/power/domain_governor.c |   96 +++++++++++++++++++++++++++++++++++
>  include/linux/pm_domain.h            |    7 ++
>  2 files changed, 103 insertions(+)
>
> Index: linux/include/linux/pm_domain.h
> ===================================================================
> --- linux.orig/include/linux/pm_domain.h
> +++ linux/include/linux/pm_domain.h
> @@ -49,6 +49,10 @@ struct generic_pm_domain {
>        int (*start_device)(struct device *dev);
>        int (*stop_device)(struct device *dev);
>        bool (*active_wakeup)(struct device *dev);
> +       ktime_t power_off_latency;
> +       ktime_t power_on_latency;
> +       s64 break_even_ns;
> +       s64 min_delta_ns;
How are those values populated? Is there a mechanism that dynamically
updates the values?

>  };
>
>  static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
> @@ -64,6 +68,9 @@ struct gpd_link {
>  };
>
>  struct gpd_gov_dev_data {
> +       ktime_t start_latency;
> +       ktime_t suspend_latency;
> +       ktime_t resume_latency;
Same question here.

>        s64 break_even_ns;
>  };
>
...

Regards,
Jean

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

* Re: [PATCH 0/5] PM: Generic PM domains and device PM QoS
  2011-08-30 22:17 ` Rafael J. Wysocki
@ 2011-09-01 15:28   ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-01 15:28 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Rafael,

On Wed, Aug 31, 2011 at 12:17 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> This patchset illustrates how device PM QoS may be used along with
> PM domains in my view.
>
> Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
> suitable for 3.2, unless somebody hates them,
The patches [1-3/5] are ok (reviewed only) excepted some remarks I have.

> but patches [4-5/5] are
> total RFC.  They haven't been tested, only compiled, so the use of them
> is not encouraged (they may kill your dog or make your cat go wild, or
> do something equally nasty, so beware).
That looks like a disclaimer ;p

> Their purpose is to illustrate
> an idea that I'd like to discuss at the PM miniconference during the
> LPC.
There is some code for OMAP that dynamically updates the worst case
values for devices activation and de-activation;
cf._omap_device_activate and _omap_device_deactivate in
arch/arm/plat-omap/omap_device.c. The idea is to start with reference
figures (worst case measured on board) at boot and then update the
worst case values at runtime.
Based on the PM QoS values and the worst case latency values the next
power domains states can be determined. Unfortunately this is not
(yet) implemented.

I am wondering if the patches [4-5/5] are meant to replace the OMAP
code, which would be really nice.

>
> [1/5] - Split device PM domain data into a "base" and additional fields
>        (one need_restore field at the moment, but the subsequent patches
>        add more fields).
>
> [2/5] - Make runtime PM always release power.lock if power.irq_safe is set.
>
> [3/5] - Add function for reading device PM QoS values safely.
>
> [4/5] - Add governor function for stopping devices.
>
> [5/5] - Add generic PM domain power off governor function.
>
> Thanks,
> Rafael
>
>

Regards,
Jean

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

* Re: [PATCH 0/5] PM: Generic PM domains and device PM QoS
@ 2011-09-01 15:28   ` Jean Pihet
  0 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-01 15:28 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Rafael,

On Wed, Aug 31, 2011 at 12:17 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> This patchset illustrates how device PM QoS may be used along with
> PM domains in my view.
>
> Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
> suitable for 3.2, unless somebody hates them,
The patches [1-3/5] are ok (reviewed only) excepted some remarks I have.

> but patches [4-5/5] are
> total RFC.  They haven't been tested, only compiled, so the use of them
> is not encouraged (they may kill your dog or make your cat go wild, or
> do something equally nasty, so beware).
That looks like a disclaimer ;p

> Their purpose is to illustrate
> an idea that I'd like to discuss at the PM miniconference during the
> LPC.
There is some code for OMAP that dynamically updates the worst case
values for devices activation and de-activation;
cf._omap_device_activate and _omap_device_deactivate in
arch/arm/plat-omap/omap_device.c. The idea is to start with reference
figures (worst case measured on board) at boot and then update the
worst case values at runtime.
Based on the PM QoS values and the worst case latency values the next
power domains states can be determined. Unfortunately this is not
(yet) implemented.

I am wondering if the patches [4-5/5] are meant to replace the OMAP
code, which would be really nice.

>
> [1/5] - Split device PM domain data into a "base" and additional fields
>        (one need_restore field at the moment, but the subsequent patches
>        add more fields).
>
> [2/5] - Make runtime PM always release power.lock if power.irq_safe is set.
>
> [3/5] - Add function for reading device PM QoS values safely.
>
> [4/5] - Add governor function for stopping devices.
>
> [5/5] - Add generic PM domain power off governor function.
>
> Thanks,
> Rafael
>
>

Regards,
Jean

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

* Re: [PATCH 0/5] PM: Generic PM domains and device PM QoS
  2011-08-30 22:17 ` Rafael J. Wysocki
                   ` (11 preceding siblings ...)
  (?)
@ 2011-09-01 15:28 ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-01 15:28 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM mailing list, LKML, Linux-sh list

Rafael,

On Wed, Aug 31, 2011 at 12:17 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> This patchset illustrates how device PM QoS may be used along with
> PM domains in my view.
>
> Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
> suitable for 3.2, unless somebody hates them,
The patches [1-3/5] are ok (reviewed only) excepted some remarks I have.

> but patches [4-5/5] are
> total RFC.  They haven't been tested, only compiled, so the use of them
> is not encouraged (they may kill your dog or make your cat go wild, or
> do something equally nasty, so beware).
That looks like a disclaimer ;p

> Their purpose is to illustrate
> an idea that I'd like to discuss at the PM miniconference during the
> LPC.
There is some code for OMAP that dynamically updates the worst case
values for devices activation and de-activation;
cf._omap_device_activate and _omap_device_deactivate in
arch/arm/plat-omap/omap_device.c. The idea is to start with reference
figures (worst case measured on board) at boot and then update the
worst case values at runtime.
Based on the PM QoS values and the worst case latency values the next
power domains states can be determined. Unfortunately this is not
(yet) implemented.

I am wondering if the patches [4-5/5] are meant to replace the OMAP
code, which would be really nice.

>
> [1/5] - Split device PM domain data into a "base" and additional fields
>        (one need_restore field at the moment, but the subsequent patches
>        add more fields).
>
> [2/5] - Make runtime PM always release power.lock if power.irq_safe is set.
>
> [3/5] - Add function for reading device PM QoS values safely.
>
> [4/5] - Add governor function for stopping devices.
>
> [5/5] - Add generic PM domain power off governor function.
>
> Thanks,
> Rafael
>
>

Regards,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-01 15:13     ` Jean Pihet
@ 2011-09-01 22:07       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-01 22:07 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Thursday, September 01, 2011, Jean Pihet wrote:
> Hi Rafael,
> 
> On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > To read the current PM QoS value for a given device we need to
> > make sure that the device's power.constraints object won't be
> > removed while we're doing that.  For this reason, put the
> > operation under dev->power.lock and acquire the lock
> > around the initialization and removal of power.constraints.
> Ok.
> 
> > Moreover, since we're using the value of power.constraints to
> > determine whether or not the object is present, the
> > power.constraints_state field isn't necessary any more and may be
> > removed.  However, dev_pm_qos_add_request() needs to check if the
> > device is being removed from the system before allocating a new
> > PM QoS constraints object for it, so it has to use device_pm_lock()
> > and the device PM QoS initialization and destruction should be done
> > under device_pm_lock() as well.
> Ok that makes sense.
> The constraints_state field can be replaced by a combination of
> dev->power.constraints and list_empty(&dev->power.entry), which makes
> the code more compact and less redundant.
> 
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> >  drivers/base/power/main.c |    4 -
> >  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
> >  include/linux/pm.h        |    8 --
> >  include/linux/pm_qos.h    |    3
> >  4 files changed, 101 insertions(+), 81 deletions(-)
> >
> > Index: linux/drivers/base/power/qos.c
> > =================================> > --- linux.orig/drivers/base/power/qos.c
> > +++ linux/drivers/base/power/qos.c
> > @@ -30,15 +30,6 @@
> ...
> 
> >
> > @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
> >  *
> >  * Returns 1 if the aggregated constraint value has changed,
> >  * 0 if the aggregated constraint value has not changed,
> > - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> > - * removed from the system
> > + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> > + * to allocate for data structures.
> Why not use -ENODEV in case there is no device?

I don't think it's useful for the caller.  If the device is gone, the
constraing simply doesn't matter, so there's no error to handle.

> >  */
> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
> >                           s32 value)
> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
> >                return -EINVAL;
> >        }
> >
> > -       mutex_lock(&dev_pm_qos_mtx);
> >        req->dev = dev;
> >
> > -       /* Return if the device has been removed */
> > -       if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
> > -               ret = -ENODEV;
> > -               goto out;
> > -       }
> > +       device_pm_lock();
> > +       mutex_lock(&dev_pm_qos_mtx);
> >
> > -       /*
> > -        * Allocate the constraints data on the first call to add_request,
> > -        * i.e. only if the data is not already allocated and if the device has
> > -        * not been removed
> > -        */
> > -       if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
> > -               ret = dev_pm_qos_constraints_allocate(dev);
> > +       if (dev->power.constraints) {
> > +               device_pm_unlock();
> > +       } else {
> > +               if (list_empty(&dev->power.entry)) {
> > +                       /* The device has been removed from the system. */
> > +                       device_pm_unlock();
> > +                       goto out;
> 0 is silently returned in case the device has been removed. Is that
> the intention?

Pretty much it is.  Is that a problem?

Rafael

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
@ 2011-09-01 22:07       ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-01 22:07 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Thursday, September 01, 2011, Jean Pihet wrote:
> Hi Rafael,
> 
> On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > To read the current PM QoS value for a given device we need to
> > make sure that the device's power.constraints object won't be
> > removed while we're doing that.  For this reason, put the
> > operation under dev->power.lock and acquire the lock
> > around the initialization and removal of power.constraints.
> Ok.
> 
> > Moreover, since we're using the value of power.constraints to
> > determine whether or not the object is present, the
> > power.constraints_state field isn't necessary any more and may be
> > removed.  However, dev_pm_qos_add_request() needs to check if the
> > device is being removed from the system before allocating a new
> > PM QoS constraints object for it, so it has to use device_pm_lock()
> > and the device PM QoS initialization and destruction should be done
> > under device_pm_lock() as well.
> Ok that makes sense.
> The constraints_state field can be replaced by a combination of
> dev->power.constraints and list_empty(&dev->power.entry), which makes
> the code more compact and less redundant.
> 
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> >  drivers/base/power/main.c |    4 -
> >  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
> >  include/linux/pm.h        |    8 --
> >  include/linux/pm_qos.h    |    3
> >  4 files changed, 101 insertions(+), 81 deletions(-)
> >
> > Index: linux/drivers/base/power/qos.c
> > ===================================================================
> > --- linux.orig/drivers/base/power/qos.c
> > +++ linux/drivers/base/power/qos.c
> > @@ -30,15 +30,6 @@
> ...
> 
> >
> > @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
> >  *
> >  * Returns 1 if the aggregated constraint value has changed,
> >  * 0 if the aggregated constraint value has not changed,
> > - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> > - * removed from the system
> > + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> > + * to allocate for data structures.
> Why not use -ENODEV in case there is no device?

I don't think it's useful for the caller.  If the device is gone, the
constraing simply doesn't matter, so there's no error to handle.

> >  */
> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
> >                           s32 value)
> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
> >                return -EINVAL;
> >        }
> >
> > -       mutex_lock(&dev_pm_qos_mtx);
> >        req->dev = dev;
> >
> > -       /* Return if the device has been removed */
> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> > -               ret = -ENODEV;
> > -               goto out;
> > -       }
> > +       device_pm_lock();
> > +       mutex_lock(&dev_pm_qos_mtx);
> >
> > -       /*
> > -        * Allocate the constraints data on the first call to add_request,
> > -        * i.e. only if the data is not already allocated and if the device has
> > -        * not been removed
> > -        */
> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> > -               ret = dev_pm_qos_constraints_allocate(dev);
> > +       if (dev->power.constraints) {
> > +               device_pm_unlock();
> > +       } else {
> > +               if (list_empty(&dev->power.entry)) {
> > +                       /* The device has been removed from the system. */
> > +                       device_pm_unlock();
> > +                       goto out;
> 0 is silently returned in case the device has been removed. Is that
> the intention?

Pretty much it is.  Is that a problem?

Rafael

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-01 15:13     ` Jean Pihet
  (?)
@ 2011-09-01 22:07     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-01 22:07 UTC (permalink / raw)
  To: Jean Pihet; +Cc: Linux PM mailing list, LKML, Linux-sh list

Hi,

On Thursday, September 01, 2011, Jean Pihet wrote:
> Hi Rafael,
> 
> On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > To read the current PM QoS value for a given device we need to
> > make sure that the device's power.constraints object won't be
> > removed while we're doing that.  For this reason, put the
> > operation under dev->power.lock and acquire the lock
> > around the initialization and removal of power.constraints.
> Ok.
> 
> > Moreover, since we're using the value of power.constraints to
> > determine whether or not the object is present, the
> > power.constraints_state field isn't necessary any more and may be
> > removed.  However, dev_pm_qos_add_request() needs to check if the
> > device is being removed from the system before allocating a new
> > PM QoS constraints object for it, so it has to use device_pm_lock()
> > and the device PM QoS initialization and destruction should be done
> > under device_pm_lock() as well.
> Ok that makes sense.
> The constraints_state field can be replaced by a combination of
> dev->power.constraints and list_empty(&dev->power.entry), which makes
> the code more compact and less redundant.
> 
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> >  drivers/base/power/main.c |    4 -
> >  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
> >  include/linux/pm.h        |    8 --
> >  include/linux/pm_qos.h    |    3
> >  4 files changed, 101 insertions(+), 81 deletions(-)
> >
> > Index: linux/drivers/base/power/qos.c
> > ===================================================================
> > --- linux.orig/drivers/base/power/qos.c
> > +++ linux/drivers/base/power/qos.c
> > @@ -30,15 +30,6 @@
> ...
> 
> >
> > @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
> >  *
> >  * Returns 1 if the aggregated constraint value has changed,
> >  * 0 if the aggregated constraint value has not changed,
> > - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> > - * removed from the system
> > + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> > + * to allocate for data structures.
> Why not use -ENODEV in case there is no device?

I don't think it's useful for the caller.  If the device is gone, the
constraing simply doesn't matter, so there's no error to handle.

> >  */
> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
> >                           s32 value)
> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
> >                return -EINVAL;
> >        }
> >
> > -       mutex_lock(&dev_pm_qos_mtx);
> >        req->dev = dev;
> >
> > -       /* Return if the device has been removed */
> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> > -               ret = -ENODEV;
> > -               goto out;
> > -       }
> > +       device_pm_lock();
> > +       mutex_lock(&dev_pm_qos_mtx);
> >
> > -       /*
> > -        * Allocate the constraints data on the first call to add_request,
> > -        * i.e. only if the data is not already allocated and if the device has
> > -        * not been removed
> > -        */
> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> > -               ret = dev_pm_qos_constraints_allocate(dev);
> > +       if (dev->power.constraints) {
> > +               device_pm_unlock();
> > +       } else {
> > +               if (list_empty(&dev->power.entry)) {
> > +                       /* The device has been removed from the system. */
> > +                       device_pm_unlock();
> > +                       goto out;
> 0 is silently returned in case the device has been removed. Is that
> the intention?

Pretty much it is.  Is that a problem?

Rafael

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

* Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
  2011-09-01 15:17     ` Jean Pihet
@ 2011-09-01 22:11       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-01 22:11 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Thursday, September 01, 2011, Jean Pihet wrote:
> Rafael,
> 
> On Wed, Aug 31, 2011 at 12:22 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Add a function deciding whether or not a given PM domain should
> > be powered off on the basis of that domain's devices' PM QoS
> > constraints.
> >
> > ---
> >  drivers/base/power/domain_governor.c |   96 +++++++++++++++++++++++++++++++++++
> >  include/linux/pm_domain.h            |    7 ++
> >  2 files changed, 103 insertions(+)
> >
> > Index: linux/include/linux/pm_domain.h
> > =================================> > --- linux.orig/include/linux/pm_domain.h
> > +++ linux/include/linux/pm_domain.h
> > @@ -49,6 +49,10 @@ struct generic_pm_domain {
> >        int (*start_device)(struct device *dev);
> >        int (*stop_device)(struct device *dev);
> >        bool (*active_wakeup)(struct device *dev);
> > +       ktime_t power_off_latency;
> > +       ktime_t power_on_latency;
> > +       s64 break_even_ns;
> > +       s64 min_delta_ns;
> How are those values populated?

I'm not sure.  There are a few possible ways to do that, but I simply
don't know which one is going to be the most useful.  That's one of
the reasons why patches [4-5/5] are RFCs.

> Is there a mechanism that dynamically updates the values?

Obviously not at the moment.

Thanks,
Rafael

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

* Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
@ 2011-09-01 22:11       ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-01 22:11 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Thursday, September 01, 2011, Jean Pihet wrote:
> Rafael,
> 
> On Wed, Aug 31, 2011 at 12:22 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Add a function deciding whether or not a given PM domain should
> > be powered off on the basis of that domain's devices' PM QoS
> > constraints.
> >
> > ---
> >  drivers/base/power/domain_governor.c |   96 +++++++++++++++++++++++++++++++++++
> >  include/linux/pm_domain.h            |    7 ++
> >  2 files changed, 103 insertions(+)
> >
> > Index: linux/include/linux/pm_domain.h
> > ===================================================================
> > --- linux.orig/include/linux/pm_domain.h
> > +++ linux/include/linux/pm_domain.h
> > @@ -49,6 +49,10 @@ struct generic_pm_domain {
> >        int (*start_device)(struct device *dev);
> >        int (*stop_device)(struct device *dev);
> >        bool (*active_wakeup)(struct device *dev);
> > +       ktime_t power_off_latency;
> > +       ktime_t power_on_latency;
> > +       s64 break_even_ns;
> > +       s64 min_delta_ns;
> How are those values populated?

I'm not sure.  There are a few possible ways to do that, but I simply
don't know which one is going to be the most useful.  That's one of
the reasons why patches [4-5/5] are RFCs.

> Is there a mechanism that dynamically updates the values?

Obviously not at the moment.

Thanks,
Rafael

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

* Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
  2011-09-01 15:17     ` Jean Pihet
  (?)
@ 2011-09-01 22:11     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-01 22:11 UTC (permalink / raw)
  To: Jean Pihet; +Cc: Linux PM mailing list, LKML, Linux-sh list

Hi,

On Thursday, September 01, 2011, Jean Pihet wrote:
> Rafael,
> 
> On Wed, Aug 31, 2011 at 12:22 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Add a function deciding whether or not a given PM domain should
> > be powered off on the basis of that domain's devices' PM QoS
> > constraints.
> >
> > ---
> >  drivers/base/power/domain_governor.c |   96 +++++++++++++++++++++++++++++++++++
> >  include/linux/pm_domain.h            |    7 ++
> >  2 files changed, 103 insertions(+)
> >
> > Index: linux/include/linux/pm_domain.h
> > ===================================================================
> > --- linux.orig/include/linux/pm_domain.h
> > +++ linux/include/linux/pm_domain.h
> > @@ -49,6 +49,10 @@ struct generic_pm_domain {
> >        int (*start_device)(struct device *dev);
> >        int (*stop_device)(struct device *dev);
> >        bool (*active_wakeup)(struct device *dev);
> > +       ktime_t power_off_latency;
> > +       ktime_t power_on_latency;
> > +       s64 break_even_ns;
> > +       s64 min_delta_ns;
> How are those values populated?

I'm not sure.  There are a few possible ways to do that, but I simply
don't know which one is going to be the most useful.  That's one of
the reasons why patches [4-5/5] are RFCs.

> Is there a mechanism that dynamically updates the values?

Obviously not at the moment.

Thanks,
Rafael

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

* Re: [PATCH 0/5] PM: Generic PM domains and device PM QoS
  2011-09-01 15:28   ` Jean Pihet
@ 2011-09-01 22:14     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-01 22:14 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Thursday, September 01, 2011, Jean Pihet wrote:
> Rafael,
> 
> On Wed, Aug 31, 2011 at 12:17 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > This patchset illustrates how device PM QoS may be used along with
> > PM domains in my view.
> >
> > Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
> > suitable for 3.2, unless somebody hates them,
> The patches [1-3/5] are ok (reviewed only) excepted some remarks I have.

OK, thanks for the comments.

> > but patches [4-5/5] are
> > total RFC.  They haven't been tested, only compiled, so the use of them
> > is not encouraged (they may kill your dog or make your cat go wild, or
> > do something equally nasty, so beware).
> That looks like a disclaimer ;p
> 
> > Their purpose is to illustrate
> > an idea that I'd like to discuss at the PM miniconference during the
> > LPC.
> There is some code for OMAP that dynamically updates the worst case
> values for devices activation and de-activation;
> cf._omap_device_activate and _omap_device_deactivate in
> arch/arm/plat-omap/omap_device.c. The idea is to start with reference
> figures (worst case measured on board) at boot and then update the
> worst case values at runtime.
> Based on the PM QoS values and the worst case latency values the next
> power domains states can be determined. Unfortunately this is not
> (yet) implemented.

I thought about that too, but I'd like to discuss the basic idea first.

> I am wondering if the patches [4-5/5] are meant to replace the OMAP
> code, which would be really nice.

I certainly hope they will be useful for multiple platforms.  Whether
or not OMAP turns out to be one of them I can't tell at the moment.

Thanks,
Rafael

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

* Re: [PATCH 0/5] PM: Generic PM domains and device PM QoS
@ 2011-09-01 22:14     ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-01 22:14 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Thursday, September 01, 2011, Jean Pihet wrote:
> Rafael,
> 
> On Wed, Aug 31, 2011 at 12:17 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > This patchset illustrates how device PM QoS may be used along with
> > PM domains in my view.
> >
> > Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
> > suitable for 3.2, unless somebody hates them,
> The patches [1-3/5] are ok (reviewed only) excepted some remarks I have.

OK, thanks for the comments.

> > but patches [4-5/5] are
> > total RFC.  They haven't been tested, only compiled, so the use of them
> > is not encouraged (they may kill your dog or make your cat go wild, or
> > do something equally nasty, so beware).
> That looks like a disclaimer ;p
> 
> > Their purpose is to illustrate
> > an idea that I'd like to discuss at the PM miniconference during the
> > LPC.
> There is some code for OMAP that dynamically updates the worst case
> values for devices activation and de-activation;
> cf._omap_device_activate and _omap_device_deactivate in
> arch/arm/plat-omap/omap_device.c. The idea is to start with reference
> figures (worst case measured on board) at boot and then update the
> worst case values at runtime.
> Based on the PM QoS values and the worst case latency values the next
> power domains states can be determined. Unfortunately this is not
> (yet) implemented.

I thought about that too, but I'd like to discuss the basic idea first.

> I am wondering if the patches [4-5/5] are meant to replace the OMAP
> code, which would be really nice.

I certainly hope they will be useful for multiple platforms.  Whether
or not OMAP turns out to be one of them I can't tell at the moment.

Thanks,
Rafael

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

* Re: [PATCH 0/5] PM: Generic PM domains and device PM QoS
  2011-09-01 15:28   ` Jean Pihet
  (?)
@ 2011-09-01 22:14   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-01 22:14 UTC (permalink / raw)
  To: Jean Pihet; +Cc: Linux PM mailing list, LKML, Linux-sh list

Hi,

On Thursday, September 01, 2011, Jean Pihet wrote:
> Rafael,
> 
> On Wed, Aug 31, 2011 at 12:17 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > This patchset illustrates how device PM QoS may be used along with
> > PM domains in my view.
> >
> > Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
> > suitable for 3.2, unless somebody hates them,
> The patches [1-3/5] are ok (reviewed only) excepted some remarks I have.

OK, thanks for the comments.

> > but patches [4-5/5] are
> > total RFC.  They haven't been tested, only compiled, so the use of them
> > is not encouraged (they may kill your dog or make your cat go wild, or
> > do something equally nasty, so beware).
> That looks like a disclaimer ;p
> 
> > Their purpose is to illustrate
> > an idea that I'd like to discuss at the PM miniconference during the
> > LPC.
> There is some code for OMAP that dynamically updates the worst case
> values for devices activation and de-activation;
> cf._omap_device_activate and _omap_device_deactivate in
> arch/arm/plat-omap/omap_device.c. The idea is to start with reference
> figures (worst case measured on board) at boot and then update the
> worst case values at runtime.
> Based on the PM QoS values and the worst case latency values the next
> power domains states can be determined. Unfortunately this is not
> (yet) implemented.

I thought about that too, but I'd like to discuss the basic idea first.

> I am wondering if the patches [4-5/5] are meant to replace the OMAP
> code, which would be really nice.

I certainly hope they will be useful for multiple platforms.  Whether
or not OMAP turns out to be one of them I can't tell at the moment.

Thanks,
Rafael

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-01 22:07       ` Rafael J. Wysocki
@ 2011-09-02  6:49         ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-02  6:49 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Thursday, September 01, 2011, Jean Pihet wrote:
>> Hi Rafael,
>>
>> On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > To read the current PM QoS value for a given device we need to
>> > make sure that the device's power.constraints object won't be
>> > removed while we're doing that.  For this reason, put the
>> > operation under dev->power.lock and acquire the lock
>> > around the initialization and removal of power.constraints.
>> Ok.
>>
>> > Moreover, since we're using the value of power.constraints to
>> > determine whether or not the object is present, the
>> > power.constraints_state field isn't necessary any more and may be
>> > removed.  However, dev_pm_qos_add_request() needs to check if the
>> > device is being removed from the system before allocating a new
>> > PM QoS constraints object for it, so it has to use device_pm_lock()
>> > and the device PM QoS initialization and destruction should be done
>> > under device_pm_lock() as well.
>> Ok that makes sense.
>> The constraints_state field can be replaced by a combination of
>> dev->power.constraints and list_empty(&dev->power.entry), which makes
>> the code more compact and less redundant.
>>
>> >
>> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > ---
>> >  drivers/base/power/main.c |    4 -
>> >  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
>> >  include/linux/pm.h        |    8 --
>> >  include/linux/pm_qos.h    |    3
>> >  4 files changed, 101 insertions(+), 81 deletions(-)
>> >
>> > Index: linux/drivers/base/power/qos.c
>> > =================================>> > --- linux.orig/drivers/base/power/qos.c
>> > +++ linux/drivers/base/power/qos.c
>> > @@ -30,15 +30,6 @@
>> ...
>>
>> >
>> > @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
>> >  *
>> >  * Returns 1 if the aggregated constraint value has changed,
>> >  * 0 if the aggregated constraint value has not changed,
>> > - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
>> > - * removed from the system
>> > + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
>> > + * to allocate for data structures.
>> Why not use -ENODEV in case there is no device?
>
> I don't think it's useful for the caller.  If the device is gone, the
> constraing simply doesn't matter, so there's no error to handle.
>
>> >  */
>> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>> >                           s32 value)
>> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
>> >                return -EINVAL;
>> >        }
>> >
>> > -       mutex_lock(&dev_pm_qos_mtx);
>> >        req->dev = dev;
>> >
>> > -       /* Return if the device has been removed */
>> > -       if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
>> > -               ret = -ENODEV;
>> > -               goto out;
>> > -       }
>> > +       device_pm_lock();
>> > +       mutex_lock(&dev_pm_qos_mtx);
>> >
>> > -       /*
>> > -        * Allocate the constraints data on the first call to add_request,
>> > -        * i.e. only if the data is not already allocated and if the device has
>> > -        * not been removed
>> > -        */
>> > -       if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
>> > -               ret = dev_pm_qos_constraints_allocate(dev);
>> > +       if (dev->power.constraints) {
>> > +               device_pm_unlock();
>> > +       } else {
>> > +               if (list_empty(&dev->power.entry)) {
>> > +                       /* The device has been removed from the system. */
>> > +                       device_pm_unlock();
>> > +                       goto out;
>> 0 is silently returned in case the device has been removed. Is that
>> the intention?
>
> Pretty much it is.  Is that a problem?
I think the caller needs to know if the constraint has been applied
correctly or not. That is why I changed the API functions to int.

Note: I still need to come with an API documentation patch after the
code is settled down.

>
> Rafael
>

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
@ 2011-09-02  6:49         ` Jean Pihet
  0 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-02  6:49 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Thursday, September 01, 2011, Jean Pihet wrote:
>> Hi Rafael,
>>
>> On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > To read the current PM QoS value for a given device we need to
>> > make sure that the device's power.constraints object won't be
>> > removed while we're doing that.  For this reason, put the
>> > operation under dev->power.lock and acquire the lock
>> > around the initialization and removal of power.constraints.
>> Ok.
>>
>> > Moreover, since we're using the value of power.constraints to
>> > determine whether or not the object is present, the
>> > power.constraints_state field isn't necessary any more and may be
>> > removed.  However, dev_pm_qos_add_request() needs to check if the
>> > device is being removed from the system before allocating a new
>> > PM QoS constraints object for it, so it has to use device_pm_lock()
>> > and the device PM QoS initialization and destruction should be done
>> > under device_pm_lock() as well.
>> Ok that makes sense.
>> The constraints_state field can be replaced by a combination of
>> dev->power.constraints and list_empty(&dev->power.entry), which makes
>> the code more compact and less redundant.
>>
>> >
>> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > ---
>> >  drivers/base/power/main.c |    4 -
>> >  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
>> >  include/linux/pm.h        |    8 --
>> >  include/linux/pm_qos.h    |    3
>> >  4 files changed, 101 insertions(+), 81 deletions(-)
>> >
>> > Index: linux/drivers/base/power/qos.c
>> > ===================================================================
>> > --- linux.orig/drivers/base/power/qos.c
>> > +++ linux/drivers/base/power/qos.c
>> > @@ -30,15 +30,6 @@
>> ...
>>
>> >
>> > @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
>> >  *
>> >  * Returns 1 if the aggregated constraint value has changed,
>> >  * 0 if the aggregated constraint value has not changed,
>> > - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
>> > - * removed from the system
>> > + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
>> > + * to allocate for data structures.
>> Why not use -ENODEV in case there is no device?
>
> I don't think it's useful for the caller.  If the device is gone, the
> constraing simply doesn't matter, so there's no error to handle.
>
>> >  */
>> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>> >                           s32 value)
>> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
>> >                return -EINVAL;
>> >        }
>> >
>> > -       mutex_lock(&dev_pm_qos_mtx);
>> >        req->dev = dev;
>> >
>> > -       /* Return if the device has been removed */
>> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
>> > -               ret = -ENODEV;
>> > -               goto out;
>> > -       }
>> > +       device_pm_lock();
>> > +       mutex_lock(&dev_pm_qos_mtx);
>> >
>> > -       /*
>> > -        * Allocate the constraints data on the first call to add_request,
>> > -        * i.e. only if the data is not already allocated and if the device has
>> > -        * not been removed
>> > -        */
>> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
>> > -               ret = dev_pm_qos_constraints_allocate(dev);
>> > +       if (dev->power.constraints) {
>> > +               device_pm_unlock();
>> > +       } else {
>> > +               if (list_empty(&dev->power.entry)) {
>> > +                       /* The device has been removed from the system. */
>> > +                       device_pm_unlock();
>> > +                       goto out;
>> 0 is silently returned in case the device has been removed. Is that
>> the intention?
>
> Pretty much it is.  Is that a problem?
I think the caller needs to know if the constraint has been applied
correctly or not. That is why I changed the API functions to int.

Note: I still need to come with an API documentation patch after the
code is settled down.

>
> Rafael
>

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-01 22:07       ` Rafael J. Wysocki
  (?)
@ 2011-09-02  6:49       ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-02  6:49 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM mailing list, LKML, Linux-sh list

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Thursday, September 01, 2011, Jean Pihet wrote:
>> Hi Rafael,
>>
>> On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > To read the current PM QoS value for a given device we need to
>> > make sure that the device's power.constraints object won't be
>> > removed while we're doing that.  For this reason, put the
>> > operation under dev->power.lock and acquire the lock
>> > around the initialization and removal of power.constraints.
>> Ok.
>>
>> > Moreover, since we're using the value of power.constraints to
>> > determine whether or not the object is present, the
>> > power.constraints_state field isn't necessary any more and may be
>> > removed.  However, dev_pm_qos_add_request() needs to check if the
>> > device is being removed from the system before allocating a new
>> > PM QoS constraints object for it, so it has to use device_pm_lock()
>> > and the device PM QoS initialization and destruction should be done
>> > under device_pm_lock() as well.
>> Ok that makes sense.
>> The constraints_state field can be replaced by a combination of
>> dev->power.constraints and list_empty(&dev->power.entry), which makes
>> the code more compact and less redundant.
>>
>> >
>> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > ---
>> >  drivers/base/power/main.c |    4 -
>> >  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
>> >  include/linux/pm.h        |    8 --
>> >  include/linux/pm_qos.h    |    3
>> >  4 files changed, 101 insertions(+), 81 deletions(-)
>> >
>> > Index: linux/drivers/base/power/qos.c
>> > ===================================================================
>> > --- linux.orig/drivers/base/power/qos.c
>> > +++ linux/drivers/base/power/qos.c
>> > @@ -30,15 +30,6 @@
>> ...
>>
>> >
>> > @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
>> >  *
>> >  * Returns 1 if the aggregated constraint value has changed,
>> >  * 0 if the aggregated constraint value has not changed,
>> > - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
>> > - * removed from the system
>> > + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
>> > + * to allocate for data structures.
>> Why not use -ENODEV in case there is no device?
>
> I don't think it's useful for the caller.  If the device is gone, the
> constraing simply doesn't matter, so there's no error to handle.
>
>> >  */
>> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>> >                           s32 value)
>> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
>> >                return -EINVAL;
>> >        }
>> >
>> > -       mutex_lock(&dev_pm_qos_mtx);
>> >        req->dev = dev;
>> >
>> > -       /* Return if the device has been removed */
>> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
>> > -               ret = -ENODEV;
>> > -               goto out;
>> > -       }
>> > +       device_pm_lock();
>> > +       mutex_lock(&dev_pm_qos_mtx);
>> >
>> > -       /*
>> > -        * Allocate the constraints data on the first call to add_request,
>> > -        * i.e. only if the data is not already allocated and if the device has
>> > -        * not been removed
>> > -        */
>> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
>> > -               ret = dev_pm_qos_constraints_allocate(dev);
>> > +       if (dev->power.constraints) {
>> > +               device_pm_unlock();
>> > +       } else {
>> > +               if (list_empty(&dev->power.entry)) {
>> > +                       /* The device has been removed from the system. */
>> > +                       device_pm_unlock();
>> > +                       goto out;
>> 0 is silently returned in case the device has been removed. Is that
>> the intention?
>
> Pretty much it is.  Is that a problem?
I think the caller needs to know if the constraint has been applied
correctly or not. That is why I changed the API functions to int.

Note: I still need to come with an API documentation patch after the
code is settled down.

>
> Rafael
>

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-02  6:49         ` Jean Pihet
@ 2011-09-02 23:55           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-02 23:55 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

On Friday, September 02, 2011, Jean Pihet wrote:
> On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Thursday, September 01, 2011, Jean Pihet wrote:
> >> Hi Rafael,
> >>
> >> On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > To read the current PM QoS value for a given device we need to
> >> > make sure that the device's power.constraints object won't be
> >> > removed while we're doing that.  For this reason, put the
> >> > operation under dev->power.lock and acquire the lock
> >> > around the initialization and removal of power.constraints.
> >> Ok.
> >>
> >> > Moreover, since we're using the value of power.constraints to
> >> > determine whether or not the object is present, the
> >> > power.constraints_state field isn't necessary any more and may be
> >> > removed.  However, dev_pm_qos_add_request() needs to check if the
> >> > device is being removed from the system before allocating a new
> >> > PM QoS constraints object for it, so it has to use device_pm_lock()
> >> > and the device PM QoS initialization and destruction should be done
> >> > under device_pm_lock() as well.
> >> Ok that makes sense.
> >> The constraints_state field can be replaced by a combination of
> >> dev->power.constraints and list_empty(&dev->power.entry), which makes
> >> the code more compact and less redundant.
> >>
> >> >
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > ---
> >> >  drivers/base/power/main.c |    4 -
> >> >  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
> >> >  include/linux/pm.h        |    8 --
> >> >  include/linux/pm_qos.h    |    3
> >> >  4 files changed, 101 insertions(+), 81 deletions(-)
> >> >
> >> > Index: linux/drivers/base/power/qos.c
> >> > =================================> >> > --- linux.orig/drivers/base/power/qos.c
> >> > +++ linux/drivers/base/power/qos.c
> >> > @@ -30,15 +30,6 @@
> >> ...
> >>
> >> >
> >> > @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
> >> >  *
> >> >  * Returns 1 if the aggregated constraint value has changed,
> >> >  * 0 if the aggregated constraint value has not changed,
> >> > - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> >> > - * removed from the system
> >> > + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> >> > + * to allocate for data structures.
> >> Why not use -ENODEV in case there is no device?
> >
> > I don't think it's useful for the caller.  If the device is gone, the
> > constraing simply doesn't matter, so there's no error to handle.
> >
> >> >  */
> >> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
> >> >                           s32 value)
> >> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
> >> >                return -EINVAL;
> >> >        }
> >> >
> >> > -       mutex_lock(&dev_pm_qos_mtx);
> >> >        req->dev = dev;
> >> >
> >> > -       /* Return if the device has been removed */
> >> > -       if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
> >> > -               ret = -ENODEV;
> >> > -               goto out;
> >> > -       }
> >> > +       device_pm_lock();
> >> > +       mutex_lock(&dev_pm_qos_mtx);
> >> >
> >> > -       /*
> >> > -        * Allocate the constraints data on the first call to add_request,
> >> > -        * i.e. only if the data is not already allocated and if the device has
> >> > -        * not been removed
> >> > -        */
> >> > -       if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
> >> > -               ret = dev_pm_qos_constraints_allocate(dev);
> >> > +       if (dev->power.constraints) {
> >> > +               device_pm_unlock();
> >> > +       } else {
> >> > +               if (list_empty(&dev->power.entry)) {
> >> > +                       /* The device has been removed from the system. */
> >> > +                       device_pm_unlock();
> >> > +                       goto out;
> >> 0 is silently returned in case the device has been removed. Is that
> >> the intention?
> >
> > Pretty much it is.  Is that a problem?
> I think the caller needs to know if the constraint has been applied
> correctly or not.

However, the removal of the device is a special case.  What would the caller
be supposed to do when it learned that the device had been removed while it had
been trying to add a QoS constraing for it?  Not much I guess.

Thanks,
Rafael

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
@ 2011-09-02 23:55           ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-02 23:55 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

On Friday, September 02, 2011, Jean Pihet wrote:
> On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Thursday, September 01, 2011, Jean Pihet wrote:
> >> Hi Rafael,
> >>
> >> On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > To read the current PM QoS value for a given device we need to
> >> > make sure that the device's power.constraints object won't be
> >> > removed while we're doing that.  For this reason, put the
> >> > operation under dev->power.lock and acquire the lock
> >> > around the initialization and removal of power.constraints.
> >> Ok.
> >>
> >> > Moreover, since we're using the value of power.constraints to
> >> > determine whether or not the object is present, the
> >> > power.constraints_state field isn't necessary any more and may be
> >> > removed.  However, dev_pm_qos_add_request() needs to check if the
> >> > device is being removed from the system before allocating a new
> >> > PM QoS constraints object for it, so it has to use device_pm_lock()
> >> > and the device PM QoS initialization and destruction should be done
> >> > under device_pm_lock() as well.
> >> Ok that makes sense.
> >> The constraints_state field can be replaced by a combination of
> >> dev->power.constraints and list_empty(&dev->power.entry), which makes
> >> the code more compact and less redundant.
> >>
> >> >
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > ---
> >> >  drivers/base/power/main.c |    4 -
> >> >  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
> >> >  include/linux/pm.h        |    8 --
> >> >  include/linux/pm_qos.h    |    3
> >> >  4 files changed, 101 insertions(+), 81 deletions(-)
> >> >
> >> > Index: linux/drivers/base/power/qos.c
> >> > ===================================================================
> >> > --- linux.orig/drivers/base/power/qos.c
> >> > +++ linux/drivers/base/power/qos.c
> >> > @@ -30,15 +30,6 @@
> >> ...
> >>
> >> >
> >> > @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
> >> >  *
> >> >  * Returns 1 if the aggregated constraint value has changed,
> >> >  * 0 if the aggregated constraint value has not changed,
> >> > - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> >> > - * removed from the system
> >> > + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> >> > + * to allocate for data structures.
> >> Why not use -ENODEV in case there is no device?
> >
> > I don't think it's useful for the caller.  If the device is gone, the
> > constraing simply doesn't matter, so there's no error to handle.
> >
> >> >  */
> >> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
> >> >                           s32 value)
> >> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
> >> >                return -EINVAL;
> >> >        }
> >> >
> >> > -       mutex_lock(&dev_pm_qos_mtx);
> >> >        req->dev = dev;
> >> >
> >> > -       /* Return if the device has been removed */
> >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> >> > -               ret = -ENODEV;
> >> > -               goto out;
> >> > -       }
> >> > +       device_pm_lock();
> >> > +       mutex_lock(&dev_pm_qos_mtx);
> >> >
> >> > -       /*
> >> > -        * Allocate the constraints data on the first call to add_request,
> >> > -        * i.e. only if the data is not already allocated and if the device has
> >> > -        * not been removed
> >> > -        */
> >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> >> > -               ret = dev_pm_qos_constraints_allocate(dev);
> >> > +       if (dev->power.constraints) {
> >> > +               device_pm_unlock();
> >> > +       } else {
> >> > +               if (list_empty(&dev->power.entry)) {
> >> > +                       /* The device has been removed from the system. */
> >> > +                       device_pm_unlock();
> >> > +                       goto out;
> >> 0 is silently returned in case the device has been removed. Is that
> >> the intention?
> >
> > Pretty much it is.  Is that a problem?
> I think the caller needs to know if the constraint has been applied
> correctly or not.

However, the removal of the device is a special case.  What would the caller
be supposed to do when it learned that the device had been removed while it had
been trying to add a QoS constraing for it?  Not much I guess.

Thanks,
Rafael

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-02  6:49         ` Jean Pihet
  (?)
@ 2011-09-02 23:55         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-02 23:55 UTC (permalink / raw)
  To: Jean Pihet; +Cc: Linux PM mailing list, LKML, Linux-sh list

On Friday, September 02, 2011, Jean Pihet wrote:
> On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Thursday, September 01, 2011, Jean Pihet wrote:
> >> Hi Rafael,
> >>
> >> On Wed, Aug 31, 2011 at 12:21 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > To read the current PM QoS value for a given device we need to
> >> > make sure that the device's power.constraints object won't be
> >> > removed while we're doing that.  For this reason, put the
> >> > operation under dev->power.lock and acquire the lock
> >> > around the initialization and removal of power.constraints.
> >> Ok.
> >>
> >> > Moreover, since we're using the value of power.constraints to
> >> > determine whether or not the object is present, the
> >> > power.constraints_state field isn't necessary any more and may be
> >> > removed.  However, dev_pm_qos_add_request() needs to check if the
> >> > device is being removed from the system before allocating a new
> >> > PM QoS constraints object for it, so it has to use device_pm_lock()
> >> > and the device PM QoS initialization and destruction should be done
> >> > under device_pm_lock() as well.
> >> Ok that makes sense.
> >> The constraints_state field can be replaced by a combination of
> >> dev->power.constraints and list_empty(&dev->power.entry), which makes
> >> the code more compact and less redundant.
> >>
> >> >
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > ---
> >> >  drivers/base/power/main.c |    4 -
> >> >  drivers/base/power/qos.c  |  167 ++++++++++++++++++++++++++--------------------
> >> >  include/linux/pm.h        |    8 --
> >> >  include/linux/pm_qos.h    |    3
> >> >  4 files changed, 101 insertions(+), 81 deletions(-)
> >> >
> >> > Index: linux/drivers/base/power/qos.c
> >> > ===================================================================
> >> > --- linux.orig/drivers/base/power/qos.c
> >> > +++ linux/drivers/base/power/qos.c
> >> > @@ -30,15 +30,6 @@
> >> ...
> >>
> >> >
> >> > @@ -178,8 +202,8 @@ void dev_pm_qos_constraints_destroy(stru
> >> >  *
> >> >  * Returns 1 if the aggregated constraint value has changed,
> >> >  * 0 if the aggregated constraint value has not changed,
> >> > - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> >> > - * removed from the system
> >> > + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> >> > + * to allocate for data structures.
> >> Why not use -ENODEV in case there is no device?
> >
> > I don't think it's useful for the caller.  If the device is gone, the
> > constraing simply doesn't matter, so there's no error to handle.
> >
> >> >  */
> >> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
> >> >                           s32 value)
> >> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
> >> >                return -EINVAL;
> >> >        }
> >> >
> >> > -       mutex_lock(&dev_pm_qos_mtx);
> >> >        req->dev = dev;
> >> >
> >> > -       /* Return if the device has been removed */
> >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> >> > -               ret = -ENODEV;
> >> > -               goto out;
> >> > -       }
> >> > +       device_pm_lock();
> >> > +       mutex_lock(&dev_pm_qos_mtx);
> >> >
> >> > -       /*
> >> > -        * Allocate the constraints data on the first call to add_request,
> >> > -        * i.e. only if the data is not already allocated and if the device has
> >> > -        * not been removed
> >> > -        */
> >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> >> > -               ret = dev_pm_qos_constraints_allocate(dev);
> >> > +       if (dev->power.constraints) {
> >> > +               device_pm_unlock();
> >> > +       } else {
> >> > +               if (list_empty(&dev->power.entry)) {
> >> > +                       /* The device has been removed from the system. */
> >> > +                       device_pm_unlock();
> >> > +                       goto out;
> >> 0 is silently returned in case the device has been removed. Is that
> >> the intention?
> >
> > Pretty much it is.  Is that a problem?
> I think the caller needs to know if the constraint has been applied
> correctly or not.

However, the removal of the device is a special case.  What would the caller
be supposed to do when it learned that the device had been removed while it had
been trying to add a QoS constraing for it?  Not much I guess.

Thanks,
Rafael

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-02 23:55           ` Rafael J. Wysocki
@ 2011-09-03  8:02             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-03  8:02 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Saturday, September 03, 2011, Rafael J. Wysocki wrote:
> On Friday, September 02, 2011, Jean Pihet wrote:
> > On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
...
> > >> >
> > >> > -       mutex_lock(&dev_pm_qos_mtx);
> > >> >        req->dev = dev;
> > >> >
> > >> > -       /* Return if the device has been removed */
> > >> > -       if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
> > >> > -               ret = -ENODEV;
> > >> > -               goto out;
> > >> > -       }
> > >> > +       device_pm_lock();
> > >> > +       mutex_lock(&dev_pm_qos_mtx);
> > >> >
> > >> > -       /*
> > >> > -        * Allocate the constraints data on the first call to add_request,
> > >> > -        * i.e. only if the data is not already allocated and if the device has
> > >> > -        * not been removed
> > >> > -        */
> > >> > -       if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
> > >> > -               ret = dev_pm_qos_constraints_allocate(dev);
> > >> > +       if (dev->power.constraints) {
> > >> > +               device_pm_unlock();
> > >> > +       } else {
> > >> > +               if (list_empty(&dev->power.entry)) {
> > >> > +                       /* The device has been removed from the system. */
> > >> > +                       device_pm_unlock();
> > >> > +                       goto out;
> > >> 0 is silently returned in case the device has been removed. Is that
> > >> the intention?
> > >
> > > Pretty much it is.  Is that a problem?
> > I think the caller needs to know if the constraint has been applied
> > correctly or not.
> 
> However, the removal of the device is a special case.  What would the caller
> be supposed to do when it learned that the device had been removed while it had
> been trying to add a QoS constraing for it?  Not much I guess.

Anyway, since returning an error code in that case won't hurt either,
below is a v2 of the patch doing that and resetting the dev field in the
req structure.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / QoS: Add function dev_pm_qos_read_value() (v2)

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  170 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 104 insertions(+), 81 deletions(-)

Index: linux/drivers/base/power/qos.c
=================================--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,13 +286,13 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
 	} else {
 		/* Return if the device has been removed */
-		ret = -ENODEV;
+		ret = -EINVAL;
 	}
 
 	mutex_unlock(&dev_pm_qos_mtx);
@@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
=================================--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
=================================--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
@ 2011-09-03  8:02             ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-03  8:02 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Saturday, September 03, 2011, Rafael J. Wysocki wrote:
> On Friday, September 02, 2011, Jean Pihet wrote:
> > On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
...
> > >> >
> > >> > -       mutex_lock(&dev_pm_qos_mtx);
> > >> >        req->dev = dev;
> > >> >
> > >> > -       /* Return if the device has been removed */
> > >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> > >> > -               ret = -ENODEV;
> > >> > -               goto out;
> > >> > -       }
> > >> > +       device_pm_lock();
> > >> > +       mutex_lock(&dev_pm_qos_mtx);
> > >> >
> > >> > -       /*
> > >> > -        * Allocate the constraints data on the first call to add_request,
> > >> > -        * i.e. only if the data is not already allocated and if the device has
> > >> > -        * not been removed
> > >> > -        */
> > >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> > >> > -               ret = dev_pm_qos_constraints_allocate(dev);
> > >> > +       if (dev->power.constraints) {
> > >> > +               device_pm_unlock();
> > >> > +       } else {
> > >> > +               if (list_empty(&dev->power.entry)) {
> > >> > +                       /* The device has been removed from the system. */
> > >> > +                       device_pm_unlock();
> > >> > +                       goto out;
> > >> 0 is silently returned in case the device has been removed. Is that
> > >> the intention?
> > >
> > > Pretty much it is.  Is that a problem?
> > I think the caller needs to know if the constraint has been applied
> > correctly or not.
> 
> However, the removal of the device is a special case.  What would the caller
> be supposed to do when it learned that the device had been removed while it had
> been trying to add a QoS constraing for it?  Not much I guess.

Anyway, since returning an error code in that case won't hurt either,
below is a v2 of the patch doing that and resetting the dev field in the
req structure.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / QoS: Add function dev_pm_qos_read_value() (v2)

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  170 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 104 insertions(+), 81 deletions(-)

Index: linux/drivers/base/power/qos.c
===================================================================
--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,13 +286,13 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
 	} else {
 		/* Return if the device has been removed */
-		ret = -ENODEV;
+		ret = -EINVAL;
 	}
 
 	mutex_unlock(&dev_pm_qos_mtx);
@@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
===================================================================
--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
===================================================================
--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-02 23:55           ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-09-03  8:02           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-03  8:02 UTC (permalink / raw)
  To: Jean Pihet; +Cc: Linux PM mailing list, LKML, Linux-sh list

Hi,

On Saturday, September 03, 2011, Rafael J. Wysocki wrote:
> On Friday, September 02, 2011, Jean Pihet wrote:
> > On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
...
> > >> >
> > >> > -       mutex_lock(&dev_pm_qos_mtx);
> > >> >        req->dev = dev;
> > >> >
> > >> > -       /* Return if the device has been removed */
> > >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> > >> > -               ret = -ENODEV;
> > >> > -               goto out;
> > >> > -       }
> > >> > +       device_pm_lock();
> > >> > +       mutex_lock(&dev_pm_qos_mtx);
> > >> >
> > >> > -       /*
> > >> > -        * Allocate the constraints data on the first call to add_request,
> > >> > -        * i.e. only if the data is not already allocated and if the device has
> > >> > -        * not been removed
> > >> > -        */
> > >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> > >> > -               ret = dev_pm_qos_constraints_allocate(dev);
> > >> > +       if (dev->power.constraints) {
> > >> > +               device_pm_unlock();
> > >> > +       } else {
> > >> > +               if (list_empty(&dev->power.entry)) {
> > >> > +                       /* The device has been removed from the system. */
> > >> > +                       device_pm_unlock();
> > >> > +                       goto out;
> > >> 0 is silently returned in case the device has been removed. Is that
> > >> the intention?
> > >
> > > Pretty much it is.  Is that a problem?
> > I think the caller needs to know if the constraint has been applied
> > correctly or not.
> 
> However, the removal of the device is a special case.  What would the caller
> be supposed to do when it learned that the device had been removed while it had
> been trying to add a QoS constraing for it?  Not much I guess.

Anyway, since returning an error code in that case won't hurt either,
below is a v2 of the patch doing that and resetting the dev field in the
req structure.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / QoS: Add function dev_pm_qos_read_value() (v2)

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  170 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 104 insertions(+), 81 deletions(-)

Index: linux/drivers/base/power/qos.c
===================================================================
--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,13 +286,13 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
 	} else {
 		/* Return if the device has been removed */
-		ret = -ENODEV;
+		ret = -EINVAL;
 	}
 
 	mutex_unlock(&dev_pm_qos_mtx);
@@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
===================================================================
--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
===================================================================
--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-02 23:55           ` Rafael J. Wysocki
@ 2011-09-05  7:44             ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-05  7:44 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi Rafael,

On Sat, Sep 3, 2011 at 1:55 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Friday, September 02, 2011, Jean Pihet wrote:
>> On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
...

>> >> >  */
>> >> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>> >> >                           s32 value)
>> >> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
>> >> >                return -EINVAL;
>> >> >        }
>> >> >
>> >> > -       mutex_lock(&dev_pm_qos_mtx);
>> >> >        req->dev = dev;
>> >> >
>> >> > -       /* Return if the device has been removed */
>> >> > -       if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
>> >> > -               ret = -ENODEV;
>> >> > -               goto out;
>> >> > -       }
>> >> > +       device_pm_lock();
>> >> > +       mutex_lock(&dev_pm_qos_mtx);
>> >> >
>> >> > -       /*
>> >> > -        * Allocate the constraints data on the first call to add_request,
>> >> > -        * i.e. only if the data is not already allocated and if the device has
>> >> > -        * not been removed
>> >> > -        */
>> >> > -       if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
>> >> > -               ret = dev_pm_qos_constraints_allocate(dev);
>> >> > +       if (dev->power.constraints) {
>> >> > +               device_pm_unlock();
>> >> > +       } else {
>> >> > +               if (list_empty(&dev->power.entry)) {
>> >> > +                       /* The device has been removed from the system. */
>> >> > +                       device_pm_unlock();
>> >> > +                       goto out;
>> >> 0 is silently returned in case the device has been removed. Is that
>> >> the intention?
>> >
>> > Pretty much it is.  Is that a problem?
>> I think the caller needs to know if the constraint has been applied
>> correctly or not.
>
> However, the removal of the device is a special case.  What would the caller
> be supposed to do when it learned that the device had been removed while it had
> been trying to add a QoS constraing for it?  Not much I guess.
When the device is removed from the system the constraints list id
flushed and the requests are zero'ed using memset.
In that case the caller needs to be aware of that otherwise the next
call to the API will throw an error.

>
> Thanks,
> Rafael
>

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
@ 2011-09-05  7:44             ` Jean Pihet
  0 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-05  7:44 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi Rafael,

On Sat, Sep 3, 2011 at 1:55 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Friday, September 02, 2011, Jean Pihet wrote:
>> On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
...

>> >> >  */
>> >> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>> >> >                           s32 value)
>> >> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
>> >> >                return -EINVAL;
>> >> >        }
>> >> >
>> >> > -       mutex_lock(&dev_pm_qos_mtx);
>> >> >        req->dev = dev;
>> >> >
>> >> > -       /* Return if the device has been removed */
>> >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
>> >> > -               ret = -ENODEV;
>> >> > -               goto out;
>> >> > -       }
>> >> > +       device_pm_lock();
>> >> > +       mutex_lock(&dev_pm_qos_mtx);
>> >> >
>> >> > -       /*
>> >> > -        * Allocate the constraints data on the first call to add_request,
>> >> > -        * i.e. only if the data is not already allocated and if the device has
>> >> > -        * not been removed
>> >> > -        */
>> >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
>> >> > -               ret = dev_pm_qos_constraints_allocate(dev);
>> >> > +       if (dev->power.constraints) {
>> >> > +               device_pm_unlock();
>> >> > +       } else {
>> >> > +               if (list_empty(&dev->power.entry)) {
>> >> > +                       /* The device has been removed from the system. */
>> >> > +                       device_pm_unlock();
>> >> > +                       goto out;
>> >> 0 is silently returned in case the device has been removed. Is that
>> >> the intention?
>> >
>> > Pretty much it is.  Is that a problem?
>> I think the caller needs to know if the constraint has been applied
>> correctly or not.
>
> However, the removal of the device is a special case.  What would the caller
> be supposed to do when it learned that the device had been removed while it had
> been trying to add a QoS constraing for it?  Not much I guess.
When the device is removed from the system the constraints list id
flushed and the requests are zero'ed using memset.
In that case the caller needs to be aware of that otherwise the next
call to the API will throw an error.

>
> Thanks,
> Rafael
>

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-02 23:55           ` Rafael J. Wysocki
                             ` (2 preceding siblings ...)
  (?)
@ 2011-09-05  7:44           ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-05  7:44 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM mailing list, LKML, Linux-sh list

Hi Rafael,

On Sat, Sep 3, 2011 at 1:55 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Friday, September 02, 2011, Jean Pihet wrote:
>> On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
...

>> >> >  */
>> >> >  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>> >> >                           s32 value)
>> >> > @@ -195,28 +219,35 @@ int dev_pm_qos_add_request(struct device
>> >> >                return -EINVAL;
>> >> >        }
>> >> >
>> >> > -       mutex_lock(&dev_pm_qos_mtx);
>> >> >        req->dev = dev;
>> >> >
>> >> > -       /* Return if the device has been removed */
>> >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
>> >> > -               ret = -ENODEV;
>> >> > -               goto out;
>> >> > -       }
>> >> > +       device_pm_lock();
>> >> > +       mutex_lock(&dev_pm_qos_mtx);
>> >> >
>> >> > -       /*
>> >> > -        * Allocate the constraints data on the first call to add_request,
>> >> > -        * i.e. only if the data is not already allocated and if the device has
>> >> > -        * not been removed
>> >> > -        */
>> >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
>> >> > -               ret = dev_pm_qos_constraints_allocate(dev);
>> >> > +       if (dev->power.constraints) {
>> >> > +               device_pm_unlock();
>> >> > +       } else {
>> >> > +               if (list_empty(&dev->power.entry)) {
>> >> > +                       /* The device has been removed from the system. */
>> >> > +                       device_pm_unlock();
>> >> > +                       goto out;
>> >> 0 is silently returned in case the device has been removed. Is that
>> >> the intention?
>> >
>> > Pretty much it is.  Is that a problem?
>> I think the caller needs to know if the constraint has been applied
>> correctly or not.
>
> However, the removal of the device is a special case.  What would the caller
> be supposed to do when it learned that the device had been removed while it had
> been trying to add a QoS constraing for it?  Not much I guess.
When the device is removed from the system the constraints list id
flushed and the requests are zero'ed using memset.
In that case the caller needs to be aware of that otherwise the next
call to the API will throw an error.

>
> Thanks,
> Rafael
>

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-03  8:02             ` Rafael J. Wysocki
@ 2011-09-05  7:51               ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-05  7:51 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Sat, Sep 3, 2011 at 10:02 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Saturday, September 03, 2011, Rafael J. Wysocki wrote:
>> On Friday, September 02, 2011, Jean Pihet wrote:
>> > On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> ...
>> > >> >
>> > >> > -       mutex_lock(&dev_pm_qos_mtx);
>> > >> >        req->dev = dev;
>> > >> >
>> > >> > -       /* Return if the device has been removed */
>> > >> > -       if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
>> > >> > -               ret = -ENODEV;
>> > >> > -               goto out;
>> > >> > -       }
>> > >> > +       device_pm_lock();
>> > >> > +       mutex_lock(&dev_pm_qos_mtx);
>> > >> >
>> > >> > -       /*
>> > >> > -        * Allocate the constraints data on the first call to add_request,
>> > >> > -        * i.e. only if the data is not already allocated and if the device has
>> > >> > -        * not been removed
>> > >> > -        */
>> > >> > -       if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
>> > >> > -               ret = dev_pm_qos_constraints_allocate(dev);
>> > >> > +       if (dev->power.constraints) {
>> > >> > +               device_pm_unlock();
>> > >> > +       } else {
>> > >> > +               if (list_empty(&dev->power.entry)) {
>> > >> > +                       /* The device has been removed from the system. */
>> > >> > +                       device_pm_unlock();
>> > >> > +                       goto out;
>> > >> 0 is silently returned in case the device has been removed. Is that
>> > >> the intention?
>> > >
>> > > Pretty much it is.  Is that a problem?
>> > I think the caller needs to know if the constraint has been applied
>> > correctly or not.
>>
>> However, the removal of the device is a special case.  What would the caller
>> be supposed to do when it learned that the device had been removed while it had
>> been trying to add a QoS constraing for it?  Not much I guess.
>
> Anyway, since returning an error code in that case won't hurt either,
> below is a v2 of the patch doing that and resetting the dev field in the
> req structure.
Ok thanks for the update. The comments are inlined below.

>
> Thanks,
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / QoS: Add function dev_pm_qos_read_value() (v2)
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
>
> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so it has to use device_pm_lock()
> and the device PM QoS initialization and destruction should be done
> under device_pm_lock() as well.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/base/power/main.c |    4 -
>  drivers/base/power/qos.c  |  170 ++++++++++++++++++++++++++--------------------
>  include/linux/pm.h        |    8 --
>  include/linux/pm_qos.h    |    3
>  4 files changed, 104 insertions(+), 81 deletions(-)
>
> Index: linux/drivers/base/power/qos.c
> =================================> --- linux.orig/drivers/base/power/qos.c
> +++ linux/drivers/base/power/qos.c
> @@ -30,15 +30,6 @@
>  * . To minimize the data usage by the per-device constraints, the data struct
>  *   is only allocated at the first call to dev_pm_qos_add_request.
>  * . The data is later free'd when the device is removed from the system.
> - * . The constraints_state variable from dev_pm_info tracks the data struct
> - *    allocation state:
> - *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
> - *     allocated,
> - *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
> - *     allocated at the first call to dev_pm_qos_add_request,
> - *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
> - *     PM QoS constraints framework is operational and constraints can be
> - *     added, updated or removed using the dev_pm_qos_* API.
>  *  . A global mutex protects the constraints users from the data being
>  *     allocated and free'd.
>  */
> @@ -51,8 +42,30 @@
>
>
>  static DEFINE_MUTEX(dev_pm_qos_mtx);
> +
>  static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
>
> +/**
> + * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
> + * @dev: Device to get the PM QoS constraint value for.
> + */
> +s32 dev_pm_qos_read_value(struct device *dev)
> +{
> +       struct pm_qos_constraints *c;
> +       unsigned long flags;
> +       s32 ret = 0;
> +
> +       spin_lock_irqsave(&dev->power.lock, flags);
> +
> +       c = dev->power.constraints;
> +       if (c)
> +               ret = pm_qos_read_value(c);
> +
> +       spin_unlock_irqrestore(&dev->power.lock, flags);
> +
> +       return ret;
> +}
> +
>  /*
>  * apply_constraint
>  * @req: constraint request to apply
> @@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
>        }
>        BLOCKING_INIT_NOTIFIER_HEAD(n);
>
> +       plist_head_init(&c->list);
> +       c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +       c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +       c->type = PM_QOS_MIN;
> +       c->notifiers = n;
> +
> +       spin_lock_irq(&dev->power.lock);
>        dev->power.constraints = c;
> -       plist_head_init(&dev->power.constraints->list);
> -       dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> -       dev->power.constraints->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> -       dev->power.constraints->type = PM_QOS_MIN;
> -       dev->power.constraints->notifiers = n;
> -       dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
> +       spin_unlock_irq(&dev->power.lock);
>
>        return 0;
>  }
>
> +static void __dev_pm_qos_constraints_init(struct device *dev)
> +{
> +       spin_lock_irq(&dev->power.lock);
> +       dev->power.constraints = NULL;
> +       spin_unlock_irq(&dev->power.lock);
> +}
> +
>  /**
> - * dev_pm_qos_constraints_init
> + * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
>  * @dev: target device
>  *
> - * Called from the device PM subsystem at device insertion
> + * Called from the device PM subsystem at device insertion under
> + * device_pm_lock().
>  */
>  void dev_pm_qos_constraints_init(struct device *dev)
>  {
>        mutex_lock(&dev_pm_qos_mtx);
> -       dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
> +       dev->power.constraints = NULL;
>        mutex_unlock(&dev_pm_qos_mtx);
>  }
>
> @@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
>  * dev_pm_qos_constraints_destroy
>  * @dev: target device
>  *
> - * Called from the device PM subsystem at device removal
> + * Called from the device PM subsystem at device removal under device_pm_lock().
>  */
>  void dev_pm_qos_constraints_destroy(struct device *dev)
>  {
>        struct dev_pm_qos_request *req, *tmp;
> +       struct pm_qos_constraints *c;
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       if (dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
> -               /* Flush the constraints list for the device */
> -               plist_for_each_entry_safe(req, tmp,
> -                                         &dev->power.constraints->list,
> -                                         node) {
> -                       /*
> -                        * Update constraints list and call the notification
> -                        * callbacks if needed
> -                        */
> -                       apply_constraint(req, PM_QOS_REMOVE_REQ,
> -                                        PM_QOS_DEFAULT_VALUE);
> -                       memset(req, 0, sizeof(*req));
> -               }
> +       c = dev->power.constraints;
> +       if (!c)
> +               goto out;
>
> -               kfree(dev->power.constraints->notifiers);
> -               kfree(dev->power.constraints);
> -               dev->power.constraints = NULL;
> +       /* Flush the constraints list for the device */
> +       plist_for_each_entry_safe(req, tmp, &c->list, node) {
> +               /*
> +                * Update constraints list and call the notification
> +                * callbacks if needed
> +                */
> +               apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
> +               memset(req, 0, sizeof(*req));
>        }
> -       dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
>
> +       __dev_pm_qos_constraints_init(dev);
> +
> +       kfree(c->notifiers);
> +       kfree(c);
> +
> + out:
>        mutex_unlock(&dev_pm_qos_mtx);
>  }
>
> @@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
>  *
>  * Returns 1 if the aggregated constraint value has changed,
>  * 0 if the aggregated constraint value has not changed,
> - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> - * removed from the system
> + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> + * to allocate for data structures, -ENODEV if the device has just been removed
> + * from the system.
>  */
>  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>                           s32 value)
> @@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
>                return -EINVAL;
>        }
>
> -       mutex_lock(&dev_pm_qos_mtx);
>        req->dev = dev;
>
> -       /* Return if the device has been removed */
> -       if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
> -               ret = -ENODEV;
> -               goto out;
> -       }
> +       device_pm_lock();
> +       mutex_lock(&dev_pm_qos_mtx);
>
> -       /*
> -        * Allocate the constraints data on the first call to add_request,
> -        * i.e. only if the data is not already allocated and if the device has
> -        * not been removed
> -        */
> -       if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
> -               ret = dev_pm_qos_constraints_allocate(dev);
> +       if (dev->power.constraints) {
> +               device_pm_unlock();
> +       } else {
> +               if (list_empty(&dev->power.entry)) {
> +                       /* The device has been removed from the system. */
> +                       device_pm_unlock();
> +                       req->dev = NULL;
> +                       ret = -ENODEV;
> +                       goto out;
> +               } else {
> +                       device_pm_unlock();
> +                       /*
> +                        * Allocate the constraints data on the first call to
> +                        * add_request, i.e. only if the data is not already
> +                        * allocated and if the device has not been removed.
> +                        */
> +                       ret = dev_pm_qos_constraints_allocate(dev);
> +               }
> +       }
>
>        if (!ret)
>                ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
>
> -out:
> + out:
>        mutex_unlock(&dev_pm_qos_mtx);
> +
>        return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
> @@ -252,13 +286,13 @@ int dev_pm_qos_update_request(struct dev
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
> +       if (req->dev->power.constraints) {
>                if (new_value != req->node.prio)
>                        ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
>                                               new_value);
>        } else {
>                /* Return if the device has been removed */
> -               ret = -ENODEV;
> +               ret = -EINVAL;
The retcode should be -ENODEV. Is the kerneldoc still OK?

>        }
>
>        mutex_unlock(&dev_pm_qos_mtx);
> @@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
> +       if (req->dev->power.constraints) {
>                ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
>                                       PM_QOS_DEFAULT_VALUE);
>                memset(req, 0, sizeof(*req));
> @@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       /* Silently return if the device has been removed */
> -       if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
> -               goto out;
> -
> -       retval = blocking_notifier_chain_register(
> -                       dev->power.constraints->notifiers,
> -                       notifier);
> +       /* Silently return if the constraints object is not present. */
> +       if (dev->power.constraints)
> +               retval = blocking_notifier_chain_register(
> +                               dev->power.constraints->notifiers,
> +                               notifier);
>
> -out:
>        mutex_unlock(&dev_pm_qos_mtx);
>        return retval;
>  }
> @@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       /* Silently return if the device has been removed */
> -       if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
> -               goto out;
> -
> -       retval = blocking_notifier_chain_unregister(
> -                       dev->power.constraints->notifiers,
> -                       notifier);
> +       /* Silently return if the constraints object is not present. */
> +       if (dev->power.constraints)
> +               retval = blocking_notifier_chain_unregister(
> +                               dev->power.constraints->notifiers,
> +                               notifier);
>
> -out:
>        mutex_unlock(&dev_pm_qos_mtx);
>        return retval;
>  }
> Index: linux/include/linux/pm_qos.h
> =================================> --- linux.orig/include/linux/pm_qos.h
> +++ linux/include/linux/pm_qos.h
> @@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
>  int pm_qos_request_active(struct pm_qos_request *req);
>  s32 pm_qos_read_value(struct pm_qos_constraints *c);
>
> +s32 dev_pm_qos_read_value(struct device *dev);
>  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>                           s32 value);
>  int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
> @@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
>  static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
>                        { return 0; }
>
> +static inline s32 dev_pm_qos_read_value(struct device *dev)
> +                       { return 0; }
>  static inline int dev_pm_qos_add_request(struct device *dev,
>                                         struct dev_pm_qos_request *req,
>                                         s32 value)
> Index: linux/drivers/base/power/main.c
> =================================> --- linux.orig/drivers/base/power/main.c
> +++ linux/drivers/base/power/main.c
> @@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
>                dev_warn(dev, "parent %s should not be sleeping\n",
>                        dev_name(dev->parent));
>        list_add_tail(&dev->power.entry, &dpm_list);
> -       mutex_unlock(&dpm_list_mtx);
>        dev_pm_qos_constraints_init(dev);
> +       mutex_unlock(&dpm_list_mtx);
>  }
>
>  /**
> @@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
>  {
>        pr_debug("PM: Removing info for %s:%s\n",
>                 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
> -       dev_pm_qos_constraints_destroy(dev);
>        complete_all(&dev->power.completion);
>        mutex_lock(&dpm_list_mtx);
> +       dev_pm_qos_constraints_destroy(dev);
>        list_del_init(&dev->power.entry);
>        mutex_unlock(&dpm_list_mtx);
>        device_wakeup_disable(dev);
> Index: linux/include/linux/pm.h
> =================================> --- linux.orig/include/linux/pm.h
> +++ linux/include/linux/pm.h
> @@ -421,13 +421,6 @@ enum rpm_request {
>        RPM_REQ_RESUME,
>  };
>
> -/* Per-device PM QoS constraints data struct state */
> -enum dev_pm_qos_state {
> -       DEV_PM_QOS_NO_DEVICE,           /* No device present */
> -       DEV_PM_QOS_DEVICE_PRESENT,      /* Device present, data not allocated */
> -       DEV_PM_QOS_ALLOCATED,           /* Device present, data allocated */
> -};
> -
>  struct wakeup_source;
>
>  struct pm_domain_data {
> @@ -489,7 +482,6 @@ struct dev_pm_info {
>  #endif
>        struct pm_subsys_data   *subsys_data;  /* Owned by the subsystem. */
>        struct pm_qos_constraints *constraints;
> -       enum dev_pm_qos_state   constraints_state;
>  };
>
>  extern void update_pm_runtime_accounting(struct device *dev);
>

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
@ 2011-09-05  7:51               ` Jean Pihet
  0 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-05  7:51 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

Hi,

On Sat, Sep 3, 2011 at 10:02 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Saturday, September 03, 2011, Rafael J. Wysocki wrote:
>> On Friday, September 02, 2011, Jean Pihet wrote:
>> > On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> ...
>> > >> >
>> > >> > -       mutex_lock(&dev_pm_qos_mtx);
>> > >> >        req->dev = dev;
>> > >> >
>> > >> > -       /* Return if the device has been removed */
>> > >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
>> > >> > -               ret = -ENODEV;
>> > >> > -               goto out;
>> > >> > -       }
>> > >> > +       device_pm_lock();
>> > >> > +       mutex_lock(&dev_pm_qos_mtx);
>> > >> >
>> > >> > -       /*
>> > >> > -        * Allocate the constraints data on the first call to add_request,
>> > >> > -        * i.e. only if the data is not already allocated and if the device has
>> > >> > -        * not been removed
>> > >> > -        */
>> > >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
>> > >> > -               ret = dev_pm_qos_constraints_allocate(dev);
>> > >> > +       if (dev->power.constraints) {
>> > >> > +               device_pm_unlock();
>> > >> > +       } else {
>> > >> > +               if (list_empty(&dev->power.entry)) {
>> > >> > +                       /* The device has been removed from the system. */
>> > >> > +                       device_pm_unlock();
>> > >> > +                       goto out;
>> > >> 0 is silently returned in case the device has been removed. Is that
>> > >> the intention?
>> > >
>> > > Pretty much it is.  Is that a problem?
>> > I think the caller needs to know if the constraint has been applied
>> > correctly or not.
>>
>> However, the removal of the device is a special case.  What would the caller
>> be supposed to do when it learned that the device had been removed while it had
>> been trying to add a QoS constraing for it?  Not much I guess.
>
> Anyway, since returning an error code in that case won't hurt either,
> below is a v2 of the patch doing that and resetting the dev field in the
> req structure.
Ok thanks for the update. The comments are inlined below.

>
> Thanks,
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / QoS: Add function dev_pm_qos_read_value() (v2)
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
>
> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so it has to use device_pm_lock()
> and the device PM QoS initialization and destruction should be done
> under device_pm_lock() as well.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/base/power/main.c |    4 -
>  drivers/base/power/qos.c  |  170 ++++++++++++++++++++++++++--------------------
>  include/linux/pm.h        |    8 --
>  include/linux/pm_qos.h    |    3
>  4 files changed, 104 insertions(+), 81 deletions(-)
>
> Index: linux/drivers/base/power/qos.c
> ===================================================================
> --- linux.orig/drivers/base/power/qos.c
> +++ linux/drivers/base/power/qos.c
> @@ -30,15 +30,6 @@
>  * . To minimize the data usage by the per-device constraints, the data struct
>  *   is only allocated at the first call to dev_pm_qos_add_request.
>  * . The data is later free'd when the device is removed from the system.
> - * . The constraints_state variable from dev_pm_info tracks the data struct
> - *    allocation state:
> - *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
> - *     allocated,
> - *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
> - *     allocated at the first call to dev_pm_qos_add_request,
> - *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
> - *     PM QoS constraints framework is operational and constraints can be
> - *     added, updated or removed using the dev_pm_qos_* API.
>  *  . A global mutex protects the constraints users from the data being
>  *     allocated and free'd.
>  */
> @@ -51,8 +42,30 @@
>
>
>  static DEFINE_MUTEX(dev_pm_qos_mtx);
> +
>  static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
>
> +/**
> + * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
> + * @dev: Device to get the PM QoS constraint value for.
> + */
> +s32 dev_pm_qos_read_value(struct device *dev)
> +{
> +       struct pm_qos_constraints *c;
> +       unsigned long flags;
> +       s32 ret = 0;
> +
> +       spin_lock_irqsave(&dev->power.lock, flags);
> +
> +       c = dev->power.constraints;
> +       if (c)
> +               ret = pm_qos_read_value(c);
> +
> +       spin_unlock_irqrestore(&dev->power.lock, flags);
> +
> +       return ret;
> +}
> +
>  /*
>  * apply_constraint
>  * @req: constraint request to apply
> @@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
>        }
>        BLOCKING_INIT_NOTIFIER_HEAD(n);
>
> +       plist_head_init(&c->list);
> +       c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +       c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +       c->type = PM_QOS_MIN;
> +       c->notifiers = n;
> +
> +       spin_lock_irq(&dev->power.lock);
>        dev->power.constraints = c;
> -       plist_head_init(&dev->power.constraints->list);
> -       dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> -       dev->power.constraints->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> -       dev->power.constraints->type = PM_QOS_MIN;
> -       dev->power.constraints->notifiers = n;
> -       dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
> +       spin_unlock_irq(&dev->power.lock);
>
>        return 0;
>  }
>
> +static void __dev_pm_qos_constraints_init(struct device *dev)
> +{
> +       spin_lock_irq(&dev->power.lock);
> +       dev->power.constraints = NULL;
> +       spin_unlock_irq(&dev->power.lock);
> +}
> +
>  /**
> - * dev_pm_qos_constraints_init
> + * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
>  * @dev: target device
>  *
> - * Called from the device PM subsystem at device insertion
> + * Called from the device PM subsystem at device insertion under
> + * device_pm_lock().
>  */
>  void dev_pm_qos_constraints_init(struct device *dev)
>  {
>        mutex_lock(&dev_pm_qos_mtx);
> -       dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
> +       dev->power.constraints = NULL;
>        mutex_unlock(&dev_pm_qos_mtx);
>  }
>
> @@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
>  * dev_pm_qos_constraints_destroy
>  * @dev: target device
>  *
> - * Called from the device PM subsystem at device removal
> + * Called from the device PM subsystem at device removal under device_pm_lock().
>  */
>  void dev_pm_qos_constraints_destroy(struct device *dev)
>  {
>        struct dev_pm_qos_request *req, *tmp;
> +       struct pm_qos_constraints *c;
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
> -               /* Flush the constraints list for the device */
> -               plist_for_each_entry_safe(req, tmp,
> -                                         &dev->power.constraints->list,
> -                                         node) {
> -                       /*
> -                        * Update constraints list and call the notification
> -                        * callbacks if needed
> -                        */
> -                       apply_constraint(req, PM_QOS_REMOVE_REQ,
> -                                        PM_QOS_DEFAULT_VALUE);
> -                       memset(req, 0, sizeof(*req));
> -               }
> +       c = dev->power.constraints;
> +       if (!c)
> +               goto out;
>
> -               kfree(dev->power.constraints->notifiers);
> -               kfree(dev->power.constraints);
> -               dev->power.constraints = NULL;
> +       /* Flush the constraints list for the device */
> +       plist_for_each_entry_safe(req, tmp, &c->list, node) {
> +               /*
> +                * Update constraints list and call the notification
> +                * callbacks if needed
> +                */
> +               apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
> +               memset(req, 0, sizeof(*req));
>        }
> -       dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
>
> +       __dev_pm_qos_constraints_init(dev);
> +
> +       kfree(c->notifiers);
> +       kfree(c);
> +
> + out:
>        mutex_unlock(&dev_pm_qos_mtx);
>  }
>
> @@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
>  *
>  * Returns 1 if the aggregated constraint value has changed,
>  * 0 if the aggregated constraint value has not changed,
> - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> - * removed from the system
> + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> + * to allocate for data structures, -ENODEV if the device has just been removed
> + * from the system.
>  */
>  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>                           s32 value)
> @@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
>                return -EINVAL;
>        }
>
> -       mutex_lock(&dev_pm_qos_mtx);
>        req->dev = dev;
>
> -       /* Return if the device has been removed */
> -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> -               ret = -ENODEV;
> -               goto out;
> -       }
> +       device_pm_lock();
> +       mutex_lock(&dev_pm_qos_mtx);
>
> -       /*
> -        * Allocate the constraints data on the first call to add_request,
> -        * i.e. only if the data is not already allocated and if the device has
> -        * not been removed
> -        */
> -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> -               ret = dev_pm_qos_constraints_allocate(dev);
> +       if (dev->power.constraints) {
> +               device_pm_unlock();
> +       } else {
> +               if (list_empty(&dev->power.entry)) {
> +                       /* The device has been removed from the system. */
> +                       device_pm_unlock();
> +                       req->dev = NULL;
> +                       ret = -ENODEV;
> +                       goto out;
> +               } else {
> +                       device_pm_unlock();
> +                       /*
> +                        * Allocate the constraints data on the first call to
> +                        * add_request, i.e. only if the data is not already
> +                        * allocated and if the device has not been removed.
> +                        */
> +                       ret = dev_pm_qos_constraints_allocate(dev);
> +               }
> +       }
>
>        if (!ret)
>                ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
>
> -out:
> + out:
>        mutex_unlock(&dev_pm_qos_mtx);
> +
>        return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
> @@ -252,13 +286,13 @@ int dev_pm_qos_update_request(struct dev
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
> +       if (req->dev->power.constraints) {
>                if (new_value != req->node.prio)
>                        ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
>                                               new_value);
>        } else {
>                /* Return if the device has been removed */
> -               ret = -ENODEV;
> +               ret = -EINVAL;
The retcode should be -ENODEV. Is the kerneldoc still OK?

>        }
>
>        mutex_unlock(&dev_pm_qos_mtx);
> @@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
> +       if (req->dev->power.constraints) {
>                ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
>                                       PM_QOS_DEFAULT_VALUE);
>                memset(req, 0, sizeof(*req));
> @@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       /* Silently return if the device has been removed */
> -       if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
> -               goto out;
> -
> -       retval = blocking_notifier_chain_register(
> -                       dev->power.constraints->notifiers,
> -                       notifier);
> +       /* Silently return if the constraints object is not present. */
> +       if (dev->power.constraints)
> +               retval = blocking_notifier_chain_register(
> +                               dev->power.constraints->notifiers,
> +                               notifier);
>
> -out:
>        mutex_unlock(&dev_pm_qos_mtx);
>        return retval;
>  }
> @@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       /* Silently return if the device has been removed */
> -       if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
> -               goto out;
> -
> -       retval = blocking_notifier_chain_unregister(
> -                       dev->power.constraints->notifiers,
> -                       notifier);
> +       /* Silently return if the constraints object is not present. */
> +       if (dev->power.constraints)
> +               retval = blocking_notifier_chain_unregister(
> +                               dev->power.constraints->notifiers,
> +                               notifier);
>
> -out:
>        mutex_unlock(&dev_pm_qos_mtx);
>        return retval;
>  }
> Index: linux/include/linux/pm_qos.h
> ===================================================================
> --- linux.orig/include/linux/pm_qos.h
> +++ linux/include/linux/pm_qos.h
> @@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
>  int pm_qos_request_active(struct pm_qos_request *req);
>  s32 pm_qos_read_value(struct pm_qos_constraints *c);
>
> +s32 dev_pm_qos_read_value(struct device *dev);
>  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>                           s32 value);
>  int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
> @@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
>  static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
>                        { return 0; }
>
> +static inline s32 dev_pm_qos_read_value(struct device *dev)
> +                       { return 0; }
>  static inline int dev_pm_qos_add_request(struct device *dev,
>                                         struct dev_pm_qos_request *req,
>                                         s32 value)
> Index: linux/drivers/base/power/main.c
> ===================================================================
> --- linux.orig/drivers/base/power/main.c
> +++ linux/drivers/base/power/main.c
> @@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
>                dev_warn(dev, "parent %s should not be sleeping\n",
>                        dev_name(dev->parent));
>        list_add_tail(&dev->power.entry, &dpm_list);
> -       mutex_unlock(&dpm_list_mtx);
>        dev_pm_qos_constraints_init(dev);
> +       mutex_unlock(&dpm_list_mtx);
>  }
>
>  /**
> @@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
>  {
>        pr_debug("PM: Removing info for %s:%s\n",
>                 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
> -       dev_pm_qos_constraints_destroy(dev);
>        complete_all(&dev->power.completion);
>        mutex_lock(&dpm_list_mtx);
> +       dev_pm_qos_constraints_destroy(dev);
>        list_del_init(&dev->power.entry);
>        mutex_unlock(&dpm_list_mtx);
>        device_wakeup_disable(dev);
> Index: linux/include/linux/pm.h
> ===================================================================
> --- linux.orig/include/linux/pm.h
> +++ linux/include/linux/pm.h
> @@ -421,13 +421,6 @@ enum rpm_request {
>        RPM_REQ_RESUME,
>  };
>
> -/* Per-device PM QoS constraints data struct state */
> -enum dev_pm_qos_state {
> -       DEV_PM_QOS_NO_DEVICE,           /* No device present */
> -       DEV_PM_QOS_DEVICE_PRESENT,      /* Device present, data not allocated */
> -       DEV_PM_QOS_ALLOCATED,           /* Device present, data allocated */
> -};
> -
>  struct wakeup_source;
>
>  struct pm_domain_data {
> @@ -489,7 +482,6 @@ struct dev_pm_info {
>  #endif
>        struct pm_subsys_data   *subsys_data;  /* Owned by the subsystem. */
>        struct pm_qos_constraints *constraints;
> -       enum dev_pm_qos_state   constraints_state;
>  };
>
>  extern void update_pm_runtime_accounting(struct device *dev);
>

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-03  8:02             ` Rafael J. Wysocki
  (?)
@ 2011-09-05  7:51             ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-05  7:51 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM mailing list, LKML, Linux-sh list

Hi,

On Sat, Sep 3, 2011 at 10:02 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Saturday, September 03, 2011, Rafael J. Wysocki wrote:
>> On Friday, September 02, 2011, Jean Pihet wrote:
>> > On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> ...
>> > >> >
>> > >> > -       mutex_lock(&dev_pm_qos_mtx);
>> > >> >        req->dev = dev;
>> > >> >
>> > >> > -       /* Return if the device has been removed */
>> > >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
>> > >> > -               ret = -ENODEV;
>> > >> > -               goto out;
>> > >> > -       }
>> > >> > +       device_pm_lock();
>> > >> > +       mutex_lock(&dev_pm_qos_mtx);
>> > >> >
>> > >> > -       /*
>> > >> > -        * Allocate the constraints data on the first call to add_request,
>> > >> > -        * i.e. only if the data is not already allocated and if the device has
>> > >> > -        * not been removed
>> > >> > -        */
>> > >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
>> > >> > -               ret = dev_pm_qos_constraints_allocate(dev);
>> > >> > +       if (dev->power.constraints) {
>> > >> > +               device_pm_unlock();
>> > >> > +       } else {
>> > >> > +               if (list_empty(&dev->power.entry)) {
>> > >> > +                       /* The device has been removed from the system. */
>> > >> > +                       device_pm_unlock();
>> > >> > +                       goto out;
>> > >> 0 is silently returned in case the device has been removed. Is that
>> > >> the intention?
>> > >
>> > > Pretty much it is.  Is that a problem?
>> > I think the caller needs to know if the constraint has been applied
>> > correctly or not.
>>
>> However, the removal of the device is a special case.  What would the caller
>> be supposed to do when it learned that the device had been removed while it had
>> been trying to add a QoS constraing for it?  Not much I guess.
>
> Anyway, since returning an error code in that case won't hurt either,
> below is a v2 of the patch doing that and resetting the dev field in the
> req structure.
Ok thanks for the update. The comments are inlined below.

>
> Thanks,
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / QoS: Add function dev_pm_qos_read_value() (v2)
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
>
> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so it has to use device_pm_lock()
> and the device PM QoS initialization and destruction should be done
> under device_pm_lock() as well.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/base/power/main.c |    4 -
>  drivers/base/power/qos.c  |  170 ++++++++++++++++++++++++++--------------------
>  include/linux/pm.h        |    8 --
>  include/linux/pm_qos.h    |    3
>  4 files changed, 104 insertions(+), 81 deletions(-)
>
> Index: linux/drivers/base/power/qos.c
> ===================================================================
> --- linux.orig/drivers/base/power/qos.c
> +++ linux/drivers/base/power/qos.c
> @@ -30,15 +30,6 @@
>  * . To minimize the data usage by the per-device constraints, the data struct
>  *   is only allocated at the first call to dev_pm_qos_add_request.
>  * . The data is later free'd when the device is removed from the system.
> - * . The constraints_state variable from dev_pm_info tracks the data struct
> - *    allocation state:
> - *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
> - *     allocated,
> - *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
> - *     allocated at the first call to dev_pm_qos_add_request,
> - *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
> - *     PM QoS constraints framework is operational and constraints can be
> - *     added, updated or removed using the dev_pm_qos_* API.
>  *  . A global mutex protects the constraints users from the data being
>  *     allocated and free'd.
>  */
> @@ -51,8 +42,30 @@
>
>
>  static DEFINE_MUTEX(dev_pm_qos_mtx);
> +
>  static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
>
> +/**
> + * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
> + * @dev: Device to get the PM QoS constraint value for.
> + */
> +s32 dev_pm_qos_read_value(struct device *dev)
> +{
> +       struct pm_qos_constraints *c;
> +       unsigned long flags;
> +       s32 ret = 0;
> +
> +       spin_lock_irqsave(&dev->power.lock, flags);
> +
> +       c = dev->power.constraints;
> +       if (c)
> +               ret = pm_qos_read_value(c);
> +
> +       spin_unlock_irqrestore(&dev->power.lock, flags);
> +
> +       return ret;
> +}
> +
>  /*
>  * apply_constraint
>  * @req: constraint request to apply
> @@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
>        }
>        BLOCKING_INIT_NOTIFIER_HEAD(n);
>
> +       plist_head_init(&c->list);
> +       c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +       c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +       c->type = PM_QOS_MIN;
> +       c->notifiers = n;
> +
> +       spin_lock_irq(&dev->power.lock);
>        dev->power.constraints = c;
> -       plist_head_init(&dev->power.constraints->list);
> -       dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> -       dev->power.constraints->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> -       dev->power.constraints->type = PM_QOS_MIN;
> -       dev->power.constraints->notifiers = n;
> -       dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
> +       spin_unlock_irq(&dev->power.lock);
>
>        return 0;
>  }
>
> +static void __dev_pm_qos_constraints_init(struct device *dev)
> +{
> +       spin_lock_irq(&dev->power.lock);
> +       dev->power.constraints = NULL;
> +       spin_unlock_irq(&dev->power.lock);
> +}
> +
>  /**
> - * dev_pm_qos_constraints_init
> + * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
>  * @dev: target device
>  *
> - * Called from the device PM subsystem at device insertion
> + * Called from the device PM subsystem at device insertion under
> + * device_pm_lock().
>  */
>  void dev_pm_qos_constraints_init(struct device *dev)
>  {
>        mutex_lock(&dev_pm_qos_mtx);
> -       dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
> +       dev->power.constraints = NULL;
>        mutex_unlock(&dev_pm_qos_mtx);
>  }
>
> @@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
>  * dev_pm_qos_constraints_destroy
>  * @dev: target device
>  *
> - * Called from the device PM subsystem at device removal
> + * Called from the device PM subsystem at device removal under device_pm_lock().
>  */
>  void dev_pm_qos_constraints_destroy(struct device *dev)
>  {
>        struct dev_pm_qos_request *req, *tmp;
> +       struct pm_qos_constraints *c;
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
> -               /* Flush the constraints list for the device */
> -               plist_for_each_entry_safe(req, tmp,
> -                                         &dev->power.constraints->list,
> -                                         node) {
> -                       /*
> -                        * Update constraints list and call the notification
> -                        * callbacks if needed
> -                        */
> -                       apply_constraint(req, PM_QOS_REMOVE_REQ,
> -                                        PM_QOS_DEFAULT_VALUE);
> -                       memset(req, 0, sizeof(*req));
> -               }
> +       c = dev->power.constraints;
> +       if (!c)
> +               goto out;
>
> -               kfree(dev->power.constraints->notifiers);
> -               kfree(dev->power.constraints);
> -               dev->power.constraints = NULL;
> +       /* Flush the constraints list for the device */
> +       plist_for_each_entry_safe(req, tmp, &c->list, node) {
> +               /*
> +                * Update constraints list and call the notification
> +                * callbacks if needed
> +                */
> +               apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
> +               memset(req, 0, sizeof(*req));
>        }
> -       dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
>
> +       __dev_pm_qos_constraints_init(dev);
> +
> +       kfree(c->notifiers);
> +       kfree(c);
> +
> + out:
>        mutex_unlock(&dev_pm_qos_mtx);
>  }
>
> @@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
>  *
>  * Returns 1 if the aggregated constraint value has changed,
>  * 0 if the aggregated constraint value has not changed,
> - * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> - * removed from the system
> + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
> + * to allocate for data structures, -ENODEV if the device has just been removed
> + * from the system.
>  */
>  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>                           s32 value)
> @@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
>                return -EINVAL;
>        }
>
> -       mutex_lock(&dev_pm_qos_mtx);
>        req->dev = dev;
>
> -       /* Return if the device has been removed */
> -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> -               ret = -ENODEV;
> -               goto out;
> -       }
> +       device_pm_lock();
> +       mutex_lock(&dev_pm_qos_mtx);
>
> -       /*
> -        * Allocate the constraints data on the first call to add_request,
> -        * i.e. only if the data is not already allocated and if the device has
> -        * not been removed
> -        */
> -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> -               ret = dev_pm_qos_constraints_allocate(dev);
> +       if (dev->power.constraints) {
> +               device_pm_unlock();
> +       } else {
> +               if (list_empty(&dev->power.entry)) {
> +                       /* The device has been removed from the system. */
> +                       device_pm_unlock();
> +                       req->dev = NULL;
> +                       ret = -ENODEV;
> +                       goto out;
> +               } else {
> +                       device_pm_unlock();
> +                       /*
> +                        * Allocate the constraints data on the first call to
> +                        * add_request, i.e. only if the data is not already
> +                        * allocated and if the device has not been removed.
> +                        */
> +                       ret = dev_pm_qos_constraints_allocate(dev);
> +               }
> +       }
>
>        if (!ret)
>                ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
>
> -out:
> + out:
>        mutex_unlock(&dev_pm_qos_mtx);
> +
>        return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
> @@ -252,13 +286,13 @@ int dev_pm_qos_update_request(struct dev
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
> +       if (req->dev->power.constraints) {
>                if (new_value != req->node.prio)
>                        ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
>                                               new_value);
>        } else {
>                /* Return if the device has been removed */
> -               ret = -ENODEV;
> +               ret = -EINVAL;
The retcode should be -ENODEV. Is the kerneldoc still OK?

>        }
>
>        mutex_unlock(&dev_pm_qos_mtx);
> @@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
> +       if (req->dev->power.constraints) {
>                ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
>                                       PM_QOS_DEFAULT_VALUE);
>                memset(req, 0, sizeof(*req));
> @@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       /* Silently return if the device has been removed */
> -       if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
> -               goto out;
> -
> -       retval = blocking_notifier_chain_register(
> -                       dev->power.constraints->notifiers,
> -                       notifier);
> +       /* Silently return if the constraints object is not present. */
> +       if (dev->power.constraints)
> +               retval = blocking_notifier_chain_register(
> +                               dev->power.constraints->notifiers,
> +                               notifier);
>
> -out:
>        mutex_unlock(&dev_pm_qos_mtx);
>        return retval;
>  }
> @@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
>
>        mutex_lock(&dev_pm_qos_mtx);
>
> -       /* Silently return if the device has been removed */
> -       if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
> -               goto out;
> -
> -       retval = blocking_notifier_chain_unregister(
> -                       dev->power.constraints->notifiers,
> -                       notifier);
> +       /* Silently return if the constraints object is not present. */
> +       if (dev->power.constraints)
> +               retval = blocking_notifier_chain_unregister(
> +                               dev->power.constraints->notifiers,
> +                               notifier);
>
> -out:
>        mutex_unlock(&dev_pm_qos_mtx);
>        return retval;
>  }
> Index: linux/include/linux/pm_qos.h
> ===================================================================
> --- linux.orig/include/linux/pm_qos.h
> +++ linux/include/linux/pm_qos.h
> @@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
>  int pm_qos_request_active(struct pm_qos_request *req);
>  s32 pm_qos_read_value(struct pm_qos_constraints *c);
>
> +s32 dev_pm_qos_read_value(struct device *dev);
>  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>                           s32 value);
>  int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
> @@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
>  static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
>                        { return 0; }
>
> +static inline s32 dev_pm_qos_read_value(struct device *dev)
> +                       { return 0; }
>  static inline int dev_pm_qos_add_request(struct device *dev,
>                                         struct dev_pm_qos_request *req,
>                                         s32 value)
> Index: linux/drivers/base/power/main.c
> ===================================================================
> --- linux.orig/drivers/base/power/main.c
> +++ linux/drivers/base/power/main.c
> @@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
>                dev_warn(dev, "parent %s should not be sleeping\n",
>                        dev_name(dev->parent));
>        list_add_tail(&dev->power.entry, &dpm_list);
> -       mutex_unlock(&dpm_list_mtx);
>        dev_pm_qos_constraints_init(dev);
> +       mutex_unlock(&dpm_list_mtx);
>  }
>
>  /**
> @@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
>  {
>        pr_debug("PM: Removing info for %s:%s\n",
>                 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
> -       dev_pm_qos_constraints_destroy(dev);
>        complete_all(&dev->power.completion);
>        mutex_lock(&dpm_list_mtx);
> +       dev_pm_qos_constraints_destroy(dev);
>        list_del_init(&dev->power.entry);
>        mutex_unlock(&dpm_list_mtx);
>        device_wakeup_disable(dev);
> Index: linux/include/linux/pm.h
> ===================================================================
> --- linux.orig/include/linux/pm.h
> +++ linux/include/linux/pm.h
> @@ -421,13 +421,6 @@ enum rpm_request {
>        RPM_REQ_RESUME,
>  };
>
> -/* Per-device PM QoS constraints data struct state */
> -enum dev_pm_qos_state {
> -       DEV_PM_QOS_NO_DEVICE,           /* No device present */
> -       DEV_PM_QOS_DEVICE_PRESENT,      /* Device present, data not allocated */
> -       DEV_PM_QOS_ALLOCATED,           /* Device present, data allocated */
> -};
> -
>  struct wakeup_source;
>
>  struct pm_domain_data {
> @@ -489,7 +482,6 @@ struct dev_pm_info {
>  #endif
>        struct pm_subsys_data   *subsys_data;  /* Owned by the subsystem. */
>        struct pm_qos_constraints *constraints;
> -       enum dev_pm_qos_state   constraints_state;
>  };
>
>  extern void update_pm_runtime_accounting(struct device *dev);
>

Thanks,
Jean

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-05  7:51               ` Jean Pihet
@ 2011-09-05 15:30                 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-05 15:30 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

On Monday, September 05, 2011, Jean Pihet wrote:
> Hi,
> 
> On Sat, Sep 3, 2011 at 10:02 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Saturday, September 03, 2011, Rafael J. Wysocki wrote:
> >> On Friday, September 02, 2011, Jean Pihet wrote:
> >> > On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > ...
> >> > >> >
> >> > >> > -       mutex_lock(&dev_pm_qos_mtx);
> >> > >> >        req->dev = dev;
> >> > >> >
> >> > >> > -       /* Return if the device has been removed */
> >> > >> > -       if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
> >> > >> > -               ret = -ENODEV;
> >> > >> > -               goto out;
> >> > >> > -       }
> >> > >> > +       device_pm_lock();
> >> > >> > +       mutex_lock(&dev_pm_qos_mtx);
> >> > >> >
> >> > >> > -       /*
> >> > >> > -        * Allocate the constraints data on the first call to add_request,
> >> > >> > -        * i.e. only if the data is not already allocated and if the device has
> >> > >> > -        * not been removed
> >> > >> > -        */
> >> > >> > -       if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
> >> > >> > -               ret = dev_pm_qos_constraints_allocate(dev);
> >> > >> > +       if (dev->power.constraints) {
> >> > >> > +               device_pm_unlock();
> >> > >> > +       } else {
> >> > >> > +               if (list_empty(&dev->power.entry)) {
> >> > >> > +                       /* The device has been removed from the system. */
> >> > >> > +                       device_pm_unlock();
> >> > >> > +                       goto out;
> >> > >> 0 is silently returned in case the device has been removed. Is that
> >> > >> the intention?
> >> > >
> >> > > Pretty much it is.  Is that a problem?
> >> > I think the caller needs to know if the constraint has been applied
> >> > correctly or not.
> >>
> >> However, the removal of the device is a special case.  What would the caller
> >> be supposed to do when it learned that the device had been removed while it had
> >> been trying to add a QoS constraing for it?  Not much I guess.
> >
> > Anyway, since returning an error code in that case won't hurt either,
> > below is a v2 of the patch doing that and resetting the dev field in the
> > req structure.
> Ok thanks for the update. The comments are inlined below.
> 

...
> >
> > -       if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
> > +       if (req->dev->power.constraints) {
> >                if (new_value != req->node.prio)
> >                        ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
> >                                               new_value);
> >        } else {
> >                /* Return if the device has been removed */
> > -               ret = -ENODEV;
> > +               ret = -EINVAL;
> The retcode should be -ENODEV.

Well, both -EINVAL and -ENODEV seem somewhat suitable here and I don't
have a strong preference, so I very well may leave the latter as it.

> Is the kerneldoc still OK?

It will be if the error code isn't changed.

Updated patch follows.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / QoS: Add function dev_pm_qos_read_value() (v2)

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  168 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 103 insertions(+), 80 deletions(-)

Index: linux/drivers/base/power/qos.c
=================================--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,7 +286,7 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
@@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
=================================--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
=================================--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
@ 2011-09-05 15:30                 ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-05 15:30 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm, Kevin Hilman

On Monday, September 05, 2011, Jean Pihet wrote:
> Hi,
> 
> On Sat, Sep 3, 2011 at 10:02 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Saturday, September 03, 2011, Rafael J. Wysocki wrote:
> >> On Friday, September 02, 2011, Jean Pihet wrote:
> >> > On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > ...
> >> > >> >
> >> > >> > -       mutex_lock(&dev_pm_qos_mtx);
> >> > >> >        req->dev = dev;
> >> > >> >
> >> > >> > -       /* Return if the device has been removed */
> >> > >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> >> > >> > -               ret = -ENODEV;
> >> > >> > -               goto out;
> >> > >> > -       }
> >> > >> > +       device_pm_lock();
> >> > >> > +       mutex_lock(&dev_pm_qos_mtx);
> >> > >> >
> >> > >> > -       /*
> >> > >> > -        * Allocate the constraints data on the first call to add_request,
> >> > >> > -        * i.e. only if the data is not already allocated and if the device has
> >> > >> > -        * not been removed
> >> > >> > -        */
> >> > >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> >> > >> > -               ret = dev_pm_qos_constraints_allocate(dev);
> >> > >> > +       if (dev->power.constraints) {
> >> > >> > +               device_pm_unlock();
> >> > >> > +       } else {
> >> > >> > +               if (list_empty(&dev->power.entry)) {
> >> > >> > +                       /* The device has been removed from the system. */
> >> > >> > +                       device_pm_unlock();
> >> > >> > +                       goto out;
> >> > >> 0 is silently returned in case the device has been removed. Is that
> >> > >> the intention?
> >> > >
> >> > > Pretty much it is.  Is that a problem?
> >> > I think the caller needs to know if the constraint has been applied
> >> > correctly or not.
> >>
> >> However, the removal of the device is a special case.  What would the caller
> >> be supposed to do when it learned that the device had been removed while it had
> >> been trying to add a QoS constraing for it?  Not much I guess.
> >
> > Anyway, since returning an error code in that case won't hurt either,
> > below is a v2 of the patch doing that and resetting the dev field in the
> > req structure.
> Ok thanks for the update. The comments are inlined below.
> 

...
> >
> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
> > +       if (req->dev->power.constraints) {
> >                if (new_value != req->node.prio)
> >                        ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
> >                                               new_value);
> >        } else {
> >                /* Return if the device has been removed */
> > -               ret = -ENODEV;
> > +               ret = -EINVAL;
> The retcode should be -ENODEV.

Well, both -EINVAL and -ENODEV seem somewhat suitable here and I don't
have a strong preference, so I very well may leave the latter as it.

> Is the kerneldoc still OK?

It will be if the error code isn't changed.

Updated patch follows.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / QoS: Add function dev_pm_qos_read_value() (v2)

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  168 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 103 insertions(+), 80 deletions(-)

Index: linux/drivers/base/power/qos.c
===================================================================
--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,7 +286,7 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
@@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
===================================================================
--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
===================================================================
--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);

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

* Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
  2011-09-05  7:51               ` Jean Pihet
  (?)
@ 2011-09-05 15:30               ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-05 15:30 UTC (permalink / raw)
  To: Jean Pihet; +Cc: Linux PM mailing list, LKML, Linux-sh list

On Monday, September 05, 2011, Jean Pihet wrote:
> Hi,
> 
> On Sat, Sep 3, 2011 at 10:02 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Saturday, September 03, 2011, Rafael J. Wysocki wrote:
> >> On Friday, September 02, 2011, Jean Pihet wrote:
> >> > On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > ...
> >> > >> >
> >> > >> > -       mutex_lock(&dev_pm_qos_mtx);
> >> > >> >        req->dev = dev;
> >> > >> >
> >> > >> > -       /* Return if the device has been removed */
> >> > >> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
> >> > >> > -               ret = -ENODEV;
> >> > >> > -               goto out;
> >> > >> > -       }
> >> > >> > +       device_pm_lock();
> >> > >> > +       mutex_lock(&dev_pm_qos_mtx);
> >> > >> >
> >> > >> > -       /*
> >> > >> > -        * Allocate the constraints data on the first call to add_request,
> >> > >> > -        * i.e. only if the data is not already allocated and if the device has
> >> > >> > -        * not been removed
> >> > >> > -        */
> >> > >> > -       if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
> >> > >> > -               ret = dev_pm_qos_constraints_allocate(dev);
> >> > >> > +       if (dev->power.constraints) {
> >> > >> > +               device_pm_unlock();
> >> > >> > +       } else {
> >> > >> > +               if (list_empty(&dev->power.entry)) {
> >> > >> > +                       /* The device has been removed from the system. */
> >> > >> > +                       device_pm_unlock();
> >> > >> > +                       goto out;
> >> > >> 0 is silently returned in case the device has been removed. Is that
> >> > >> the intention?
> >> > >
> >> > > Pretty much it is.  Is that a problem?
> >> > I think the caller needs to know if the constraint has been applied
> >> > correctly or not.
> >>
> >> However, the removal of the device is a special case.  What would the caller
> >> be supposed to do when it learned that the device had been removed while it had
> >> been trying to add a QoS constraing for it?  Not much I guess.
> >
> > Anyway, since returning an error code in that case won't hurt either,
> > below is a v2 of the patch doing that and resetting the dev field in the
> > req structure.
> Ok thanks for the update. The comments are inlined below.
> 

...
> >
> > -       if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
> > +       if (req->dev->power.constraints) {
> >                if (new_value != req->node.prio)
> >                        ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
> >                                               new_value);
> >        } else {
> >                /* Return if the device has been removed */
> > -               ret = -ENODEV;
> > +               ret = -EINVAL;
> The retcode should be -ENODEV.

Well, both -EINVAL and -ENODEV seem somewhat suitable here and I don't
have a strong preference, so I very well may leave the latter as it.

> Is the kerneldoc still OK?

It will be if the error code isn't changed.

Updated patch follows.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / QoS: Add function dev_pm_qos_read_value() (v2)

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  168 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 103 insertions(+), 80 deletions(-)

Index: linux/drivers/base/power/qos.c
===================================================================
--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,7 +286,7 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
@@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
===================================================================
--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
===================================================================
--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for
  2011-08-30 22:20   ` Rafael J. Wysocki
@ 2011-09-12  8:26     ` Ming Lei
  -1 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-12  8:26 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

Hi,

On Wed, Aug 31, 2011 at 6:20 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> domain callbacks under power.lock if power.irq_safe is set for the
> given device.  This is inconsistent with that rpm_idle() does after
> commit 02b2677 (PM / Runtime: Allow _put_sync() from
> interrupts-disabled context) and is problematic for subsystems and PM
> domains wanting to use power.lock for synchronization in their
> runtime PM callbacks.  For this reason, make runtime PM core functions
> always release power.lock before invoking subsystem or PM domain

If power.lock is released, the transition states(resuming or suspending)
may be observed in rpm_suspend or rpm_resume, then tasks schedule
will be produced in these two functions, so the functions below can't be

      pm_runtime_suspend()
      pm_runtime_autosuspend()
      pm_runtime_resume()
      pm_runtime_put_sync_suspend()
      pm_runtime_put_sync_autosuspend()

called in irq-off contexts safely even though irq_safe flag is set.

> callbacks.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/base/power/runtime.c |   50 ++++++++++++++++++++++++-------------------
>  1 file changed, 28 insertions(+), 22 deletions(-)
>
> Index: linux/drivers/base/power/runtime.c
> =================================> --- linux.orig/drivers/base/power/runtime.c
> +++ linux/drivers/base/power/runtime.c
> @@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
>  }
>
>  /**
> + * __rpm_callback - Run a given runtime PM callback for a given device.
> + * @cb: Runtime PM callback to run.
> + * @dev: Device to run the callback for.
> + */
> +static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
> +       __releases(&dev->power.lock) __acquires(&dev->power.lock)
> +{
> +       int retval;
> +
> +       if (dev->power.irq_safe)
> +               spin_unlock(&dev->power.lock);
> +       else
> +               spin_unlock_irq(&dev->power.lock);
> +
> +       retval = cb(dev);
> +
> +       if (dev->power.irq_safe)
> +               spin_lock(&dev->power.lock);
> +       else
> +               spin_lock_irq(&dev->power.lock);
> +
> +       return retval;
> +}
> +
> +/**
>  * rpm_idle - Notify device bus type if the device can be suspended.
>  * @dev: Device to notify the bus type about.
>  * @rpmflags: Flag bits.
> @@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
>        else
>                callback = NULL;
>
> -       if (callback) {
> -               if (dev->power.irq_safe)
> -                       spin_unlock(&dev->power.lock);
> -               else
> -                       spin_unlock_irq(&dev->power.lock);
> -
> -               callback(dev);
> -
> -               if (dev->power.irq_safe)
> -                       spin_lock(&dev->power.lock);
> -               else
> -                       spin_lock_irq(&dev->power.lock);
> -       }
> +       if (callback)
> +               __rpm_callback(callback, dev);
>
>        dev->power.idle_notification = false;
>        wake_up_all(&dev->power.wait_queue);
> @@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
>  * @dev: Device to run the callback for.
>  */
>  static int rpm_callback(int (*cb)(struct device *), struct device *dev)
> -       __releases(&dev->power.lock) __acquires(&dev->power.lock)
>  {
>        int retval;
>
>        if (!cb)
>                return -ENOSYS;
>
> -       if (dev->power.irq_safe) {
> -               retval = cb(dev);
> -       } else {
> -               spin_unlock_irq(&dev->power.lock);
> -
> -               retval = cb(dev);
> +       retval = __rpm_callback(cb, dev);
>
> -               spin_lock_irq(&dev->power.lock);
> -       }
>        dev->power.runtime_error = retval;
>        return retval != -EACCES ? retval : -EIO;
>  }
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>


thanks,
-- 
Ming Lei

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
@ 2011-09-12  8:26     ` Ming Lei
  0 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-12  8:26 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

Hi,

On Wed, Aug 31, 2011 at 6:20 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> domain callbacks under power.lock if power.irq_safe is set for the
> given device.  This is inconsistent with that rpm_idle() does after
> commit 02b2677 (PM / Runtime: Allow _put_sync() from
> interrupts-disabled context) and is problematic for subsystems and PM
> domains wanting to use power.lock for synchronization in their
> runtime PM callbacks.  For this reason, make runtime PM core functions
> always release power.lock before invoking subsystem or PM domain

If power.lock is released, the transition states(resuming or suspending)
may be observed in rpm_suspend or rpm_resume, then tasks schedule
will be produced in these two functions, so the functions below can't be

      pm_runtime_suspend()
      pm_runtime_autosuspend()
      pm_runtime_resume()
      pm_runtime_put_sync_suspend()
      pm_runtime_put_sync_autosuspend()

called in irq-off contexts safely even though irq_safe flag is set.

> callbacks.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/base/power/runtime.c |   50 ++++++++++++++++++++++++-------------------
>  1 file changed, 28 insertions(+), 22 deletions(-)
>
> Index: linux/drivers/base/power/runtime.c
> ===================================================================
> --- linux.orig/drivers/base/power/runtime.c
> +++ linux/drivers/base/power/runtime.c
> @@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
>  }
>
>  /**
> + * __rpm_callback - Run a given runtime PM callback for a given device.
> + * @cb: Runtime PM callback to run.
> + * @dev: Device to run the callback for.
> + */
> +static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
> +       __releases(&dev->power.lock) __acquires(&dev->power.lock)
> +{
> +       int retval;
> +
> +       if (dev->power.irq_safe)
> +               spin_unlock(&dev->power.lock);
> +       else
> +               spin_unlock_irq(&dev->power.lock);
> +
> +       retval = cb(dev);
> +
> +       if (dev->power.irq_safe)
> +               spin_lock(&dev->power.lock);
> +       else
> +               spin_lock_irq(&dev->power.lock);
> +
> +       return retval;
> +}
> +
> +/**
>  * rpm_idle - Notify device bus type if the device can be suspended.
>  * @dev: Device to notify the bus type about.
>  * @rpmflags: Flag bits.
> @@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
>        else
>                callback = NULL;
>
> -       if (callback) {
> -               if (dev->power.irq_safe)
> -                       spin_unlock(&dev->power.lock);
> -               else
> -                       spin_unlock_irq(&dev->power.lock);
> -
> -               callback(dev);
> -
> -               if (dev->power.irq_safe)
> -                       spin_lock(&dev->power.lock);
> -               else
> -                       spin_lock_irq(&dev->power.lock);
> -       }
> +       if (callback)
> +               __rpm_callback(callback, dev);
>
>        dev->power.idle_notification = false;
>        wake_up_all(&dev->power.wait_queue);
> @@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
>  * @dev: Device to run the callback for.
>  */
>  static int rpm_callback(int (*cb)(struct device *), struct device *dev)
> -       __releases(&dev->power.lock) __acquires(&dev->power.lock)
>  {
>        int retval;
>
>        if (!cb)
>                return -ENOSYS;
>
> -       if (dev->power.irq_safe) {
> -               retval = cb(dev);
> -       } else {
> -               spin_unlock_irq(&dev->power.lock);
> -
> -               retval = cb(dev);
> +       retval = __rpm_callback(cb, dev);
>
> -               spin_lock_irq(&dev->power.lock);
> -       }
>        dev->power.runtime_error = retval;
>        return retval != -EACCES ? retval : -EIO;
>  }
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>


thanks,
-- 
Ming Lei

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
  2011-09-12  8:26     ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set Ming Lei
@ 2011-09-12 21:52       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-12 21:52 UTC (permalink / raw)
  To: Ming Lei
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Monday, September 12, 2011, Ming Lei wrote:
> Hi,
> 
> On Wed, Aug 31, 2011 at 6:20 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> > domain callbacks under power.lock if power.irq_safe is set for the
> > given device.  This is inconsistent with that rpm_idle() does after
> > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> > interrupts-disabled context) and is problematic for subsystems and PM
> > domains wanting to use power.lock for synchronization in their
> > runtime PM callbacks.  For this reason, make runtime PM core functions
> > always release power.lock before invoking subsystem or PM domain
> 
> If power.lock is released, the transition states(resuming or suspending)
> may be observed in rpm_suspend or rpm_resume, then tasks schedule
> will be produced in these two functions,

I don't think so, because the interrupts are still off.

> so the functions below can't be
> 
>       pm_runtime_suspend()
>       pm_runtime_autosuspend()
>       pm_runtime_resume()
>       pm_runtime_put_sync_suspend()
>       pm_runtime_put_sync_autosuspend()
> 
> called in irq-off contexts safely even though irq_safe flag is set.

The patch doesn't cause rpm_suspend() and rpm_resume() to turn interrupts
on if irq_safe is set, it only causes them to release the lock (temporarily).

Thanks,
Rafael

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
@ 2011-09-12 21:52       ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-12 21:52 UTC (permalink / raw)
  To: Ming Lei
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Monday, September 12, 2011, Ming Lei wrote:
> Hi,
> 
> On Wed, Aug 31, 2011 at 6:20 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> > domain callbacks under power.lock if power.irq_safe is set for the
> > given device.  This is inconsistent with that rpm_idle() does after
> > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> > interrupts-disabled context) and is problematic for subsystems and PM
> > domains wanting to use power.lock for synchronization in their
> > runtime PM callbacks.  For this reason, make runtime PM core functions
> > always release power.lock before invoking subsystem or PM domain
> 
> If power.lock is released, the transition states(resuming or suspending)
> may be observed in rpm_suspend or rpm_resume, then tasks schedule
> will be produced in these two functions,

I don't think so, because the interrupts are still off.

> so the functions below can't be
> 
>       pm_runtime_suspend()
>       pm_runtime_autosuspend()
>       pm_runtime_resume()
>       pm_runtime_put_sync_suspend()
>       pm_runtime_put_sync_autosuspend()
> 
> called in irq-off contexts safely even though irq_safe flag is set.

The patch doesn't cause rpm_suspend() and rpm_resume() to turn interrupts
on if irq_safe is set, it only causes them to release the lock (temporarily).

Thanks,
Rafael

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for
       [not found]     ` <201109122344.02386.rjw@sisk.pl>
@ 2011-09-13  1:22         ` Ming Lei
  0 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-13  1:22 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

Hi,

On Tue, Sep 13, 2011 at 5:44 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Monday, September 12, 2011, Ming Lei wrote:
>
>> Hi,
>
>>
>
>> On Wed, Aug 31, 2011 at 6:20 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>
>> >
>
>> > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
>
>> > domain callbacks under power.lock if power.irq_safe is set for the
>
>> > given device.  This is inconsistent with that rpm_idle() does after
>
>> > commit 02b2677 (PM / Runtime: Allow _put_sync() from
>
>> > interrupts-disabled context) and is problematic for subsystems and PM
>
>> > domains wanting to use power.lock for synchronization in their
>
>> > runtime PM callbacks.  For this reason, make runtime PM core functions
>
>> > always release power.lock before invoking subsystem or PM domain
>
>>
>
>> If power.lock is released, the transition states(resuming or suspending)
>
>> may be observed in rpm_suspend or rpm_resume, then tasks schedule
>
>> will be produced in these two functions,
>
> I don't think so, because the interrupts are still off.

Yes, the interrupts are still off on local CPU, but the release of spin lock may
cause another CPUs to run into rpm_suspend or rpm_resume and produce
task schedule inside the two functions.

>
>> so the functions below can't be
>
>>
>
>> pm_runtime_suspend()
>
>> pm_runtime_autosuspend()
>
>> pm_runtime_resume()
>
>> pm_runtime_put_sync_suspend()
>
>> pm_runtime_put_sync_autosuspend()
>
>>
>
>> called in irq-off contexts safely even though irq_safe flag is set.
>
> The patch doesn't cause rpm_suspend() and rpm_resume() to turn interrupts
>
> on if irq_safe is set, it only causes them to release the lock
> (temporarily).


thanks,
-- 
Ming Lei

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
@ 2011-09-13  1:22         ` Ming Lei
  0 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-13  1:22 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

Hi,

On Tue, Sep 13, 2011 at 5:44 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Monday, September 12, 2011, Ming Lei wrote:
>
>> Hi,
>
>>
>
>> On Wed, Aug 31, 2011 at 6:20 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>
>> >
>
>> > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
>
>> > domain callbacks under power.lock if power.irq_safe is set for the
>
>> > given device.  This is inconsistent with that rpm_idle() does after
>
>> > commit 02b2677 (PM / Runtime: Allow _put_sync() from
>
>> > interrupts-disabled context) and is problematic for subsystems and PM
>
>> > domains wanting to use power.lock for synchronization in their
>
>> > runtime PM callbacks.  For this reason, make runtime PM core functions
>
>> > always release power.lock before invoking subsystem or PM domain
>
>>
>
>> If power.lock is released, the transition states(resuming or suspending)
>
>> may be observed in rpm_suspend or rpm_resume, then tasks schedule
>
>> will be produced in these two functions,
>
> I don't think so, because the interrupts are still off.

Yes, the interrupts are still off on local CPU, but the release of spin lock may
cause another CPUs to run into rpm_suspend or rpm_resume and produce
task schedule inside the two functions.

>
>> so the functions below can't be
>
>>
>
>> pm_runtime_suspend()
>
>> pm_runtime_autosuspend()
>
>> pm_runtime_resume()
>
>> pm_runtime_put_sync_suspend()
>
>> pm_runtime_put_sync_autosuspend()
>
>>
>
>> called in irq-off contexts safely even though irq_safe flag is set.
>
> The patch doesn't cause rpm_suspend() and rpm_resume() to turn interrupts
>
> on if irq_safe is set, it only causes them to release the lock
> (temporarily).


thanks,
-- 
Ming Lei

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
  2011-09-13  1:22         ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set Ming Lei
@ 2011-09-13 16:06           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-13 16:06 UTC (permalink / raw)
  To: Ming Lei
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Tuesday, September 13, 2011, Ming Lei wrote:
> Hi,
> 
> On Tue, Sep 13, 2011 at 5:44 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Monday, September 12, 2011, Ming Lei wrote:
> >
> >> Hi,
> >
> >>
> >
> >> On Wed, Aug 31, 2011 at 6:20 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> >> >
> >
> >> > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> >
> >> > domain callbacks under power.lock if power.irq_safe is set for the
> >
> >> > given device.  This is inconsistent with that rpm_idle() does after
> >
> >> > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> >
> >> > interrupts-disabled context) and is problematic for subsystems and PM
> >
> >> > domains wanting to use power.lock for synchronization in their
> >
> >> > runtime PM callbacks.  For this reason, make runtime PM core functions
> >
> >> > always release power.lock before invoking subsystem or PM domain
> >
> >>
> >
> >> If power.lock is released, the transition states(resuming or suspending)
> >
> >> may be observed in rpm_suspend or rpm_resume, then tasks schedule
> >
> >> will be produced in these two functions,
> >
> > I don't think so, because the interrupts are still off.
> 
> Yes, the interrupts are still off on local CPU, but the release of spin lock may
> cause another CPUs to run into rpm_suspend or rpm_resume and produce
> task schedule inside the two functions.

Not for the same device, though.

Also, I'm not quite sure what scenario exactly are you referring to.
Could you please give an example?

Thanks,
Rafael

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
@ 2011-09-13 16:06           ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-13 16:06 UTC (permalink / raw)
  To: Ming Lei
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Tuesday, September 13, 2011, Ming Lei wrote:
> Hi,
> 
> On Tue, Sep 13, 2011 at 5:44 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Monday, September 12, 2011, Ming Lei wrote:
> >
> >> Hi,
> >
> >>
> >
> >> On Wed, Aug 31, 2011 at 6:20 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> >> >
> >
> >> > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> >
> >> > domain callbacks under power.lock if power.irq_safe is set for the
> >
> >> > given device.  This is inconsistent with that rpm_idle() does after
> >
> >> > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> >
> >> > interrupts-disabled context) and is problematic for subsystems and PM
> >
> >> > domains wanting to use power.lock for synchronization in their
> >
> >> > runtime PM callbacks.  For this reason, make runtime PM core functions
> >
> >> > always release power.lock before invoking subsystem or PM domain
> >
> >>
> >
> >> If power.lock is released, the transition states(resuming or suspending)
> >
> >> may be observed in rpm_suspend or rpm_resume, then tasks schedule
> >
> >> will be produced in these two functions,
> >
> > I don't think so, because the interrupts are still off.
> 
> Yes, the interrupts are still off on local CPU, but the release of spin lock may
> cause another CPUs to run into rpm_suspend or rpm_resume and produce
> task schedule inside the two functions.

Not for the same device, though.

Also, I'm not quite sure what scenario exactly are you referring to.
Could you please give an example?

Thanks,
Rafael

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for
  2011-09-13 16:06           ` Rafael J. Wysocki
@ 2011-09-14  1:12             ` Ming Lei
  -1 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-14  1:12 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

Hi,

On Wed, Sep 14, 2011 at 12:06 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:

>> >> If power.lock is released, the transition states(resuming or suspending)
>> >
>> >> may be observed in rpm_suspend or rpm_resume, then tasks schedule
>> >
>> >> will be produced in these two functions,
>> >
>> > I don't think so, because the interrupts are still off.
>>
>> Yes, the interrupts are still off on local CPU, but the release of spin lock may
>> cause another CPUs to run into rpm_suspend or rpm_resume and produce
>> task schedule inside the two functions.
>
> Not for the same device, though.

I think it is probable to happen on the same device in theory, see below:

- suppose irq_safe is set before calling two pm_runtime_suspend below
- suppose this patch has been applied

CPU0						CPU1
pm_runtime_suspend
	acquired power lock
	rpm_suspend
						pm_runtime_suspend
							spining power lock
	...
	release power lock
							acquired power lock
	run .runtime_suspend
							found the dev suspending
							wait for power state and schedule

>
> Also, I'm not quite sure what scenario exactly are you referring to.
> Could you please give an example?

I have no actual examples about it, just a theory analysis.

Also, maybe the patch below can avoid the race above.

diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index a6750fc..87c9a37 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -150,6 +150,9 @@ static int rpm_check_suspend_allowed(struct device *dev)
 		retval = -EAGAIN;
 	else if (dev->power.runtime_status = RPM_SUSPENDED)
 		retval = 1;
+	else if (dev->power.runtime_status != RPM_ACTIVE &&
+			dev->power.irq_safe)
+		retval = -EAGAIN;

 	return retval;
 }


thanks,
-- 
Ming Lei

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
@ 2011-09-14  1:12             ` Ming Lei
  0 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-14  1:12 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

Hi,

On Wed, Sep 14, 2011 at 12:06 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:

>> >> If power.lock is released, the transition states(resuming or suspending)
>> >
>> >> may be observed in rpm_suspend or rpm_resume, then tasks schedule
>> >
>> >> will be produced in these two functions,
>> >
>> > I don't think so, because the interrupts are still off.
>>
>> Yes, the interrupts are still off on local CPU, but the release of spin lock may
>> cause another CPUs to run into rpm_suspend or rpm_resume and produce
>> task schedule inside the two functions.
>
> Not for the same device, though.

I think it is probable to happen on the same device in theory, see below:

- suppose irq_safe is set before calling two pm_runtime_suspend below
- suppose this patch has been applied

CPU0						CPU1
pm_runtime_suspend
	acquired power lock
	rpm_suspend
						pm_runtime_suspend
							spining power lock
	...
	release power lock
							acquired power lock
	run .runtime_suspend
							found the dev suspending
							wait for power state and schedule

>
> Also, I'm not quite sure what scenario exactly are you referring to.
> Could you please give an example?

I have no actual examples about it, just a theory analysis.

Also, maybe the patch below can avoid the race above.

diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index a6750fc..87c9a37 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -150,6 +150,9 @@ static int rpm_check_suspend_allowed(struct device *dev)
 		retval = -EAGAIN;
 	else if (dev->power.runtime_status == RPM_SUSPENDED)
 		retval = 1;
+	else if (dev->power.runtime_status != RPM_ACTIVE &&
+			dev->power.irq_safe)
+		retval = -EAGAIN;

 	return retval;
 }


thanks,
-- 
Ming Lei

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
  2011-09-14  1:12             ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set Ming Lei
@ 2011-09-14 20:45               ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-14 20:45 UTC (permalink / raw)
  To: Ming Lei
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Wednesday, September 14, 2011, Ming Lei wrote:
> Hi,
> 
> On Wed, Sep 14, 2011 at 12:06 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> 
> >> >> If power.lock is released, the transition states(resuming or suspending)
> >> >
> >> >> may be observed in rpm_suspend or rpm_resume, then tasks schedule
> >> >
> >> >> will be produced in these two functions,
> >> >
> >> > I don't think so, because the interrupts are still off.
> >>
> >> Yes, the interrupts are still off on local CPU, but the release of spin lock may
> >> cause another CPUs to run into rpm_suspend or rpm_resume and produce
> >> task schedule inside the two functions.
> >
> > Not for the same device, though.
> 
> I think it is probable to happen on the same device in theory, see below:
> 
> - suppose irq_safe is set before calling two pm_runtime_suspend below
> - suppose this patch has been applied
> 
> CPU0						CPU1
> pm_runtime_suspend
> 	acquired power lock
> 	rpm_suspend
> 						pm_runtime_suspend
> 							spining power lock
> 	...
> 	release power lock
> 							acquired power lock
> 	run .runtime_suspend
> 							found the dev suspending
> 							wait for power state and schedule

OK, I see what the problem is.  The second CPU can see the status
being RPM_SUSPENDING in the irq_safe case, which isn't possible
without the patch.

Good catch!

I think in that case rpm_suspend() should just release the lock,
run cpu_relax(), reacquire the lock and go to the "repeat" label.

Thanks,
Rafael

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
@ 2011-09-14 20:45               ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-14 20:45 UTC (permalink / raw)
  To: Ming Lei
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Wednesday, September 14, 2011, Ming Lei wrote:
> Hi,
> 
> On Wed, Sep 14, 2011 at 12:06 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> 
> >> >> If power.lock is released, the transition states(resuming or suspending)
> >> >
> >> >> may be observed in rpm_suspend or rpm_resume, then tasks schedule
> >> >
> >> >> will be produced in these two functions,
> >> >
> >> > I don't think so, because the interrupts are still off.
> >>
> >> Yes, the interrupts are still off on local CPU, but the release of spin lock may
> >> cause another CPUs to run into rpm_suspend or rpm_resume and produce
> >> task schedule inside the two functions.
> >
> > Not for the same device, though.
> 
> I think it is probable to happen on the same device in theory, see below:
> 
> - suppose irq_safe is set before calling two pm_runtime_suspend below
> - suppose this patch has been applied
> 
> CPU0						CPU1
> pm_runtime_suspend
> 	acquired power lock
> 	rpm_suspend
> 						pm_runtime_suspend
> 							spining power lock
> 	...
> 	release power lock
> 							acquired power lock
> 	run .runtime_suspend
> 							found the dev suspending
> 							wait for power state and schedule

OK, I see what the problem is.  The second CPU can see the status
being RPM_SUSPENDING in the irq_safe case, which isn't possible
without the patch.

Good catch!

I think in that case rpm_suspend() should just release the lock,
run cpu_relax(), reacquire the lock and go to the "repeat" label.

Thanks,
Rafael

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for
  2011-09-14 20:45               ` Rafael J. Wysocki
@ 2011-09-15 10:55                 ` Ming Lei
  -1 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-15 10:55 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Thu, Sep 15, 2011 at 4:45 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Wednesday, September 14, 2011, Ming Lei wrote:
>> Hi,
>>
>> On Wed, Sep 14, 2011 at 12:06 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>>
>> >> >> If power.lock is released, the transition states(resuming or suspending)
>> >> >
>> >> >> may be observed in rpm_suspend or rpm_resume, then tasks schedule
>> >> >
>> >> >> will be produced in these two functions,
>> >> >
>> >> > I don't think so, because the interrupts are still off.
>> >>
>> >> Yes, the interrupts are still off on local CPU, but the release of spin lock may
>> >> cause another CPUs to run into rpm_suspend or rpm_resume and produce
>> >> task schedule inside the two functions.
>> >
>> > Not for the same device, though.
>>
>> I think it is probable to happen on the same device in theory, see below:
>>
>> - suppose irq_safe is set before calling two pm_runtime_suspend below
>> - suppose this patch has been applied
>>
>> CPU0                                          CPU1
>> pm_runtime_suspend
>>       acquired power lock
>>       rpm_suspend
>>                                               pm_runtime_suspend
>>                                                       spining power lock
>>       ...
>>       release power lock
>>                                                       acquired power lock
>>       run .runtime_suspend
>>                                                       found the dev suspending
>>                                                       wait for power state and schedule
>
> OK, I see what the problem is.  The second CPU can see the status
> being RPM_SUSPENDING in the irq_safe case, which isn't possible
> without the patch.
>
> Good catch!
>
> I think in that case rpm_suspend() should just release the lock,
> run cpu_relax(), reacquire the lock and go to the "repeat" label.

Yes, it makes sense in this case.

BTW: looks like something is wrong about PM mail list, I often got
the messages below these days:

Delivery to the following recipient has been delayed:

    linux-pm@lists.linux-foundation.org

Message will be retried for 1 more day(s)


thanks,
-- 
Ming Lei

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

* Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set
@ 2011-09-15 10:55                 ` Ming Lei
  0 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-15 10:55 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Thu, Sep 15, 2011 at 4:45 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Wednesday, September 14, 2011, Ming Lei wrote:
>> Hi,
>>
>> On Wed, Sep 14, 2011 at 12:06 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>>
>> >> >> If power.lock is released, the transition states(resuming or suspending)
>> >> >
>> >> >> may be observed in rpm_suspend or rpm_resume, then tasks schedule
>> >> >
>> >> >> will be produced in these two functions,
>> >> >
>> >> > I don't think so, because the interrupts are still off.
>> >>
>> >> Yes, the interrupts are still off on local CPU, but the release of spin lock may
>> >> cause another CPUs to run into rpm_suspend or rpm_resume and produce
>> >> task schedule inside the two functions.
>> >
>> > Not for the same device, though.
>>
>> I think it is probable to happen on the same device in theory, see below:
>>
>> - suppose irq_safe is set before calling two pm_runtime_suspend below
>> - suppose this patch has been applied
>>
>> CPU0                                          CPU1
>> pm_runtime_suspend
>>       acquired power lock
>>       rpm_suspend
>>                                               pm_runtime_suspend
>>                                                       spining power lock
>>       ...
>>       release power lock
>>                                                       acquired power lock
>>       run .runtime_suspend
>>                                                       found the dev suspending
>>                                                       wait for power state and schedule
>
> OK, I see what the problem is.  The second CPU can see the status
> being RPM_SUSPENDING in the irq_safe case, which isn't possible
> without the patch.
>
> Good catch!
>
> I think in that case rpm_suspend() should just release the lock,
> run cpu_relax(), reacquire the lock and go to the "repeat" label.

Yes, it makes sense in this case.

BTW: looks like something is wrong about PM mail list, I often got
the messages below these days:

Delivery to the following recipient has been delayed:

    linux-pm@lists.linux-foundation.org

Message will be retried for 1 more day(s)


thanks,
-- 
Ming Lei

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

* [PATCH 0/3] PM: Runtime PM and device PM QoS refinements
  2011-08-30 22:17 ` Rafael J. Wysocki
@ 2011-09-24 21:23   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-24 21:23 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet, Ming Lei

Hi,

On Wednesday, August 31, 2011, Rafael J. Wysocki wrote:
> Hi,
> 
> This patchset illustrates how device PM QoS may be used along with
> PM domains in my view.
> 
> Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
> suitable for 3.2, unless somebody hates them, but patches [4-5/5] are
> total RFC.  They haven't been tested, only compiled, so the use of them
> is not encouraged (they may kill your dog or make your cat go wild, or
> do something equally nasty, so beware).  Their purpose is to illustrate
> an idea that I'd like to discuss at the PM miniconference during the
> LPC.
> 
> [1/5] - Split device PM domain data into a "base" and additional fields
>         (one need_restore field at the moment, but the subsequent patches
>         add more fields).
> 
> [2/5] - Make runtime PM always release power.lock if power.irq_safe is set.
> 
> [3/5] - Add function for reading device PM QoS values safely.
> 
> [4/5] - Add governor function for stopping devices.
> 
> [5/5] - Add generic PM domain power off governor function.

I have updated patches [1-3] after receiving some feedback and the result
follows.  These patches are regarded as 3.2 material, if no one has
objections.

[1/3] - Split device PM domain data into a "base" and additional fields
        (one need_restore field at the moment).
 
[2/3] - Make runtime PM always release power.lock if power.irq_safe is set.
 
[3/3] - Add function for reading device PM QoS values safely.

Thanks,
Rafael


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

* [PATCH 0/3] PM: Runtime PM and device PM QoS refinements
@ 2011-09-24 21:23   ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-24 21:23 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet, Ming Lei

Hi,

On Wednesday, August 31, 2011, Rafael J. Wysocki wrote:
> Hi,
> 
> This patchset illustrates how device PM QoS may be used along with
> PM domains in my view.
> 
> Actually, it consists of two parts.  Namely, patches [1-3/5] seem to be
> suitable for 3.2, unless somebody hates them, but patches [4-5/5] are
> total RFC.  They haven't been tested, only compiled, so the use of them
> is not encouraged (they may kill your dog or make your cat go wild, or
> do something equally nasty, so beware).  Their purpose is to illustrate
> an idea that I'd like to discuss at the PM miniconference during the
> LPC.
> 
> [1/5] - Split device PM domain data into a "base" and additional fields
>         (one need_restore field at the moment, but the subsequent patches
>         add more fields).
> 
> [2/5] - Make runtime PM always release power.lock if power.irq_safe is set.
> 
> [3/5] - Add function for reading device PM QoS values safely.
> 
> [4/5] - Add governor function for stopping devices.
> 
> [5/5] - Add generic PM domain power off governor function.

I have updated patches [1-3] after receiving some feedback and the result
follows.  These patches are regarded as 3.2 material, if no one has
objections.

[1/3] - Split device PM domain data into a "base" and additional fields
        (one need_restore field at the moment).
 
[2/3] - Make runtime PM always release power.lock if power.irq_safe is set.
 
[3/3] - Add function for reading device PM QoS values safely.

Thanks,
Rafael


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

* [PATCH 1/3] PM / Domains: Split device PM domain data into base and need_restore
  2011-09-24 21:23   ` Rafael J. Wysocki
@ 2011-09-24 21:24     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-24 21:24 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet, Ming Lei

From: Rafael J. Wysocki <rjw@sisk.pl>

The struct pm_domain_data data type is defined in such a way that
adding new fields specific to the generic PM domains code will
require include/linux/pm.h to be modified.  As a result, data types
used only by the generic PM domains code will be defined in two
headers, although they all should be defined in pm_domain.h and
pm.h will need to include more headers, which won't be very nice.

For this reason change the definition of struct pm_subsys_data
so that its domain_data member is a pointer, which will allow
struct pm_domain_data to be subclassed by various PM domains
implementations.  Remove the need_restore member from
struct pm_domain_data and make the generic PM domains code
subclass it by adding the need_restore member to the new data type.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |   28 +++++++++++++++++++---------
 include/linux/pm.h          |    3 +--
 include/linux/pm_domain.h   |   10 ++++++++++
 3 files changed, 30 insertions(+), 11 deletions(-)

Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -433,7 +433,6 @@ struct wakeup_source;
 struct pm_domain_data {
 	struct list_head list_node;
 	struct device *dev;
-	bool need_restore;
 };
 
 struct pm_subsys_data {
@@ -443,7 +442,7 @@ struct pm_subsys_data {
 	struct list_head clock_list;
 #endif
 #ifdef CONFIG_PM_GENERIC_DOMAINS
-	struct pm_domain_data domain_data;
+	struct pm_domain_data *domain_data;
 #endif
 };
 
Index: linux/include/linux/pm_domain.h
=================================--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -62,6 +62,16 @@ struct gpd_link {
 	struct list_head slave_node;
 };
 
+struct generic_pm_domain_data {
+	struct pm_domain_data base;
+	bool need_restore;
+};
+
+static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data *pdd)
+{
+	return container_of(pdd, struct generic_pm_domain_data, base);
+}
+
 #ifdef CONFIG_PM_GENERIC_DOMAINS
 extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
 			       struct device *dev);
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -188,11 +188,12 @@ static int __pm_genpd_save_device(struct
 				  struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 	int ret = 0;
 
-	if (pdd->need_restore)
+	if (gpd_data->need_restore)
 		return 0;
 
 	mutex_unlock(&genpd->lock);
@@ -210,7 +211,7 @@ static int __pm_genpd_save_device(struct
 	mutex_lock(&genpd->lock);
 
 	if (!ret)
-		pdd->need_restore = true;
+		gpd_data->need_restore = true;
 
 	return ret;
 }
@@ -224,10 +225,11 @@ static void __pm_genpd_restore_device(st
 				      struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 
-	if (!pdd->need_restore)
+	if (!gpd_data->need_restore)
 		return;
 
 	mutex_unlock(&genpd->lock);
@@ -244,7 +246,7 @@ static void __pm_genpd_restore_device(st
 
 	mutex_lock(&genpd->lock);
 
-	pdd->need_restore = false;
+	gpd_data->need_restore = false;
 }
 
 /**
@@ -493,7 +495,7 @@ static int pm_genpd_runtime_resume(struc
 		mutex_lock(&genpd->lock);
 	}
 	finish_wait(&genpd->status_wait_queue, &wait);
-	__pm_genpd_restore_device(&dev->power.subsys_data->domain_data, genpd);
+	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
 	genpd->resume_count--;
 	genpd_set_active(genpd);
 	wake_up_all(&genpd->status_wait_queue);
@@ -1080,6 +1082,7 @@ static void pm_genpd_complete(struct dev
  */
 int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
 {
+	struct generic_pm_domain_data *gpd_data;
 	struct pm_domain_data *pdd;
 	int ret = 0;
 
@@ -1106,14 +1109,20 @@ int pm_genpd_add_device(struct generic_p
 			goto out;
 		}
 
+	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
+	if (!gpd_data) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
 	genpd->device_count++;
 
 	dev->pm_domain = &genpd->domain;
 	dev_pm_get_subsys_data(dev);
-	pdd = &dev->power.subsys_data->domain_data;
-	pdd->dev = dev;
-	pdd->need_restore = false;
-	list_add_tail(&pdd->list_node, &genpd->dev_list);
+	dev->power.subsys_data->domain_data = &gpd_data->base;
+	gpd_data->base.dev = dev;
+	gpd_data->need_restore = false;
+	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
 
  out:
 	genpd_release_lock(genpd);
@@ -1152,6 +1161,7 @@ int pm_genpd_remove_device(struct generi
 		pdd->dev = NULL;
 		dev_pm_put_subsys_data(dev);
 		dev->pm_domain = NULL;
+		kfree(to_gpd_data(pdd));
 
 		genpd->device_count--;
 


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

* [PATCH 1/3] PM / Domains: Split device PM domain data into base and need_restore
@ 2011-09-24 21:24     ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-24 21:24 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet, Ming Lei

From: Rafael J. Wysocki <rjw@sisk.pl>

The struct pm_domain_data data type is defined in such a way that
adding new fields specific to the generic PM domains code will
require include/linux/pm.h to be modified.  As a result, data types
used only by the generic PM domains code will be defined in two
headers, although they all should be defined in pm_domain.h and
pm.h will need to include more headers, which won't be very nice.

For this reason change the definition of struct pm_subsys_data
so that its domain_data member is a pointer, which will allow
struct pm_domain_data to be subclassed by various PM domains
implementations.  Remove the need_restore member from
struct pm_domain_data and make the generic PM domains code
subclass it by adding the need_restore member to the new data type.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |   28 +++++++++++++++++++---------
 include/linux/pm.h          |    3 +--
 include/linux/pm_domain.h   |   10 ++++++++++
 3 files changed, 30 insertions(+), 11 deletions(-)

Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -433,7 +433,6 @@ struct wakeup_source;
 struct pm_domain_data {
 	struct list_head list_node;
 	struct device *dev;
-	bool need_restore;
 };
 
 struct pm_subsys_data {
@@ -443,7 +442,7 @@ struct pm_subsys_data {
 	struct list_head clock_list;
 #endif
 #ifdef CONFIG_PM_GENERIC_DOMAINS
-	struct pm_domain_data domain_data;
+	struct pm_domain_data *domain_data;
 #endif
 };
 
Index: linux/include/linux/pm_domain.h
===================================================================
--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -62,6 +62,16 @@ struct gpd_link {
 	struct list_head slave_node;
 };
 
+struct generic_pm_domain_data {
+	struct pm_domain_data base;
+	bool need_restore;
+};
+
+static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data *pdd)
+{
+	return container_of(pdd, struct generic_pm_domain_data, base);
+}
+
 #ifdef CONFIG_PM_GENERIC_DOMAINS
 extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
 			       struct device *dev);
Index: linux/drivers/base/power/domain.c
===================================================================
--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -188,11 +188,12 @@ static int __pm_genpd_save_device(struct
 				  struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 	int ret = 0;
 
-	if (pdd->need_restore)
+	if (gpd_data->need_restore)
 		return 0;
 
 	mutex_unlock(&genpd->lock);
@@ -210,7 +211,7 @@ static int __pm_genpd_save_device(struct
 	mutex_lock(&genpd->lock);
 
 	if (!ret)
-		pdd->need_restore = true;
+		gpd_data->need_restore = true;
 
 	return ret;
 }
@@ -224,10 +225,11 @@ static void __pm_genpd_restore_device(st
 				      struct generic_pm_domain *genpd)
 	__releases(&genpd->lock) __acquires(&genpd->lock)
 {
+	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
 	struct device *dev = pdd->dev;
 	struct device_driver *drv = dev->driver;
 
-	if (!pdd->need_restore)
+	if (!gpd_data->need_restore)
 		return;
 
 	mutex_unlock(&genpd->lock);
@@ -244,7 +246,7 @@ static void __pm_genpd_restore_device(st
 
 	mutex_lock(&genpd->lock);
 
-	pdd->need_restore = false;
+	gpd_data->need_restore = false;
 }
 
 /**
@@ -493,7 +495,7 @@ static int pm_genpd_runtime_resume(struc
 		mutex_lock(&genpd->lock);
 	}
 	finish_wait(&genpd->status_wait_queue, &wait);
-	__pm_genpd_restore_device(&dev->power.subsys_data->domain_data, genpd);
+	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
 	genpd->resume_count--;
 	genpd_set_active(genpd);
 	wake_up_all(&genpd->status_wait_queue);
@@ -1080,6 +1082,7 @@ static void pm_genpd_complete(struct dev
  */
 int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
 {
+	struct generic_pm_domain_data *gpd_data;
 	struct pm_domain_data *pdd;
 	int ret = 0;
 
@@ -1106,14 +1109,20 @@ int pm_genpd_add_device(struct generic_p
 			goto out;
 		}
 
+	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
+	if (!gpd_data) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
 	genpd->device_count++;
 
 	dev->pm_domain = &genpd->domain;
 	dev_pm_get_subsys_data(dev);
-	pdd = &dev->power.subsys_data->domain_data;
-	pdd->dev = dev;
-	pdd->need_restore = false;
-	list_add_tail(&pdd->list_node, &genpd->dev_list);
+	dev->power.subsys_data->domain_data = &gpd_data->base;
+	gpd_data->base.dev = dev;
+	gpd_data->need_restore = false;
+	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
 
  out:
 	genpd_release_lock(genpd);
@@ -1152,6 +1161,7 @@ int pm_genpd_remove_device(struct generi
 		pdd->dev = NULL;
 		dev_pm_put_subsys_data(dev);
 		dev->pm_domain = NULL;
+		kfree(to_gpd_data(pdd));
 
 		genpd->device_count--;
 


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

* [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
  2011-09-24 21:23   ` Rafael J. Wysocki
@ 2011-09-24 21:25     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-24 21:25 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet, Ming Lei

From: Rafael J. Wysocki <rjw@sisk.pl>

The rpm_suspend() and rpm_resume() routines execute subsystem or PM
domain callbacks under power.lock if power.irq_safe is set for the
given device.  This is inconsistent with that rpm_idle() does after
commit 02b2677 (PM / Runtime: Allow _put_sync() from
interrupts-disabled context) and is problematic for subsystems and PM
domains wanting to use power.lock for synchronization in their
runtime PM callbacks.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
 1 file changed, 46 insertions(+), 22 deletions(-)

Index: linux/drivers/base/power/runtime.c
=================================--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
 }
 
 /**
+ * __rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	int retval;
+
+	if (dev->power.irq_safe)
+		spin_unlock(&dev->power.lock);
+	else
+		spin_unlock_irq(&dev->power.lock);
+
+	retval = cb(dev);
+
+	if (dev->power.irq_safe)
+		spin_lock(&dev->power.lock);
+	else
+		spin_lock_irq(&dev->power.lock);
+
+	return retval;
+}
+
+/**
  * rpm_idle - Notify device bus type if the device can be suspended.
  * @dev: Device to notify the bus type about.
  * @rpmflags: Flag bits.
@@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
 	else
 		callback = NULL;
 
-	if (callback) {
-		if (dev->power.irq_safe)
-			spin_unlock(&dev->power.lock);
-		else
-			spin_unlock_irq(&dev->power.lock);
-
-		callback(dev);
-
-		if (dev->power.irq_safe)
-			spin_lock(&dev->power.lock);
-		else
-			spin_lock_irq(&dev->power.lock);
-	}
+	if (callback)
+		__rpm_callback(callback, dev);
 
 	dev->power.idle_notification = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
  * @dev: Device to run the callback for.
  */
 static int rpm_callback(int (*cb)(struct device *), struct device *dev)
-	__releases(&dev->power.lock) __acquires(&dev->power.lock)
 {
 	int retval;
 
 	if (!cb)
 		return -ENOSYS;
 
-	if (dev->power.irq_safe) {
-		retval = cb(dev);
-	} else {
-		spin_unlock_irq(&dev->power.lock);
-
-		retval = cb(dev);
+	retval = __rpm_callback(cb, dev);
 
-		spin_lock_irq(&dev->power.lock);
-	}
 	dev->power.runtime_error = retval;
 	return retval != -EACCES ? retval : -EIO;
 }
@@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the other suspend running in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,
@@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the operation carried out in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,


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

* [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
@ 2011-09-24 21:25     ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-24 21:25 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet, Ming Lei

From: Rafael J. Wysocki <rjw@sisk.pl>

The rpm_suspend() and rpm_resume() routines execute subsystem or PM
domain callbacks under power.lock if power.irq_safe is set for the
given device.  This is inconsistent with that rpm_idle() does after
commit 02b2677 (PM / Runtime: Allow _put_sync() from
interrupts-disabled context) and is problematic for subsystems and PM
domains wanting to use power.lock for synchronization in their
runtime PM callbacks.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
 1 file changed, 46 insertions(+), 22 deletions(-)

Index: linux/drivers/base/power/runtime.c
===================================================================
--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
 }
 
 /**
+ * __rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	int retval;
+
+	if (dev->power.irq_safe)
+		spin_unlock(&dev->power.lock);
+	else
+		spin_unlock_irq(&dev->power.lock);
+
+	retval = cb(dev);
+
+	if (dev->power.irq_safe)
+		spin_lock(&dev->power.lock);
+	else
+		spin_lock_irq(&dev->power.lock);
+
+	return retval;
+}
+
+/**
  * rpm_idle - Notify device bus type if the device can be suspended.
  * @dev: Device to notify the bus type about.
  * @rpmflags: Flag bits.
@@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
 	else
 		callback = NULL;
 
-	if (callback) {
-		if (dev->power.irq_safe)
-			spin_unlock(&dev->power.lock);
-		else
-			spin_unlock_irq(&dev->power.lock);
-
-		callback(dev);
-
-		if (dev->power.irq_safe)
-			spin_lock(&dev->power.lock);
-		else
-			spin_lock_irq(&dev->power.lock);
-	}
+	if (callback)
+		__rpm_callback(callback, dev);
 
 	dev->power.idle_notification = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
  * @dev: Device to run the callback for.
  */
 static int rpm_callback(int (*cb)(struct device *), struct device *dev)
-	__releases(&dev->power.lock) __acquires(&dev->power.lock)
 {
 	int retval;
 
 	if (!cb)
 		return -ENOSYS;
 
-	if (dev->power.irq_safe) {
-		retval = cb(dev);
-	} else {
-		spin_unlock_irq(&dev->power.lock);
-
-		retval = cb(dev);
+	retval = __rpm_callback(cb, dev);
 
-		spin_lock_irq(&dev->power.lock);
-	}
 	dev->power.runtime_error = retval;
 	return retval != -EACCES ? retval : -EIO;
 }
@@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the other suspend running in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,
@@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the operation carried out in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,


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

* [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
  2011-09-24 21:23   ` Rafael J. Wysocki
@ 2011-09-24 21:26     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-24 21:26 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet, Ming Lei

From: Rafael J. Wysocki <rjw@sisk.pl>

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  168 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 103 insertions(+), 80 deletions(-)

Index: linux/drivers/base/power/qos.c
=================================--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,7 +286,7 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
@@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
=================================--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
=================================--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);


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

* [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
@ 2011-09-24 21:26     ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-24 21:26 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: LKML, Linux-sh list, Magnus Damm, Kevin Hilman, jean.pihet, Ming Lei

From: Rafael J. Wysocki <rjw@sisk.pl>

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so it has to use device_pm_lock()
and the device PM QoS initialization and destruction should be done
under device_pm_lock() as well.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c |    4 -
 drivers/base/power/qos.c  |  168 ++++++++++++++++++++++++++--------------------
 include/linux/pm.h        |    8 --
 include/linux/pm_qos.h    |    3 
 4 files changed, 103 insertions(+), 80 deletions(-)

Index: linux/drivers/base/power/qos.c
===================================================================
--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,37 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
+static void __dev_pm_qos_constraints_init(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+}
+
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem at device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +156,35 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem at device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	__dev_pm_qos_constraints_init(dev);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +202,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +220,37 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	device_pm_lock();
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (dev->power.constraints) {
+		device_pm_unlock();
+	} else {
+		if (list_empty(&dev->power.entry)) {
+			/* The device has been removed from the system. */
+			device_pm_unlock();
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			device_pm_unlock();
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,7 +286,7 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
@@ -293,7 +327,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +357,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +385,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
===================================================================
--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +118,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
Index: linux/drivers/base/power/main.c
===================================================================
--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -421,13 +421,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +482,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);


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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for
  2011-09-24 21:25     ` Rafael J. Wysocki
@ 2011-09-25  8:24       ` Ming Lei
  -1 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-25  8:24 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Sun, Sep 25, 2011 at 5:25 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> domain callbacks under power.lock if power.irq_safe is set for the
> given device.  This is inconsistent with that rpm_idle() does after
> commit 02b2677 (PM / Runtime: Allow _put_sync() from
> interrupts-disabled context) and is problematic for subsystems and PM
> domains wanting to use power.lock for synchronization in their
> runtime PM callbacks.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Reviewed-by: Ming Lei <tom.leiming@gmail.com>

> ---
>  drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
>  1 file changed, 46 insertions(+), 22 deletions(-)
>
> Index: linux/drivers/base/power/runtime.c
> =================================> --- linux.orig/drivers/base/power/runtime.c
> +++ linux/drivers/base/power/runtime.c
> @@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
>  }
>
>  /**
> + * __rpm_callback - Run a given runtime PM callback for a given device.
> + * @cb: Runtime PM callback to run.
> + * @dev: Device to run the callback for.
> + */
> +static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
> +       __releases(&dev->power.lock) __acquires(&dev->power.lock)
> +{
> +       int retval;
> +
> +       if (dev->power.irq_safe)
> +               spin_unlock(&dev->power.lock);
> +       else
> +               spin_unlock_irq(&dev->power.lock);
> +
> +       retval = cb(dev);
> +
> +       if (dev->power.irq_safe)
> +               spin_lock(&dev->power.lock);
> +       else
> +               spin_lock_irq(&dev->power.lock);
> +
> +       return retval;
> +}
> +
> +/**
>  * rpm_idle - Notify device bus type if the device can be suspended.
>  * @dev: Device to notify the bus type about.
>  * @rpmflags: Flag bits.
> @@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
>        else
>                callback = NULL;
>
> -       if (callback) {
> -               if (dev->power.irq_safe)
> -                       spin_unlock(&dev->power.lock);
> -               else
> -                       spin_unlock_irq(&dev->power.lock);
> -
> -               callback(dev);
> -
> -               if (dev->power.irq_safe)
> -                       spin_lock(&dev->power.lock);
> -               else
> -                       spin_lock_irq(&dev->power.lock);
> -       }
> +       if (callback)
> +               __rpm_callback(callback, dev);
>
>        dev->power.idle_notification = false;
>        wake_up_all(&dev->power.wait_queue);
> @@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
>  * @dev: Device to run the callback for.
>  */
>  static int rpm_callback(int (*cb)(struct device *), struct device *dev)
> -       __releases(&dev->power.lock) __acquires(&dev->power.lock)
>  {
>        int retval;
>
>        if (!cb)
>                return -ENOSYS;
>
> -       if (dev->power.irq_safe) {
> -               retval = cb(dev);
> -       } else {
> -               spin_unlock_irq(&dev->power.lock);
> -
> -               retval = cb(dev);
> +       retval = __rpm_callback(cb, dev);
>
> -               spin_lock_irq(&dev->power.lock);
> -       }
>        dev->power.runtime_error = retval;
>        return retval != -EACCES ? retval : -EIO;
>  }
> @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>                        goto out;
>                }
>
> +               if (dev->power.irq_safe) {
> +                       spin_unlock(&dev->power.lock);
> +
> +                       cpu_relax();
> +
> +                       spin_lock(&dev->power.lock);
> +                       goto repeat;
> +               }
> +
>                /* Wait for the other suspend running in parallel with us. */
>                for (;;) {
>                        prepare_to_wait(&dev->power.wait_queue, &wait,
> @@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
>                        goto out;
>                }
>
> +               if (dev->power.irq_safe) {
> +                       spin_unlock(&dev->power.lock);
> +
> +                       cpu_relax();
> +
> +                       spin_lock(&dev->power.lock);
> +                       goto repeat;
> +               }
> +
>                /* Wait for the operation carried out in parallel with us. */
>                for (;;) {
>                        prepare_to_wait(&dev->power.wait_queue, &wait,
>
>


thanks,
-- 
Ming Lei

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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
@ 2011-09-25  8:24       ` Ming Lei
  0 siblings, 0 replies; 93+ messages in thread
From: Ming Lei @ 2011-09-25  8:24 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, jean.pihet

On Sun, Sep 25, 2011 at 5:25 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> domain callbacks under power.lock if power.irq_safe is set for the
> given device.  This is inconsistent with that rpm_idle() does after
> commit 02b2677 (PM / Runtime: Allow _put_sync() from
> interrupts-disabled context) and is problematic for subsystems and PM
> domains wanting to use power.lock for synchronization in their
> runtime PM callbacks.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Reviewed-by: Ming Lei <tom.leiming@gmail.com>

> ---
>  drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
>  1 file changed, 46 insertions(+), 22 deletions(-)
>
> Index: linux/drivers/base/power/runtime.c
> ===================================================================
> --- linux.orig/drivers/base/power/runtime.c
> +++ linux/drivers/base/power/runtime.c
> @@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
>  }
>
>  /**
> + * __rpm_callback - Run a given runtime PM callback for a given device.
> + * @cb: Runtime PM callback to run.
> + * @dev: Device to run the callback for.
> + */
> +static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
> +       __releases(&dev->power.lock) __acquires(&dev->power.lock)
> +{
> +       int retval;
> +
> +       if (dev->power.irq_safe)
> +               spin_unlock(&dev->power.lock);
> +       else
> +               spin_unlock_irq(&dev->power.lock);
> +
> +       retval = cb(dev);
> +
> +       if (dev->power.irq_safe)
> +               spin_lock(&dev->power.lock);
> +       else
> +               spin_lock_irq(&dev->power.lock);
> +
> +       return retval;
> +}
> +
> +/**
>  * rpm_idle - Notify device bus type if the device can be suspended.
>  * @dev: Device to notify the bus type about.
>  * @rpmflags: Flag bits.
> @@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
>        else
>                callback = NULL;
>
> -       if (callback) {
> -               if (dev->power.irq_safe)
> -                       spin_unlock(&dev->power.lock);
> -               else
> -                       spin_unlock_irq(&dev->power.lock);
> -
> -               callback(dev);
> -
> -               if (dev->power.irq_safe)
> -                       spin_lock(&dev->power.lock);
> -               else
> -                       spin_lock_irq(&dev->power.lock);
> -       }
> +       if (callback)
> +               __rpm_callback(callback, dev);
>
>        dev->power.idle_notification = false;
>        wake_up_all(&dev->power.wait_queue);
> @@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
>  * @dev: Device to run the callback for.
>  */
>  static int rpm_callback(int (*cb)(struct device *), struct device *dev)
> -       __releases(&dev->power.lock) __acquires(&dev->power.lock)
>  {
>        int retval;
>
>        if (!cb)
>                return -ENOSYS;
>
> -       if (dev->power.irq_safe) {
> -               retval = cb(dev);
> -       } else {
> -               spin_unlock_irq(&dev->power.lock);
> -
> -               retval = cb(dev);
> +       retval = __rpm_callback(cb, dev);
>
> -               spin_lock_irq(&dev->power.lock);
> -       }
>        dev->power.runtime_error = retval;
>        return retval != -EACCES ? retval : -EIO;
>  }
> @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>                        goto out;
>                }
>
> +               if (dev->power.irq_safe) {
> +                       spin_unlock(&dev->power.lock);
> +
> +                       cpu_relax();
> +
> +                       spin_lock(&dev->power.lock);
> +                       goto repeat;
> +               }
> +
>                /* Wait for the other suspend running in parallel with us. */
>                for (;;) {
>                        prepare_to_wait(&dev->power.wait_queue, &wait,
> @@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
>                        goto out;
>                }
>
> +               if (dev->power.irq_safe) {
> +                       spin_unlock(&dev->power.lock);
> +
> +                       cpu_relax();
> +
> +                       spin_lock(&dev->power.lock);
> +                       goto repeat;
> +               }
> +
>                /* Wait for the operation carried out in parallel with us. */
>                for (;;) {
>                        prepare_to_wait(&dev->power.wait_queue, &wait,
>
>


thanks,
-- 
Ming Lei

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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
  2011-09-24 21:25     ` Rafael J. Wysocki
@ 2011-09-26 23:59       ` Kevin Hilman
  -1 siblings, 0 replies; 93+ messages in thread
From: Kevin Hilman @ 2011-09-26 23:59 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> domain callbacks under power.lock if power.irq_safe is set for the
> given device.  This is inconsistent with that rpm_idle() does after
> commit 02b2677 (PM / Runtime: Allow _put_sync() from
> interrupts-disabled context) and is problematic for subsystems and PM
> domains wanting to use power.lock for synchronization in their
> runtime PM callbacks.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

The part described here looks right, and is much better for consistency.

Reviewed-by: Kevin Hilman <khilman@ti.com>

but...

[...]

> @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>  			goto out;
>  		}
>  
> +		if (dev->power.irq_safe) {
> +			spin_unlock(&dev->power.lock);
> +
> +			cpu_relax();
> +
> +			spin_lock(&dev->power.lock);
> +			goto repeat;
> +		}
> +


... AFAICT, this isn't directly related to the problem described in the
changelog (or at least I didn't find it obvious), and probably deserves
a comment in the code as well.

Thanks,

Kevin

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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
@ 2011-09-26 23:59       ` Kevin Hilman
  0 siblings, 0 replies; 93+ messages in thread
From: Kevin Hilman @ 2011-09-26 23:59 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> domain callbacks under power.lock if power.irq_safe is set for the
> given device.  This is inconsistent with that rpm_idle() does after
> commit 02b2677 (PM / Runtime: Allow _put_sync() from
> interrupts-disabled context) and is problematic for subsystems and PM
> domains wanting to use power.lock for synchronization in their
> runtime PM callbacks.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

The part described here looks right, and is much better for consistency.

Reviewed-by: Kevin Hilman <khilman@ti.com>

but...

[...]

> @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>  			goto out;
>  		}
>  
> +		if (dev->power.irq_safe) {
> +			spin_unlock(&dev->power.lock);
> +
> +			cpu_relax();
> +
> +			spin_lock(&dev->power.lock);
> +			goto repeat;
> +		}
> +


... AFAICT, this isn't directly related to the problem described in the
changelog (or at least I didn't find it obvious), and probably deserves
a comment in the code as well.

Thanks,

Kevin

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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
  2011-09-26 23:59       ` Kevin Hilman
@ 2011-09-27 17:16         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-27 17:16 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei

On Tuesday, September 27, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> > domain callbacks under power.lock if power.irq_safe is set for the
> > given device.  This is inconsistent with that rpm_idle() does after
> > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> > interrupts-disabled context) and is problematic for subsystems and PM
> > domains wanting to use power.lock for synchronization in their
> > runtime PM callbacks.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> 
> The part described here looks right, and is much better for consistency.
> 
> Reviewed-by: Kevin Hilman <khilman@ti.com>
> 
> but...
> 
> [...]
> 
> > @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
> >  			goto out;
> >  		}
> >  
> > +		if (dev->power.irq_safe) {
> > +			spin_unlock(&dev->power.lock);
> > +
> > +			cpu_relax();
> > +
> > +			spin_lock(&dev->power.lock);
> > +			goto repeat;
> > +		}
> > +
> 
> 
> ... AFAICT, this isn't directly related to the problem described in the
> changelog (or at least I didn't find it obvious),

It is related.  Whether or not it's obvious, I'm not sure. :-)

The problem is that after the changes in __rpm_callback() another CPU may start
executing the same routine for the same device if dev->power.irq_safe is set
(previously, it would block on the dev's power.lock) and it may see
dev->power.runtime_status = RPM_RESUMING or
dev->power.runtime_status = RPM_SUSPENDING, while previously, it wouldn't
reach the relevant code.  Thus we have to modify that code to take
the dev->power.irq_safe case into account.

> and probably deserves a comment in the code as well.

Well, the comment in the code would explain why the commit did what it did,
but it wouldn't be very useful afterwards IMHO.

Perhaps I'll simply add some explanation to the changelog.

Thanks,
Rafael

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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
@ 2011-09-27 17:16         ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-27 17:16 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei

On Tuesday, September 27, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> > domain callbacks under power.lock if power.irq_safe is set for the
> > given device.  This is inconsistent with that rpm_idle() does after
> > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> > interrupts-disabled context) and is problematic for subsystems and PM
> > domains wanting to use power.lock for synchronization in their
> > runtime PM callbacks.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> 
> The part described here looks right, and is much better for consistency.
> 
> Reviewed-by: Kevin Hilman <khilman@ti.com>
> 
> but...
> 
> [...]
> 
> > @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
> >  			goto out;
> >  		}
> >  
> > +		if (dev->power.irq_safe) {
> > +			spin_unlock(&dev->power.lock);
> > +
> > +			cpu_relax();
> > +
> > +			spin_lock(&dev->power.lock);
> > +			goto repeat;
> > +		}
> > +
> 
> 
> ... AFAICT, this isn't directly related to the problem described in the
> changelog (or at least I didn't find it obvious),

It is related.  Whether or not it's obvious, I'm not sure. :-)

The problem is that after the changes in __rpm_callback() another CPU may start
executing the same routine for the same device if dev->power.irq_safe is set
(previously, it would block on the dev's power.lock) and it may see
dev->power.runtime_status == RPM_RESUMING or
dev->power.runtime_status == RPM_SUSPENDING, while previously, it wouldn't
reach the relevant code.  Thus we have to modify that code to take
the dev->power.irq_safe case into account.

> and probably deserves a comment in the code as well.

Well, the comment in the code would explain why the commit did what it did,
but it wouldn't be very useful afterwards IMHO.

Perhaps I'll simply add some explanation to the changelog.

Thanks,
Rafael

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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
  2011-09-27 17:16         ` Rafael J. Wysocki
@ 2011-09-27 19:50           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-27 19:50 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei

On Tuesday, September 27, 2011, Rafael J. Wysocki wrote:
> On Tuesday, September 27, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > 
> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > >
> > > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> > > domain callbacks under power.lock if power.irq_safe is set for the
> > > given device.  This is inconsistent with that rpm_idle() does after
> > > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> > > interrupts-disabled context) and is problematic for subsystems and PM
> > > domains wanting to use power.lock for synchronization in their
> > > runtime PM callbacks.
> > >
> > > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > 
> > The part described here looks right, and is much better for consistency.
> > 
> > Reviewed-by: Kevin Hilman <khilman@ti.com>
> > 
> > but...
> > 
> > [...]
> > 
> > > @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
> > >  			goto out;
> > >  		}
> > >  
> > > +		if (dev->power.irq_safe) {
> > > +			spin_unlock(&dev->power.lock);
> > > +
> > > +			cpu_relax();
> > > +
> > > +			spin_lock(&dev->power.lock);
> > > +			goto repeat;
> > > +		}
> > > +
> > 
> > 
> > ... AFAICT, this isn't directly related to the problem described in the
> > changelog (or at least I didn't find it obvious),
> 
> It is related.  Whether or not it's obvious, I'm not sure. :-)
> 
> The problem is that after the changes in __rpm_callback() another CPU may start
> executing the same routine for the same device if dev->power.irq_safe is set
> (previously, it would block on the dev's power.lock) and it may see
> dev->power.runtime_status = RPM_RESUMING or
> dev->power.runtime_status = RPM_SUSPENDING, while previously, it wouldn't
> reach the relevant code.  Thus we have to modify that code to take
> the dev->power.irq_safe case into account.
> 
> > and probably deserves a comment in the code as well.
> 
> Well, the comment in the code would explain why the commit did what it did,
> but it wouldn't be very useful afterwards IMHO.
> 
> Perhaps I'll simply add some explanation to the changelog.

Below is the patch with the new changelog, for completness.

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Runtime: Don't run callbacks under lock for power.irq_safe set

The rpm_suspend() and rpm_resume() routines execute subsystem or PM
domain callbacks under power.lock if power.irq_safe is set for the
given device.  This is inconsistent with that rpm_idle() does after
commit 02b2677 (PM / Runtime: Allow _put_sync() from
interrupts-disabled context) and is problematic for subsystems and PM
domains wanting to use power.lock for synchronization in their
runtime PM callbacks.

This change requires the code checking if the device's runtime PM
status is RPM_SUSPENDING or RPM_RESUMING to be modified too, to take
the power.irq_safe set case into account (that code wasn't reachable
before with power.irq_safe set, because it's executed with the
device's power.lock held).

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Ming Lei <tom.leiming@gmail.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
 1 file changed, 46 insertions(+), 22 deletions(-)

Index: linux/drivers/base/power/runtime.c
=================================--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
 }
 
 /**
+ * __rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	int retval;
+
+	if (dev->power.irq_safe)
+		spin_unlock(&dev->power.lock);
+	else
+		spin_unlock_irq(&dev->power.lock);
+
+	retval = cb(dev);
+
+	if (dev->power.irq_safe)
+		spin_lock(&dev->power.lock);
+	else
+		spin_lock_irq(&dev->power.lock);
+
+	return retval;
+}
+
+/**
  * rpm_idle - Notify device bus type if the device can be suspended.
  * @dev: Device to notify the bus type about.
  * @rpmflags: Flag bits.
@@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
 	else
 		callback = NULL;
 
-	if (callback) {
-		if (dev->power.irq_safe)
-			spin_unlock(&dev->power.lock);
-		else
-			spin_unlock_irq(&dev->power.lock);
-
-		callback(dev);
-
-		if (dev->power.irq_safe)
-			spin_lock(&dev->power.lock);
-		else
-			spin_lock_irq(&dev->power.lock);
-	}
+	if (callback)
+		__rpm_callback(callback, dev);
 
 	dev->power.idle_notification = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
  * @dev: Device to run the callback for.
  */
 static int rpm_callback(int (*cb)(struct device *), struct device *dev)
-	__releases(&dev->power.lock) __acquires(&dev->power.lock)
 {
 	int retval;
 
 	if (!cb)
 		return -ENOSYS;
 
-	if (dev->power.irq_safe) {
-		retval = cb(dev);
-	} else {
-		spin_unlock_irq(&dev->power.lock);
-
-		retval = cb(dev);
+	retval = __rpm_callback(cb, dev);
 
-		spin_lock_irq(&dev->power.lock);
-	}
 	dev->power.runtime_error = retval;
 	return retval != -EACCES ? retval : -EIO;
 }
@@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the other suspend running in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,
@@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the operation carried out in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,

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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
@ 2011-09-27 19:50           ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-27 19:50 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei

On Tuesday, September 27, 2011, Rafael J. Wysocki wrote:
> On Tuesday, September 27, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > 
> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > >
> > > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> > > domain callbacks under power.lock if power.irq_safe is set for the
> > > given device.  This is inconsistent with that rpm_idle() does after
> > > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> > > interrupts-disabled context) and is problematic for subsystems and PM
> > > domains wanting to use power.lock for synchronization in their
> > > runtime PM callbacks.
> > >
> > > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > 
> > The part described here looks right, and is much better for consistency.
> > 
> > Reviewed-by: Kevin Hilman <khilman@ti.com>
> > 
> > but...
> > 
> > [...]
> > 
> > > @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
> > >  			goto out;
> > >  		}
> > >  
> > > +		if (dev->power.irq_safe) {
> > > +			spin_unlock(&dev->power.lock);
> > > +
> > > +			cpu_relax();
> > > +
> > > +			spin_lock(&dev->power.lock);
> > > +			goto repeat;
> > > +		}
> > > +
> > 
> > 
> > ... AFAICT, this isn't directly related to the problem described in the
> > changelog (or at least I didn't find it obvious),
> 
> It is related.  Whether or not it's obvious, I'm not sure. :-)
> 
> The problem is that after the changes in __rpm_callback() another CPU may start
> executing the same routine for the same device if dev->power.irq_safe is set
> (previously, it would block on the dev's power.lock) and it may see
> dev->power.runtime_status == RPM_RESUMING or
> dev->power.runtime_status == RPM_SUSPENDING, while previously, it wouldn't
> reach the relevant code.  Thus we have to modify that code to take
> the dev->power.irq_safe case into account.
> 
> > and probably deserves a comment in the code as well.
> 
> Well, the comment in the code would explain why the commit did what it did,
> but it wouldn't be very useful afterwards IMHO.
> 
> Perhaps I'll simply add some explanation to the changelog.

Below is the patch with the new changelog, for completness.

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Runtime: Don't run callbacks under lock for power.irq_safe set

The rpm_suspend() and rpm_resume() routines execute subsystem or PM
domain callbacks under power.lock if power.irq_safe is set for the
given device.  This is inconsistent with that rpm_idle() does after
commit 02b2677 (PM / Runtime: Allow _put_sync() from
interrupts-disabled context) and is problematic for subsystems and PM
domains wanting to use power.lock for synchronization in their
runtime PM callbacks.

This change requires the code checking if the device's runtime PM
status is RPM_SUSPENDING or RPM_RESUMING to be modified too, to take
the power.irq_safe set case into account (that code wasn't reachable
before with power.irq_safe set, because it's executed with the
device's power.lock held).

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Ming Lei <tom.leiming@gmail.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
 1 file changed, 46 insertions(+), 22 deletions(-)

Index: linux/drivers/base/power/runtime.c
===================================================================
--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
 }
 
 /**
+ * __rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	int retval;
+
+	if (dev->power.irq_safe)
+		spin_unlock(&dev->power.lock);
+	else
+		spin_unlock_irq(&dev->power.lock);
+
+	retval = cb(dev);
+
+	if (dev->power.irq_safe)
+		spin_lock(&dev->power.lock);
+	else
+		spin_lock_irq(&dev->power.lock);
+
+	return retval;
+}
+
+/**
  * rpm_idle - Notify device bus type if the device can be suspended.
  * @dev: Device to notify the bus type about.
  * @rpmflags: Flag bits.
@@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
 	else
 		callback = NULL;
 
-	if (callback) {
-		if (dev->power.irq_safe)
-			spin_unlock(&dev->power.lock);
-		else
-			spin_unlock_irq(&dev->power.lock);
-
-		callback(dev);
-
-		if (dev->power.irq_safe)
-			spin_lock(&dev->power.lock);
-		else
-			spin_lock_irq(&dev->power.lock);
-	}
+	if (callback)
+		__rpm_callback(callback, dev);
 
 	dev->power.idle_notification = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
  * @dev: Device to run the callback for.
  */
 static int rpm_callback(int (*cb)(struct device *), struct device *dev)
-	__releases(&dev->power.lock) __acquires(&dev->power.lock)
 {
 	int retval;
 
 	if (!cb)
 		return -ENOSYS;
 
-	if (dev->power.irq_safe) {
-		retval = cb(dev);
-	} else {
-		spin_unlock_irq(&dev->power.lock);
-
-		retval = cb(dev);
+	retval = __rpm_callback(cb, dev);
 
-		spin_lock_irq(&dev->power.lock);
-	}
 	dev->power.runtime_error = retval;
 	return retval != -EACCES ? retval : -EIO;
 }
@@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the other suspend running in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,
@@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the operation carried out in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,

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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
  2011-09-27 19:50           ` Rafael J. Wysocki
@ 2011-09-29  0:17             ` Kevin Hilman
  -1 siblings, 0 replies; 93+ messages in thread
From: Kevin Hilman @ 2011-09-29  0:17 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Tuesday, September 27, 2011, Rafael J. Wysocki wrote:
>> On Tuesday, September 27, 2011, Kevin Hilman wrote:
>> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> > 
>> > > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > >
>> > > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
>> > > domain callbacks under power.lock if power.irq_safe is set for the
>> > > given device.  This is inconsistent with that rpm_idle() does after
>> > > commit 02b2677 (PM / Runtime: Allow _put_sync() from
>> > > interrupts-disabled context) and is problematic for subsystems and PM
>> > > domains wanting to use power.lock for synchronization in their
>> > > runtime PM callbacks.
>> > >
>> > > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > 
>> > The part described here looks right, and is much better for consistency.
>> > 
>> > Reviewed-by: Kevin Hilman <khilman@ti.com>
>> > 
>> > but...
>> > 
>> > [...]
>> > 
>> > > @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>> > >  			goto out;
>> > >  		}
>> > >  
>> > > +		if (dev->power.irq_safe) {
>> > > +			spin_unlock(&dev->power.lock);
>> > > +
>> > > +			cpu_relax();
>> > > +
>> > > +			spin_lock(&dev->power.lock);
>> > > +			goto repeat;
>> > > +		}
>> > > +
>> > 
>> > 
>> > ... AFAICT, this isn't directly related to the problem described in the
>> > changelog (or at least I didn't find it obvious),
>> 
>> It is related.  Whether or not it's obvious, I'm not sure. :-)
>> 
>> The problem is that after the changes in __rpm_callback() another CPU may start
>> executing the same routine for the same device if dev->power.irq_safe is set
>> (previously, it would block on the dev's power.lock) and it may see
>> dev->power.runtime_status = RPM_RESUMING or
>> dev->power.runtime_status = RPM_SUSPENDING, while previously, it wouldn't
>> reach the relevant code.  Thus we have to modify that code to take
>> the dev->power.irq_safe case into account.
>> 
>> > and probably deserves a comment in the code as well.
>> 
>> Well, the comment in the code would explain why the commit did what it did,
>> but it wouldn't be very useful afterwards IMHO.
>> 
>> Perhaps I'll simply add some explanation to the changelog.
>
> Below is the patch with the new changelog, for completness.

Thanks, it's much clearer now.

Kevin

> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / Runtime: Don't run callbacks under lock for power.irq_safe set
>
> The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> domain callbacks under power.lock if power.irq_safe is set for the
> given device.  This is inconsistent with that rpm_idle() does after
> commit 02b2677 (PM / Runtime: Allow _put_sync() from
> interrupts-disabled context) and is problematic for subsystems and PM
> domains wanting to use power.lock for synchronization in their
> runtime PM callbacks.
>
> This change requires the code checking if the device's runtime PM
> status is RPM_SUSPENDING or RPM_RESUMING to be modified too, to take
> the power.irq_safe set case into account (that code wasn't reachable
> before with power.irq_safe set, because it's executed with the
> device's power.lock held).
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Reviewed-by: Ming Lei <tom.leiming@gmail.com>
> Reviewed-by: Kevin Hilman <khilman@ti.com>
> ---
>  drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
>  1 file changed, 46 insertions(+), 22 deletions(-)
>
> Index: linux/drivers/base/power/runtime.c
> =================================> --- linux.orig/drivers/base/power/runtime.c
> +++ linux/drivers/base/power/runtime.c
> @@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
>  }
>  
>  /**
> + * __rpm_callback - Run a given runtime PM callback for a given device.
> + * @cb: Runtime PM callback to run.
> + * @dev: Device to run the callback for.
> + */
> +static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
> +	__releases(&dev->power.lock) __acquires(&dev->power.lock)
> +{
> +	int retval;
> +
> +	if (dev->power.irq_safe)
> +		spin_unlock(&dev->power.lock);
> +	else
> +		spin_unlock_irq(&dev->power.lock);
> +
> +	retval = cb(dev);
> +
> +	if (dev->power.irq_safe)
> +		spin_lock(&dev->power.lock);
> +	else
> +		spin_lock_irq(&dev->power.lock);
> +
> +	return retval;
> +}
> +
> +/**
>   * rpm_idle - Notify device bus type if the device can be suspended.
>   * @dev: Device to notify the bus type about.
>   * @rpmflags: Flag bits.
> @@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
>  	else
>  		callback = NULL;
>  
> -	if (callback) {
> -		if (dev->power.irq_safe)
> -			spin_unlock(&dev->power.lock);
> -		else
> -			spin_unlock_irq(&dev->power.lock);
> -
> -		callback(dev);
> -
> -		if (dev->power.irq_safe)
> -			spin_lock(&dev->power.lock);
> -		else
> -			spin_lock_irq(&dev->power.lock);
> -	}
> +	if (callback)
> +		__rpm_callback(callback, dev);
>  
>  	dev->power.idle_notification = false;
>  	wake_up_all(&dev->power.wait_queue);
> @@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
>   * @dev: Device to run the callback for.
>   */
>  static int rpm_callback(int (*cb)(struct device *), struct device *dev)
> -	__releases(&dev->power.lock) __acquires(&dev->power.lock)
>  {
>  	int retval;
>  
>  	if (!cb)
>  		return -ENOSYS;
>  
> -	if (dev->power.irq_safe) {
> -		retval = cb(dev);
> -	} else {
> -		spin_unlock_irq(&dev->power.lock);
> -
> -		retval = cb(dev);
> +	retval = __rpm_callback(cb, dev);
>  
> -		spin_lock_irq(&dev->power.lock);
> -	}
>  	dev->power.runtime_error = retval;
>  	return retval != -EACCES ? retval : -EIO;
>  }
> @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>  			goto out;
>  		}
>  
> +		if (dev->power.irq_safe) {
> +			spin_unlock(&dev->power.lock);
> +
> +			cpu_relax();
> +
> +			spin_lock(&dev->power.lock);
> +			goto repeat;
> +		}
> +
>  		/* Wait for the other suspend running in parallel with us. */
>  		for (;;) {
>  			prepare_to_wait(&dev->power.wait_queue, &wait,
> @@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
>  			goto out;
>  		}
>  
> +		if (dev->power.irq_safe) {
> +			spin_unlock(&dev->power.lock);
> +
> +			cpu_relax();
> +
> +			spin_lock(&dev->power.lock);
> +			goto repeat;
> +		}
> +
>  		/* Wait for the operation carried out in parallel with us. */
>  		for (;;) {
>  			prepare_to_wait(&dev->power.wait_queue, &wait,
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

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

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
@ 2011-09-29  0:17             ` Kevin Hilman
  0 siblings, 0 replies; 93+ messages in thread
From: Kevin Hilman @ 2011-09-29  0:17 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Tuesday, September 27, 2011, Rafael J. Wysocki wrote:
>> On Tuesday, September 27, 2011, Kevin Hilman wrote:
>> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> > 
>> > > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > >
>> > > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
>> > > domain callbacks under power.lock if power.irq_safe is set for the
>> > > given device.  This is inconsistent with that rpm_idle() does after
>> > > commit 02b2677 (PM / Runtime: Allow _put_sync() from
>> > > interrupts-disabled context) and is problematic for subsystems and PM
>> > > domains wanting to use power.lock for synchronization in their
>> > > runtime PM callbacks.
>> > >
>> > > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > 
>> > The part described here looks right, and is much better for consistency.
>> > 
>> > Reviewed-by: Kevin Hilman <khilman@ti.com>
>> > 
>> > but...
>> > 
>> > [...]
>> > 
>> > > @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>> > >  			goto out;
>> > >  		}
>> > >  
>> > > +		if (dev->power.irq_safe) {
>> > > +			spin_unlock(&dev->power.lock);
>> > > +
>> > > +			cpu_relax();
>> > > +
>> > > +			spin_lock(&dev->power.lock);
>> > > +			goto repeat;
>> > > +		}
>> > > +
>> > 
>> > 
>> > ... AFAICT, this isn't directly related to the problem described in the
>> > changelog (or at least I didn't find it obvious),
>> 
>> It is related.  Whether or not it's obvious, I'm not sure. :-)
>> 
>> The problem is that after the changes in __rpm_callback() another CPU may start
>> executing the same routine for the same device if dev->power.irq_safe is set
>> (previously, it would block on the dev's power.lock) and it may see
>> dev->power.runtime_status == RPM_RESUMING or
>> dev->power.runtime_status == RPM_SUSPENDING, while previously, it wouldn't
>> reach the relevant code.  Thus we have to modify that code to take
>> the dev->power.irq_safe case into account.
>> 
>> > and probably deserves a comment in the code as well.
>> 
>> Well, the comment in the code would explain why the commit did what it did,
>> but it wouldn't be very useful afterwards IMHO.
>> 
>> Perhaps I'll simply add some explanation to the changelog.
>
> Below is the patch with the new changelog, for completness.

Thanks, it's much clearer now.

Kevin

> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / Runtime: Don't run callbacks under lock for power.irq_safe set
>
> The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> domain callbacks under power.lock if power.irq_safe is set for the
> given device.  This is inconsistent with that rpm_idle() does after
> commit 02b2677 (PM / Runtime: Allow _put_sync() from
> interrupts-disabled context) and is problematic for subsystems and PM
> domains wanting to use power.lock for synchronization in their
> runtime PM callbacks.
>
> This change requires the code checking if the device's runtime PM
> status is RPM_SUSPENDING or RPM_RESUMING to be modified too, to take
> the power.irq_safe set case into account (that code wasn't reachable
> before with power.irq_safe set, because it's executed with the
> device's power.lock held).
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Reviewed-by: Ming Lei <tom.leiming@gmail.com>
> Reviewed-by: Kevin Hilman <khilman@ti.com>
> ---
>  drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
>  1 file changed, 46 insertions(+), 22 deletions(-)
>
> Index: linux/drivers/base/power/runtime.c
> ===================================================================
> --- linux.orig/drivers/base/power/runtime.c
> +++ linux/drivers/base/power/runtime.c
> @@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
>  }
>  
>  /**
> + * __rpm_callback - Run a given runtime PM callback for a given device.
> + * @cb: Runtime PM callback to run.
> + * @dev: Device to run the callback for.
> + */
> +static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
> +	__releases(&dev->power.lock) __acquires(&dev->power.lock)
> +{
> +	int retval;
> +
> +	if (dev->power.irq_safe)
> +		spin_unlock(&dev->power.lock);
> +	else
> +		spin_unlock_irq(&dev->power.lock);
> +
> +	retval = cb(dev);
> +
> +	if (dev->power.irq_safe)
> +		spin_lock(&dev->power.lock);
> +	else
> +		spin_lock_irq(&dev->power.lock);
> +
> +	return retval;
> +}
> +
> +/**
>   * rpm_idle - Notify device bus type if the device can be suspended.
>   * @dev: Device to notify the bus type about.
>   * @rpmflags: Flag bits.
> @@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
>  	else
>  		callback = NULL;
>  
> -	if (callback) {
> -		if (dev->power.irq_safe)
> -			spin_unlock(&dev->power.lock);
> -		else
> -			spin_unlock_irq(&dev->power.lock);
> -
> -		callback(dev);
> -
> -		if (dev->power.irq_safe)
> -			spin_lock(&dev->power.lock);
> -		else
> -			spin_lock_irq(&dev->power.lock);
> -	}
> +	if (callback)
> +		__rpm_callback(callback, dev);
>  
>  	dev->power.idle_notification = false;
>  	wake_up_all(&dev->power.wait_queue);
> @@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
>   * @dev: Device to run the callback for.
>   */
>  static int rpm_callback(int (*cb)(struct device *), struct device *dev)
> -	__releases(&dev->power.lock) __acquires(&dev->power.lock)
>  {
>  	int retval;
>  
>  	if (!cb)
>  		return -ENOSYS;
>  
> -	if (dev->power.irq_safe) {
> -		retval = cb(dev);
> -	} else {
> -		spin_unlock_irq(&dev->power.lock);
> -
> -		retval = cb(dev);
> +	retval = __rpm_callback(cb, dev);
>  
> -		spin_lock_irq(&dev->power.lock);
> -	}
>  	dev->power.runtime_error = retval;
>  	return retval != -EACCES ? retval : -EIO;
>  }
> @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>  			goto out;
>  		}
>  
> +		if (dev->power.irq_safe) {
> +			spin_unlock(&dev->power.lock);
> +
> +			cpu_relax();
> +
> +			spin_lock(&dev->power.lock);
> +			goto repeat;
> +		}
> +
>  		/* Wait for the other suspend running in parallel with us. */
>  		for (;;) {
>  			prepare_to_wait(&dev->power.wait_queue, &wait,
> @@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
>  			goto out;
>  		}
>  
> +		if (dev->power.irq_safe) {
> +			spin_unlock(&dev->power.lock);
> +
> +			cpu_relax();
> +
> +			spin_lock(&dev->power.lock);
> +			goto repeat;
> +		}
> +
>  		/* Wait for the operation carried out in parallel with us. */
>  		for (;;) {
>  			prepare_to_wait(&dev->power.wait_queue, &wait,
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

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

* Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
  2011-09-24 21:26     ` Rafael J. Wysocki
@ 2011-09-29  8:11       ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-29  8:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, Ming Lei

Hi Rafael,

I have a few minor remarks, inlined below.

On Sat, Sep 24, 2011 at 11:26 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
>
> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so it has to use device_pm_lock()
> and the device PM QoS initialization and destruction should be done
> under device_pm_lock() as well.
It might be good to mention dev_pm_qos_mtx which is used internally by
the per-device PM QoS code.

>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Other then the minor remarks:
Acked-by: Jean Pihet <j-pihet@ti.com>

> ---
>  drivers/base/power/main.c |    4 -
>  drivers/base/power/qos.c  |  168 ++++++++++++++++++++++++++--------------------
>  include/linux/pm.h        |    8 --
>  include/linux/pm_qos.h    |    3
>  4 files changed, 103 insertions(+), 80 deletions(-)
>
> Index: linux/drivers/base/power/qos.c
> =================================> --- linux.orig/drivers/base/power/qos.c
> +++ linux/drivers/base/power/qos.c
...

>
> +static void __dev_pm_qos_constraints_init(struct device *dev)
I would change the name or add a comment stating that this is an
internal function which reinits the constraints field under the lock.

Thanks & regards,
Jean

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

* Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
@ 2011-09-29  8:11       ` Jean Pihet
  0 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-29  8:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, Ming Lei

Hi Rafael,

I have a few minor remarks, inlined below.

On Sat, Sep 24, 2011 at 11:26 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
>
> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so it has to use device_pm_lock()
> and the device PM QoS initialization and destruction should be done
> under device_pm_lock() as well.
It might be good to mention dev_pm_qos_mtx which is used internally by
the per-device PM QoS code.

>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Other then the minor remarks:
Acked-by: Jean Pihet <j-pihet@ti.com>

> ---
>  drivers/base/power/main.c |    4 -
>  drivers/base/power/qos.c  |  168 ++++++++++++++++++++++++++--------------------
>  include/linux/pm.h        |    8 --
>  include/linux/pm_qos.h    |    3
>  4 files changed, 103 insertions(+), 80 deletions(-)
>
> Index: linux/drivers/base/power/qos.c
> ===================================================================
> --- linux.orig/drivers/base/power/qos.c
> +++ linux/drivers/base/power/qos.c
...

>
> +static void __dev_pm_qos_constraints_init(struct device *dev)
I would change the name or add a comment stating that this is an
internal function which reinits the constraints field under the lock.

Thanks & regards,
Jean

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

* Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
  2011-09-29  8:11       ` Jean Pihet
@ 2011-09-29 20:33         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-29 20:33 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, Ming Lei

Hi,

On Thursday, September 29, 2011, Jean Pihet wrote:
> Hi Rafael,
> 
> I have a few minor remarks, inlined below.

In the meantime it turned out that the patch caused a build failure
for CONFIG_PM_SLEEP unset (your original patches won't really work
in this case too), so I posted a patch to fix it.  However, since
you had remarks anyway, I've simply folded the fix into the next
version of the $subject patch, which is appended.

> On Sat, Sep 24, 2011 at 11:26 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > To read the current PM QoS value for a given device we need to
> > make sure that the device's power.constraints object won't be
> > removed while we're doing that.  For this reason, put the
> > operation under dev->power.lock and acquire the lock
> > around the initialization and removal of power.constraints.
> >
> > Moreover, since we're using the value of power.constraints to
> > determine whether or not the object is present, the
> > power.constraints_state field isn't necessary any more and may be
> > removed.  However, dev_pm_qos_add_request() needs to check if the
> > device is being removed from the system before allocating a new
> > PM QoS constraints object for it, so it has to use device_pm_lock()
> > and the device PM QoS initialization and destruction should be done
> > under device_pm_lock() as well.
> It might be good to mention dev_pm_qos_mtx which is used internally by
> the per-device PM QoS code.
> 
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Other then the minor remarks:
> Acked-by: Jean Pihet <j-pihet@ti.com>
> 
> > ---
> >  drivers/base/power/main.c |    4 -
> >  drivers/base/power/qos.c  |  168 ++++++++++++++++++++++++++--------------------
> >  include/linux/pm.h        |    8 --
> >  include/linux/pm_qos.h    |    3
> >  4 files changed, 103 insertions(+), 80 deletions(-)
> >
> > Index: linux/drivers/base/power/qos.c
> > =================================> > --- linux.orig/drivers/base/power/qos.c
> > +++ linux/drivers/base/power/qos.c
> ...
> 
> >
> > +static void __dev_pm_qos_constraints_init(struct device *dev)
> I would change the name or add a comment stating that this is an
> internal function which reinits the constraints field under the lock.

This function was used only once, so it wasn't really necessary.  I simply
removed it.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / QoS: Add function dev_pm_qos_read_value() (v3)

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so make it use the
power.power_state field of struct device for this purpose.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c  |    6 -
 drivers/base/power/power.h |   10 ++
 drivers/base/power/qos.c   |  160 +++++++++++++++++++++++++--------------------
 include/linux/pm.h         |   10 --
 include/linux/pm_qos.h     |   12 ++-
 5 files changed, 114 insertions(+), 84 deletions(-)

Index: linux/drivers/base/power/qos.c
=================================--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,31 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem during device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
+	dev->power.power_state = PMSG_ON;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +150,38 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem on device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	dev->power.power_state = PMSG_INVALID;
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +199,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +217,32 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (!dev->power.constraints) {
+		if (dev->power.power_state.event = PM_EVENT_INVALID) {
+			/* The device has been removed from the system. */
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,7 +278,7 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
@@ -293,7 +319,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state = DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +349,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +377,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
=================================--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -7,6 +7,7 @@
 #include <linux/plist.h>
 #include <linux/notifier.h>
 #include <linux/miscdevice.h>
+#include <linux/device.h>
 
 #define PM_QOS_RESERVED 0
 #define PM_QOS_CPU_DMA_LATENCY 1
@@ -77,6 +78,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +119,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
@@ -139,9 +143,13 @@ static inline int dev_pm_qos_remove_glob
 					struct notifier_block *notifier)
 			{ return 0; }
 static inline void dev_pm_qos_constraints_init(struct device *dev)
-			{ return; }
+{
+	dev->power.power_state = PMSG_ON;
+}
 static inline void dev_pm_qos_constraints_destroy(struct device *dev)
-			{ return; }
+{
+	dev->power.power_state = PMSG_INVALID;
+}
 #endif
 
 #endif
Index: linux/drivers/base/power/main.c
=================================--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -22,7 +22,6 @@
 #include <linux/mutex.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
-#include <linux/pm_qos.h>
 #include <linux/resume-trace.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
@@ -67,6 +66,7 @@ void device_pm_init(struct device *dev)
 	spin_lock_init(&dev->power.lock);
 	pm_runtime_init(dev);
 	INIT_LIST_HEAD(&dev->power.entry);
+	dev->power.power_state = PMSG_INVALID;
 }
 
 /**
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -326,6 +326,7 @@ extern struct dev_pm_ops generic_subsys_
  *			requested by a driver.
  */
 
+#define PM_EVENT_INVALID	(-1)
 #define PM_EVENT_ON		0x0000
 #define PM_EVENT_FREEZE 	0x0001
 #define PM_EVENT_SUSPEND	0x0002
@@ -346,6 +347,7 @@ extern struct dev_pm_ops generic_subsys_
 #define PM_EVENT_AUTO_SUSPEND	(PM_EVENT_AUTO | PM_EVENT_SUSPEND)
 #define PM_EVENT_AUTO_RESUME	(PM_EVENT_AUTO | PM_EVENT_RESUME)
 
+#define PMSG_INVALID	((struct pm_message){ .event = PM_EVENT_INVALID, })
 #define PMSG_ON		((struct pm_message){ .event = PM_EVENT_ON, })
 #define PMSG_FREEZE	((struct pm_message){ .event = PM_EVENT_FREEZE, })
 #define PMSG_QUIESCE	((struct pm_message){ .event = PM_EVENT_QUIESCE, })
@@ -421,13 +423,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +484,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);
Index: linux/drivers/base/power/power.h
=================================--- linux.orig/drivers/base/power/power.h
+++ linux/drivers/base/power/power.h
@@ -1,3 +1,5 @@
+#include <linux/pm_qos.h>
+
 #ifdef CONFIG_PM_RUNTIME
 
 extern void pm_runtime_init(struct device *dev);
@@ -35,15 +37,21 @@ extern void device_pm_move_last(struct d
 static inline void device_pm_init(struct device *dev)
 {
 	spin_lock_init(&dev->power.lock);
+	dev->power.power_state = PMSG_INVALID;
 	pm_runtime_init(dev);
 }
 
+static inline void device_pm_add(struct device *dev)
+{
+	dev_pm_qos_constraints_init(dev);
+}
+
 static inline void device_pm_remove(struct device *dev)
 {
+	dev_pm_qos_constraints_destroy(dev);
 	pm_runtime_remove(dev);
 }
 
-static inline void device_pm_add(struct device *dev) {}
 static inline void device_pm_move_before(struct device *deva,
 					 struct device *devb) {}
 static inline void device_pm_move_after(struct device *deva,

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

* Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
@ 2011-09-29 20:33         ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-29 20:33 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, Ming Lei

Hi,

On Thursday, September 29, 2011, Jean Pihet wrote:
> Hi Rafael,
> 
> I have a few minor remarks, inlined below.

In the meantime it turned out that the patch caused a build failure
for CONFIG_PM_SLEEP unset (your original patches won't really work
in this case too), so I posted a patch to fix it.  However, since
you had remarks anyway, I've simply folded the fix into the next
version of the $subject patch, which is appended.

> On Sat, Sep 24, 2011 at 11:26 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > To read the current PM QoS value for a given device we need to
> > make sure that the device's power.constraints object won't be
> > removed while we're doing that.  For this reason, put the
> > operation under dev->power.lock and acquire the lock
> > around the initialization and removal of power.constraints.
> >
> > Moreover, since we're using the value of power.constraints to
> > determine whether or not the object is present, the
> > power.constraints_state field isn't necessary any more and may be
> > removed.  However, dev_pm_qos_add_request() needs to check if the
> > device is being removed from the system before allocating a new
> > PM QoS constraints object for it, so it has to use device_pm_lock()
> > and the device PM QoS initialization and destruction should be done
> > under device_pm_lock() as well.
> It might be good to mention dev_pm_qos_mtx which is used internally by
> the per-device PM QoS code.
> 
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Other then the minor remarks:
> Acked-by: Jean Pihet <j-pihet@ti.com>
> 
> > ---
> >  drivers/base/power/main.c |    4 -
> >  drivers/base/power/qos.c  |  168 ++++++++++++++++++++++++++--------------------
> >  include/linux/pm.h        |    8 --
> >  include/linux/pm_qos.h    |    3
> >  4 files changed, 103 insertions(+), 80 deletions(-)
> >
> > Index: linux/drivers/base/power/qos.c
> > ===================================================================
> > --- linux.orig/drivers/base/power/qos.c
> > +++ linux/drivers/base/power/qos.c
> ...
> 
> >
> > +static void __dev_pm_qos_constraints_init(struct device *dev)
> I would change the name or add a comment stating that this is an
> internal function which reinits the constraints field under the lock.

This function was used only once, so it wasn't really necessary.  I simply
removed it.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / QoS: Add function dev_pm_qos_read_value() (v3)

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that.  For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed.  However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so make it use the
power.power_state field of struct device for this purpose.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/main.c  |    6 -
 drivers/base/power/power.h |   10 ++
 drivers/base/power/qos.c   |  160 +++++++++++++++++++++++++--------------------
 include/linux/pm.h         |   10 --
 include/linux/pm_qos.h     |   12 ++-
 5 files changed, 114 insertions(+), 84 deletions(-)

Index: linux/drivers/base/power/qos.c
===================================================================
--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -30,15 +30,6 @@
  * . To minimize the data usage by the per-device constraints, the data struct
  *   is only allocated at the first call to dev_pm_qos_add_request.
  * . The data is later free'd when the device is removed from the system.
- * . The constraints_state variable from dev_pm_info tracks the data struct
- *    allocation state:
- *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
- *     allocated,
- *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
- *     allocated at the first call to dev_pm_qos_add_request,
- *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
- *     PM QoS constraints framework is operational and constraints can be
- *     added, updated or removed using the dev_pm_qos_* API.
  *  . A global mutex protects the constraints users from the data being
  *     allocated and free'd.
  */
@@ -51,8 +42,30 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+
 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
 
+/**
+ * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * @dev: Device to get the PM QoS constraint value for.
+ */
+s32 dev_pm_qos_read_value(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	unsigned long flags;
+	s32 ret = 0;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	c = dev->power.constraints;
+	if (c)
+		ret = pm_qos_read_value(c);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	return ret;
+}
+
 /*
  * apply_constraint
  * @req: constraint request to apply
@@ -105,27 +118,31 @@ static int dev_pm_qos_constraints_alloca
 	}
 	BLOCKING_INIT_NOTIFIER_HEAD(n);
 
+	plist_head_init(&c->list);
+	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	c->type = PM_QOS_MIN;
+	c->notifiers = n;
+
+	spin_lock_irq(&dev->power.lock);
 	dev->power.constraints = c;
-	plist_head_init(&dev->power.constraints->list);
-	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
-	dev->power.constraints->type = PM_QOS_MIN;
-	dev->power.constraints->notifiers = n;
-	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
 }
 
 /**
- * dev_pm_qos_constraints_init
+ * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
  * @dev: target device
  *
- * Called from the device PM subsystem at device insertion
+ * Called from the device PM subsystem during device insertion under
+ * device_pm_lock().
  */
 void dev_pm_qos_constraints_init(struct device *dev)
 {
 	mutex_lock(&dev_pm_qos_mtx);
-	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	dev->power.constraints = NULL;
+	dev->power.power_state = PMSG_ON;
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -133,34 +150,38 @@ void dev_pm_qos_constraints_init(struct
  * dev_pm_qos_constraints_destroy
  * @dev: target device
  *
- * Called from the device PM subsystem at device removal
+ * Called from the device PM subsystem on device removal under device_pm_lock().
  */
 void dev_pm_qos_constraints_destroy(struct device *dev)
 {
 	struct dev_pm_qos_request *req, *tmp;
+	struct pm_qos_constraints *c;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
-		/* Flush the constraints list for the device */
-		plist_for_each_entry_safe(req, tmp,
-					  &dev->power.constraints->list,
-					  node) {
-			/*
-			 * Update constraints list and call the notification
-			 * callbacks if needed
-			 */
-			apply_constraint(req, PM_QOS_REMOVE_REQ,
-					 PM_QOS_DEFAULT_VALUE);
-			memset(req, 0, sizeof(*req));
-		}
+	dev->power.power_state = PMSG_INVALID;
+	c = dev->power.constraints;
+	if (!c)
+		goto out;
 
-		kfree(dev->power.constraints->notifiers);
-		kfree(dev->power.constraints);
-		dev->power.constraints = NULL;
+	/* Flush the constraints list for the device */
+	plist_for_each_entry_safe(req, tmp, &c->list, node) {
+		/*
+		 * Update constraints list and call the notification
+		 * callbacks if needed
+		 */
+		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
 	}
-	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
 
+	spin_lock_irq(&dev->power.lock);
+	dev->power.constraints = NULL;
+	spin_unlock_irq(&dev->power.lock);
+
+	kfree(c->notifiers);
+	kfree(c);
+
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
 }
 
@@ -178,8 +199,9 @@ void dev_pm_qos_constraints_destroy(stru
  *
  * Returns 1 if the aggregated constraint value has changed,
  * 0 if the aggregated constraint value has not changed,
- * -EINVAL in case of wrong parameters, -ENODEV if the device has been
- * removed from the system
+ * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
+ * to allocate for data structures, -ENODEV if the device has just been removed
+ * from the system.
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value)
@@ -195,28 +217,32 @@ int dev_pm_qos_add_request(struct device
 		return -EINVAL;
 	}
 
-	mutex_lock(&dev_pm_qos_mtx);
 	req->dev = dev;
 
-	/* Return if the device has been removed */
-	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
-		ret = -ENODEV;
-		goto out;
-	}
+	mutex_lock(&dev_pm_qos_mtx);
 
-	/*
-	 * Allocate the constraints data on the first call to add_request,
-	 * i.e. only if the data is not already allocated and if the device has
-	 * not been removed
-	 */
-	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
-		ret = dev_pm_qos_constraints_allocate(dev);
+	if (!dev->power.constraints) {
+		if (dev->power.power_state.event == PM_EVENT_INVALID) {
+			/* The device has been removed from the system. */
+			req->dev = NULL;
+			ret = -ENODEV;
+			goto out;
+		} else {
+			/*
+			 * Allocate the constraints data on the first call to
+			 * add_request, i.e. only if the data is not already
+			 * allocated and if the device has not been removed.
+			 */
+			ret = dev_pm_qos_constraints_allocate(dev);
+		}
+	}
 
 	if (!ret)
 		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
-out:
+ out:
 	mutex_unlock(&dev_pm_qos_mtx);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -252,7 +278,7 @@ int dev_pm_qos_update_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		if (new_value != req->node.prio)
 			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 					       new_value);
@@ -293,7 +319,7 @@ int dev_pm_qos_remove_request(struct dev
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+	if (req->dev->power.constraints) {
 		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
@@ -323,15 +349,12 @@ int dev_pm_qos_add_notifier(struct devic
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_register(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
@@ -354,15 +377,12 @@ int dev_pm_qos_remove_notifier(struct de
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the device has been removed */
-	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
-		goto out;
-
-	retval = blocking_notifier_chain_unregister(
-			dev->power.constraints->notifiers,
-			notifier);
+	/* Silently return if the constraints object is not present. */
+	if (dev->power.constraints)
+		retval = blocking_notifier_chain_unregister(
+				dev->power.constraints->notifiers,
+				notifier);
 
-out:
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
 }
Index: linux/include/linux/pm_qos.h
===================================================================
--- linux.orig/include/linux/pm_qos.h
+++ linux/include/linux/pm_qos.h
@@ -7,6 +7,7 @@
 #include <linux/plist.h>
 #include <linux/notifier.h>
 #include <linux/miscdevice.h>
+#include <linux/device.h>
 
 #define PM_QOS_RESERVED 0
 #define PM_QOS_CPU_DMA_LATENCY 1
@@ -77,6 +78,7 @@ int pm_qos_remove_notifier(int pm_qos_cl
 int pm_qos_request_active(struct pm_qos_request *req);
 s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
+s32 dev_pm_qos_read_value(struct device *dev);
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -117,6 +119,8 @@ static inline int pm_qos_request_active(
 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 			{ return 0; }
 
+static inline s32 dev_pm_qos_read_value(struct device *dev)
+			{ return 0; }
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
 					 s32 value)
@@ -139,9 +143,13 @@ static inline int dev_pm_qos_remove_glob
 					struct notifier_block *notifier)
 			{ return 0; }
 static inline void dev_pm_qos_constraints_init(struct device *dev)
-			{ return; }
+{
+	dev->power.power_state = PMSG_ON;
+}
 static inline void dev_pm_qos_constraints_destroy(struct device *dev)
-			{ return; }
+{
+	dev->power.power_state = PMSG_INVALID;
+}
 #endif
 
 #endif
Index: linux/drivers/base/power/main.c
===================================================================
--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -22,7 +22,6 @@
 #include <linux/mutex.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
-#include <linux/pm_qos.h>
 #include <linux/resume-trace.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
@@ -67,6 +66,7 @@ void device_pm_init(struct device *dev)
 	spin_lock_init(&dev->power.lock);
 	pm_runtime_init(dev);
 	INIT_LIST_HEAD(&dev->power.entry);
+	dev->power.power_state = PMSG_INVALID;
 }
 
 /**
@@ -98,8 +98,8 @@ void device_pm_add(struct device *dev)
 		dev_warn(dev, "parent %s should not be sleeping\n",
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
-	mutex_unlock(&dpm_list_mtx);
 	dev_pm_qos_constraints_init(dev);
+	mutex_unlock(&dpm_list_mtx);
 }
 
 /**
@@ -110,9 +110,9 @@ void device_pm_remove(struct device *dev
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
-	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
+	dev_pm_qos_constraints_destroy(dev);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
 	device_wakeup_disable(dev);
Index: linux/include/linux/pm.h
===================================================================
--- linux.orig/include/linux/pm.h
+++ linux/include/linux/pm.h
@@ -326,6 +326,7 @@ extern struct dev_pm_ops generic_subsys_
  *			requested by a driver.
  */
 
+#define PM_EVENT_INVALID	(-1)
 #define PM_EVENT_ON		0x0000
 #define PM_EVENT_FREEZE 	0x0001
 #define PM_EVENT_SUSPEND	0x0002
@@ -346,6 +347,7 @@ extern struct dev_pm_ops generic_subsys_
 #define PM_EVENT_AUTO_SUSPEND	(PM_EVENT_AUTO | PM_EVENT_SUSPEND)
 #define PM_EVENT_AUTO_RESUME	(PM_EVENT_AUTO | PM_EVENT_RESUME)
 
+#define PMSG_INVALID	((struct pm_message){ .event = PM_EVENT_INVALID, })
 #define PMSG_ON		((struct pm_message){ .event = PM_EVENT_ON, })
 #define PMSG_FREEZE	((struct pm_message){ .event = PM_EVENT_FREEZE, })
 #define PMSG_QUIESCE	((struct pm_message){ .event = PM_EVENT_QUIESCE, })
@@ -421,13 +423,6 @@ enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
-/* Per-device PM QoS constraints data struct state */
-enum dev_pm_qos_state {
-	DEV_PM_QOS_NO_DEVICE,		/* No device present */
-	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
-	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
-};
-
 struct wakeup_source;
 
 struct pm_domain_data {
@@ -489,7 +484,6 @@ struct dev_pm_info {
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
 	struct pm_qos_constraints *constraints;
-	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);
Index: linux/drivers/base/power/power.h
===================================================================
--- linux.orig/drivers/base/power/power.h
+++ linux/drivers/base/power/power.h
@@ -1,3 +1,5 @@
+#include <linux/pm_qos.h>
+
 #ifdef CONFIG_PM_RUNTIME
 
 extern void pm_runtime_init(struct device *dev);
@@ -35,15 +37,21 @@ extern void device_pm_move_last(struct d
 static inline void device_pm_init(struct device *dev)
 {
 	spin_lock_init(&dev->power.lock);
+	dev->power.power_state = PMSG_INVALID;
 	pm_runtime_init(dev);
 }
 
+static inline void device_pm_add(struct device *dev)
+{
+	dev_pm_qos_constraints_init(dev);
+}
+
 static inline void device_pm_remove(struct device *dev)
 {
+	dev_pm_qos_constraints_destroy(dev);
 	pm_runtime_remove(dev);
 }
 
-static inline void device_pm_add(struct device *dev) {}
 static inline void device_pm_move_before(struct device *deva,
 					 struct device *devb) {}
 static inline void device_pm_move_after(struct device *deva,

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

* Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
  2011-09-29 20:33         ` Rafael J. Wysocki
@ 2011-09-30  8:08           ` Jean Pihet
  -1 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-30  8:08 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, Ming Lei

Hi Rafael,

On Thu, Sep 29, 2011 at 10:33 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Thursday, September 29, 2011, Jean Pihet wrote:
>> Hi Rafael,
>>
>> I have a few minor remarks, inlined below.
>
> In the meantime it turned out that the patch caused a build failure
> for CONFIG_PM_SLEEP unset (your original patches won't really work
> in this case too), so I posted a patch to fix it.  However, since
> you had remarks anyway, I've simply folded the fix into the next
> version of the $subject patch, which is appended.
OK to me.

I have 2 remarks though:
- some drivers are using the power_state field in the suspend/resume
operation, IIUC without conflict. Can you confirm?
- the power_state field is scheduled for removal, cf.
Documentation/feature-removal-schedule.txt. That does not look
correct.

...
>> > +static void __dev_pm_qos_constraints_init(struct device *dev)
>> I would change the name or add a comment stating that this is an
>> internal function which reinits the constraints field under the lock.
>
> This function was used only once, so it wasn't really necessary.  I simply
> removed it.
>
> Thanks,
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / QoS: Add function dev_pm_qos_read_value() (v3)
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
>
> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so make it use the
> power.power_state field of struct device for this purpose.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Regards,
Jean

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

* Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
@ 2011-09-30  8:08           ` Jean Pihet
  0 siblings, 0 replies; 93+ messages in thread
From: Jean Pihet @ 2011-09-30  8:08 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, Ming Lei

Hi Rafael,

On Thu, Sep 29, 2011 at 10:33 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> Hi,
>
> On Thursday, September 29, 2011, Jean Pihet wrote:
>> Hi Rafael,
>>
>> I have a few minor remarks, inlined below.
>
> In the meantime it turned out that the patch caused a build failure
> for CONFIG_PM_SLEEP unset (your original patches won't really work
> in this case too), so I posted a patch to fix it.  However, since
> you had remarks anyway, I've simply folded the fix into the next
> version of the $subject patch, which is appended.
OK to me.

I have 2 remarks though:
- some drivers are using the power_state field in the suspend/resume
operation, IIUC without conflict. Can you confirm?
- the power_state field is scheduled for removal, cf.
Documentation/feature-removal-schedule.txt. That does not look
correct.

...
>> > +static void __dev_pm_qos_constraints_init(struct device *dev)
>> I would change the name or add a comment stating that this is an
>> internal function which reinits the constraints field under the lock.
>
> This function was used only once, so it wasn't really necessary.  I simply
> removed it.
>
> Thanks,
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / QoS: Add function dev_pm_qos_read_value() (v3)
>
> To read the current PM QoS value for a given device we need to
> make sure that the device's power.constraints object won't be
> removed while we're doing that.  For this reason, put the
> operation under dev->power.lock and acquire the lock
> around the initialization and removal of power.constraints.
>
> Moreover, since we're using the value of power.constraints to
> determine whether or not the object is present, the
> power.constraints_state field isn't necessary any more and may be
> removed.  However, dev_pm_qos_add_request() needs to check if the
> device is being removed from the system before allocating a new
> PM QoS constraints object for it, so make it use the
> power.power_state field of struct device for this purpose.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Regards,
Jean

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

* Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
  2011-09-30  8:08           ` Jean Pihet
@ 2011-09-30 16:46             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-30 16:46 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, Ming Lei

On Friday, September 30, 2011, Jean Pihet wrote:
> Hi Rafael,
> 
> On Thu, Sep 29, 2011 at 10:33 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Thursday, September 29, 2011, Jean Pihet wrote:
> >> Hi Rafael,
> >>
> >> I have a few minor remarks, inlined below.
> >
> > In the meantime it turned out that the patch caused a build failure
> > for CONFIG_PM_SLEEP unset (your original patches won't really work
> > in this case too), so I posted a patch to fix it.  However, since
> > you had remarks anyway, I've simply folded the fix into the next
> > version of the $subject patch, which is appended.
> OK to me.
> 
> I have 2 remarks though:
> - some drivers are using the power_state field in the suspend/resume
> operation, IIUC without conflict. Can you confirm?

Yes, I can.  There are no conflicts.

> - the power_state field is scheduled for removal, cf.
> Documentation/feature-removal-schedule.txt. That does not look
> correct.

It still is schediled for removal, but we can't remove it right now
anyway because of the users mentioned above.  I'll replace this field
with a one-bit flag when we're ready to drop it (some time in future).

Thanks,
Rafael

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

* Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)
@ 2011-09-30 16:46             ` Rafael J. Wysocki
  0 siblings, 0 replies; 93+ messages in thread
From: Rafael J. Wysocki @ 2011-09-30 16:46 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	Kevin Hilman, Ming Lei

On Friday, September 30, 2011, Jean Pihet wrote:
> Hi Rafael,
> 
> On Thu, Sep 29, 2011 at 10:33 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > Hi,
> >
> > On Thursday, September 29, 2011, Jean Pihet wrote:
> >> Hi Rafael,
> >>
> >> I have a few minor remarks, inlined below.
> >
> > In the meantime it turned out that the patch caused a build failure
> > for CONFIG_PM_SLEEP unset (your original patches won't really work
> > in this case too), so I posted a patch to fix it.  However, since
> > you had remarks anyway, I've simply folded the fix into the next
> > version of the $subject patch, which is appended.
> OK to me.
> 
> I have 2 remarks though:
> - some drivers are using the power_state field in the suspend/resume
> operation, IIUC without conflict. Can you confirm?

Yes, I can.  There are no conflicts.

> - the power_state field is scheduled for removal, cf.
> Documentation/feature-removal-schedule.txt. That does not look
> correct.

It still is schediled for removal, but we can't remove it right now
anyway because of the users mentioned above.  I'll replace this field
with a one-bit flag when we're ready to drop it (some time in future).

Thanks,
Rafael

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

end of thread, other threads:[~2011-09-30 16:46 UTC | newest]

Thread overview: 93+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-30 22:17 [PATCH 0/5] PM: Generic PM domains and device PM QoS Rafael J. Wysocki
2011-08-30 22:17 ` Rafael J. Wysocki
2011-08-30 22:18 ` [PATCH 1/5] PM / Domains: Split device PM domain data into base and need_restore Rafael J. Wysocki
2011-08-30 22:18 ` Rafael J. Wysocki
2011-08-30 22:18   ` Rafael J. Wysocki
2011-08-30 22:20 ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set Rafael J. Wysocki
2011-08-30 22:20   ` Rafael J. Wysocki
2011-09-12  8:26   ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for Ming Lei
2011-09-12  8:26     ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set Ming Lei
2011-09-12 21:52     ` Rafael J. Wysocki
2011-09-12 21:52       ` Rafael J. Wysocki
     [not found]     ` <201109122344.02386.rjw@sisk.pl>
2011-09-13  1:22       ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for Ming Lei
2011-09-13  1:22         ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set Ming Lei
2011-09-13 16:06         ` Rafael J. Wysocki
2011-09-13 16:06           ` Rafael J. Wysocki
2011-09-14  1:12           ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for Ming Lei
2011-09-14  1:12             ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set Ming Lei
2011-09-14 20:45             ` Rafael J. Wysocki
2011-09-14 20:45               ` Rafael J. Wysocki
2011-09-15 10:55               ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for Ming Lei
2011-09-15 10:55                 ` [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set Ming Lei
2011-08-30 22:20 ` Rafael J. Wysocki
2011-08-30 22:21 ` [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value() Rafael J. Wysocki
2011-08-30 22:21 ` Rafael J. Wysocki
2011-08-30 22:21   ` Rafael J. Wysocki
2011-09-01 15:13   ` Jean Pihet
2011-09-01 15:13     ` Jean Pihet
2011-09-01 22:07     ` Rafael J. Wysocki
2011-09-01 22:07     ` Rafael J. Wysocki
2011-09-01 22:07       ` Rafael J. Wysocki
2011-09-02  6:49       ` Jean Pihet
2011-09-02  6:49       ` Jean Pihet
2011-09-02  6:49         ` Jean Pihet
2011-09-02 23:55         ` Rafael J. Wysocki
2011-09-02 23:55         ` Rafael J. Wysocki
2011-09-02 23:55           ` Rafael J. Wysocki
2011-09-03  8:02           ` Rafael J. Wysocki
2011-09-03  8:02             ` Rafael J. Wysocki
2011-09-05  7:51             ` Jean Pihet
2011-09-05  7:51             ` Jean Pihet
2011-09-05  7:51               ` Jean Pihet
2011-09-05 15:30               ` Rafael J. Wysocki
2011-09-05 15:30               ` Rafael J. Wysocki
2011-09-05 15:30                 ` Rafael J. Wysocki
2011-09-03  8:02           ` Rafael J. Wysocki
2011-09-05  7:44           ` Jean Pihet
2011-09-05  7:44           ` Jean Pihet
2011-09-05  7:44             ` Jean Pihet
2011-09-01 15:13   ` Jean Pihet
2011-08-30 22:21 ` [RFC][PATCH 4/5] PM / Domains: Add device stop governor function Rafael J. Wysocki
2011-08-30 22:21 ` Rafael J. Wysocki
2011-08-30 22:21   ` Rafael J. Wysocki
2011-08-30 22:22 ` [RFC][PATCH 5/5] PM / Domains: Add default power off " Rafael J. Wysocki
2011-08-30 22:22 ` Rafael J. Wysocki
2011-08-30 22:22   ` Rafael J. Wysocki
2011-09-01 15:17   ` Jean Pihet
2011-09-01 15:17   ` Jean Pihet
2011-09-01 15:17     ` Jean Pihet
2011-09-01 22:11     ` Rafael J. Wysocki
2011-09-01 22:11     ` Rafael J. Wysocki
2011-09-01 22:11       ` Rafael J. Wysocki
2011-09-01 15:28 ` [PATCH 0/5] PM: Generic PM domains and device PM QoS Jean Pihet
2011-09-01 15:28   ` Jean Pihet
2011-09-01 22:14   ` Rafael J. Wysocki
2011-09-01 22:14   ` Rafael J. Wysocki
2011-09-01 22:14     ` Rafael J. Wysocki
2011-09-01 15:28 ` Jean Pihet
2011-09-24 21:23 ` [PATCH 0/3] PM: Runtime PM and device PM QoS refinements Rafael J. Wysocki
2011-09-24 21:23   ` Rafael J. Wysocki
2011-09-24 21:24   ` [PATCH 1/3] PM / Domains: Split device PM domain data into base and need_restore Rafael J. Wysocki
2011-09-24 21:24     ` Rafael J. Wysocki
2011-09-24 21:25   ` [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set Rafael J. Wysocki
2011-09-24 21:25     ` Rafael J. Wysocki
2011-09-25  8:24     ` [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for Ming Lei
2011-09-25  8:24       ` [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set Ming Lei
2011-09-26 23:59     ` Kevin Hilman
2011-09-26 23:59       ` Kevin Hilman
2011-09-27 17:16       ` Rafael J. Wysocki
2011-09-27 17:16         ` Rafael J. Wysocki
2011-09-27 19:50         ` Rafael J. Wysocki
2011-09-27 19:50           ` Rafael J. Wysocki
2011-09-29  0:17           ` Kevin Hilman
2011-09-29  0:17             ` Kevin Hilman
2011-09-24 21:26   ` [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2) Rafael J. Wysocki
2011-09-24 21:26     ` Rafael J. Wysocki
2011-09-29  8:11     ` Jean Pihet
2011-09-29  8:11       ` Jean Pihet
2011-09-29 20:33       ` Rafael J. Wysocki
2011-09-29 20:33         ` Rafael J. Wysocki
2011-09-30  8:08         ` Jean Pihet
2011-09-30  8:08           ` Jean Pihet
2011-09-30 16:46           ` Rafael J. Wysocki
2011-09-30 16:46             ` Rafael J. Wysocki

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.