Skip to contentSkip to conversation

PM: Generic PM domains and device PM QoS

PATCH In review Rafael J. Wysocki 4 participants 55 messages Aug 30, 2011 – Sep 30, 2011 1 message not in the archive

Subsystems (9)
lore
Share & tools
lore
#0 lore raw

cover PM: Generic PM domains and device PM QoS

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

#1 lore raw

PATCH 1/5 PM / Domains: Split device PM domain data into base and need_restore

From: Rafael J. Wysocki <[email protected]>

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.

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

#2 lore raw

PATCH 1/5 PM / Domains: Split device PM domain data into base and need_restore

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

---
 drivers/base/power/domain.c |   28 +++++++++++++++++++---------
 include/linux/pm.h          |    3 +--
 include/linux/pm_domain.h   |   10 ++++++++++
 3 files changed, 30 insertions(+), 11 deletions(-)
include/linux/pm.h +1 -2
Index: 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
 };
 
include/linux/pm_domain.h +10 -0
Index: 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);
drivers/base/power/domain.c +19 -9
Index: 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--;
 
#3 lore raw

PATCH 2/5 PM / Runtime: Do not run callbacks under lock for power.irq_safe set

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

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

#4 lore raw

Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set

Hi,

Quoted 10 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 90 lines

callbacks.

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

(quoted diff) +25 -0
@@ -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);

(quoted diff) +2 -8
-
-               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);

(quoted diff) +1 -10
@@ -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;
  }
 
--
Quoted 5 lines

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

thanks,

Signature · 1 line
Ming Lei
#5 lore raw

Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set

Quoted 14 lines · Ming Lei

Hi,

Quoted 10 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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

Quoted 3 lines

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.

Quoted 9 lines

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

A message that is not in the archive (sent off-list or not yet received): [email protected]

#7 lore raw

Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set

Hi,

Quoted 32 lines · Rafael J. Wysocki

Hi,

Quoted 27 lines · Ming Lei

Hi,

Quoted 19 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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

Quoted 7 lines
Quoted 5 lines

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.

Quoted 17 lines
Quoted 16 lines

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

Quoted 6 lines
Quoted 1 line

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,

Signature · 1 line
Ming Lei
#8 lore raw

Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set

Quoted 39 lines · Ming Lei

Hi,

Quoted 36 lines · Rafael J. Wysocki

Hi,

Quoted 31 lines · Ming Lei

Hi,

Quoted 12 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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

Quoted 7 lines

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

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

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

#9 lore raw

Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set

Hi,

Quoted 6 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines

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,

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

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

Quoted 3 lines

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.

drivers/base/power/runtime.c +3 -0
index a6750fc..87c9a37 100644
@@ -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,

Signature · 1 line
Ming Lei
#10 lore raw

Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set

Quoted 23 lines · Ming Lei

Hi,

Quoted 6 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines

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,

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

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

Quoted 12 lines

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

#11 lore raw

Re: [PATCH 2/5] PM / Runtime: Do not run callbacks under lock for power.irq_safe set

Quoted 37 lines · Rafael J. Wysocki
Quoted 23 lines · Ming Lei

Hi,

Quoted 6 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines

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,

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

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

Quoted 12 lines

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

Quoted 8 lines

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:

[email protected]

Message will be retried for 1 more day(s)

thanks,

Signature · 1 line
Ming Lei
#12 lore raw

PATCH 2/5 PM / Runtime: Do not run callbacks under lock for power.irq_safe set

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

---
 drivers/base/power/runtime.c |   50 ++++++++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 22 deletions(-)
drivers/base/power/runtime.c +28 -22
Index: 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;
 }
#13 lore raw

PATCH 3/5 PM / QoS: Add function dev_pm_qos_read_value()

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

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

#14 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

Hi Rafael,

Quoted 7 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 13 lines

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

(quoted diff) +0 -0
@@ -30,15 +30,6 @@

...

Review void dev_pm_qos_constraints_destroy(stru · line 206

Quoted 9 lines
(quoted diff) +2 -2
@@ -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?

Quoted 22 lines
(quoted diff) +2 -10
  */
  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

Review int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
(quoted diff) +7 -3
-        */
-       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?

Quoted 11 lines
(quoted diff) +10 -0
+               } 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);
+               }
+       }
 

Review int dev_pm_qos_add_request(struct device · line 253

Quoted 10 lines
(quoted diff) +2 -1
        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

#15 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

Hi,

Quoted 42 lines · Jean Pihet

Hi Rafael,

Quoted 7 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 14 lines
---
 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
===================================================================

drivers/base/power/qos.c +0 -0
@@ -30,15 +30,6 @@

...

Quoted 1 line
Quoted 9 lines

drivers/base/power/qos.c void dev_pm_qos_constraints_destroy(stru · line 206

Quoted 8 lines
drivers/base/power/qos.c +2 -2
@@ -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.

Quoted 18 lines
Quoted 18 lines
drivers/base/power/qos.c +2 -6
 */
 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);
 
Quoted 16 lines
Quoted 4 lines
drivers/base/power/qos.c +0 -4
-       /*
-        * 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

drivers/base/power/qos.c int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
drivers/base/power/qos.c +7 -3
-        */
-       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

#16 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

Quoted 92 lines · Rafael J. Wysocki

Hi,

Quoted 41 lines · Jean Pihet

Hi Rafael,

Quoted 7 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 13 lines

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

(quoted diff) +0 -0
@@ -30,15 +30,6 @@

...

Quoted 1 line
Quoted 9 lines

Review void dev_pm_qos_constraints_destroy(stru · line 206

Quoted 8 lines
(quoted diff) +2 -2
@@ -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.

Quoted 18 lines
Quoted 18 lines
(quoted diff) +2 -6
  */
  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);
 
Quoted 16 lines
Quoted 4 lines
(quoted diff) +0 -4
-       /*
-        * 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

Review int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
(quoted diff) +7 -3
-        */
-       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?

Quoted 1 line

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.

Quoted 3 lines

Rafael

Thanks,
Jean

#17 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

Quoted 94 lines · Jean Pihet
Quoted 93 lines · Rafael J. Wysocki

Hi,

Quoted 42 lines · Jean Pihet

Hi Rafael,

Quoted 7 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 14 lines
---
 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
===================================================================

drivers/base/power/qos.c +0 -0
@@ -30,15 +30,6 @@

...

Quoted 1 line
Quoted 9 lines

drivers/base/power/qos.c void dev_pm_qos_constraints_destroy(stru · line 206

Quoted 8 lines
drivers/base/power/qos.c +2 -2
@@ -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.

Quoted 34 lines
Quoted 22 lines
drivers/base/power/qos.c +2 -10
 */
 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

drivers/base/power/qos.c int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
drivers/base/power/qos.c +7 -3
-        */
-       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?

Quoted 3 lines
Quoted 1 line

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

#18 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

Hi,

Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Jean Pihet

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <[email protected]> wrote:

...

Quoted 29 lines
Quoted 29 lines
Quoted 29 lines
Quoted 28 lines
Quoted 16 lines
(quoted diff) +2 -10
-       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

Review

Quoted 10 lines
(quoted diff) +7 -3
-        */
-       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?

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

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 <[email protected]>
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.

---
 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(-)
drivers/base/power/qos.c +99 -71
Index: 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;
 }
include/linux/pm_qos.h +3 -0
Index: 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)
drivers/base/power/main.c +2 -2
Index: 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);
include/linux/pm.h +0 -8
Index: 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);
#19 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

Hi,

Quoted 39 lines · Rafael J. Wysocki

Hi,

Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Jean Pihet

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <[email protected]> wrote:

...

Quoted 33 lines
Quoted 32 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines

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

Quoted 4 lines
Quoted 2 lines

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.

Quoted 7 lines
Quoted 3 lines

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.

Quoted 249 lines

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
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.

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

drivers/base/power/qos.c +86 -52
@@ -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);

drivers/base/power/qos.c int dev_pm_qos_update_request(struct dev · line 295

Quoted 10 lines
drivers/base/power/qos.c +2 -2
 
-       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?

Quoted 122 lines
drivers/base/power/qos.c +11 -17
        }
 
        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

include/linux/pm_qos.h +3 -0
@@ -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

drivers/base/power/main.c +2 -2
@@ -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

include/linux/pm.h +0 -7
@@ -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;
 };

Quoted 2 lines

 extern void update_pm_runtime_accounting(struct device *dev);

Thanks,
Jean

#20 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

Quoted 51 lines · Jean Pihet

Hi,

Quoted 39 lines · Rafael J. Wysocki

Hi,

Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Jean Pihet

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <[email protected]> wrote:

...

Quoted 33 lines
Quoted 32 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
(quoted diff) +9 -13
-       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;
Quoted 4 lines
Quoted 2 lines

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.

Quoted 7 lines
Quoted 3 lines

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.

...

Quoted 11 lines

Review

Quoted 10 lines
(quoted diff) +2 -2
-       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.

Quoted 1 line

Is the kerneldoc still OK?

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

Updated patch follows.

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
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.

---
 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(-)
drivers/base/power/qos.c +98 -70
Index: 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;
 }
include/linux/pm_qos.h +3 -0
Index: 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)
drivers/base/power/main.c +2 -2
Index: 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);
include/linux/pm.h +0 -8
Index: 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);
#21 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

In reply to Jean Pihet
Quoted 51 lines · Jean Pihet

Hi,

Quoted 39 lines · Rafael J. Wysocki

Hi,

Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Jean Pihet

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <[email protected]> wrote:

...

Quoted 33 lines
Quoted 32 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
(quoted diff) +9 -13
-       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;
Quoted 4 lines
Quoted 2 lines

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.

Quoted 7 lines
Quoted 3 lines

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.

...

Quoted 11 lines

Review

Quoted 10 lines
(quoted diff) +2 -2
-       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.

Quoted 1 line

Is the kerneldoc still OK?

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

Updated patch follows.

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
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.

---
 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(-)
drivers/base/power/qos.c +98 -70
Index: 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;
 }
include/linux/pm_qos.h +3 -0
Index: 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)
drivers/base/power/main.c +2 -2
Index: 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);
include/linux/pm.h +0 -8
Index: 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);
#22 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

In reply to Rafael J. Wysocki

Hi,

Quoted 39 lines · Rafael J. Wysocki

Hi,

Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Jean Pihet

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <[email protected]> wrote:

...

Quoted 33 lines
Quoted 32 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines

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

Quoted 4 lines
Quoted 2 lines

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.

Quoted 7 lines
Quoted 3 lines

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.

Quoted 250 lines

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
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.

---
 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
===================================================================

drivers/base/power/qos.c +86 -52
@@ -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);

drivers/base/power/qos.c int dev_pm_qos_update_request(struct dev · line 295

Quoted 10 lines
drivers/base/power/qos.c +2 -2
 
-       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?

Quoted 125 lines
drivers/base/power/qos.c +11 -17
        }
 
        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
===================================================================

include/linux/pm_qos.h +3 -0
@@ -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
===================================================================

drivers/base/power/main.c +2 -2
@@ -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
===================================================================

include/linux/pm.h +0 -7
@@ -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;
 };

Quoted 2 lines

 extern void update_pm_runtime_accounting(struct device *dev);

Thanks,
Jean

#23 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

In reply to Rafael J. Wysocki

Hi,

Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Jean Pihet

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <[email protected]> wrote:

...

Quoted 29 lines
Quoted 29 lines
Quoted 29 lines
Quoted 28 lines
Quoted 16 lines
(quoted diff) +2 -10
-       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

Review

Quoted 10 lines
(quoted diff) +7 -3
-        */
-       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?

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

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 <[email protected]>
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.

---
 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(-)
drivers/base/power/qos.c +99 -71
Index: 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;
 }
include/linux/pm_qos.h +3 -0
Index: 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)
drivers/base/power/main.c +2 -2
Index: 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);
include/linux/pm.h +0 -8
Index: 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);
#24 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

In reply to Rafael J. Wysocki

Hi Rafael,

Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Jean Pihet

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <[email protected]> wrote:

...

Quoted 35 lines
Quoted 35 lines
Quoted 35 lines
Quoted 34 lines
Quoted 22 lines

 */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, s32 value)

(quoted diff) +2 -10
@@ -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

Review int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
(quoted diff) +7 -3
-        */
-       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?

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

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.

Quoted 4 lines

Thanks,
Rafael

Thanks,
Jean

#25 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

In reply to Rafael J. Wysocki

Hi Rafael,

Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Jean Pihet

On Fri, Sep 2, 2011 at 12:07 AM, Rafael J. Wysocki <[email protected]> wrote:

...

Quoted 35 lines
Quoted 35 lines
Quoted 35 lines
Quoted 34 lines
Quoted 22 lines

 */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, s32 value)

(quoted diff) +2 -10
@@ -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

Review int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
(quoted diff) +7 -3
-        */
-       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?

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

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.

Quoted 4 lines

Thanks,
Rafael

Thanks,
Jean

#26 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

In reply to Jean Pihet
Quoted 94 lines · Jean Pihet
Quoted 93 lines · Rafael J. Wysocki

Hi,

Quoted 42 lines · Jean Pihet

Hi Rafael,

Quoted 7 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 14 lines
---
 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
===================================================================

drivers/base/power/qos.c +0 -0
@@ -30,15 +30,6 @@

...

Quoted 1 line
Quoted 9 lines

drivers/base/power/qos.c void dev_pm_qos_constraints_destroy(stru · line 206

Quoted 8 lines
drivers/base/power/qos.c +2 -2
@@ -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.

Quoted 34 lines
Quoted 22 lines
drivers/base/power/qos.c +2 -10
 */
 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

drivers/base/power/qos.c int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
drivers/base/power/qos.c +7 -3
-        */
-       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?

Quoted 3 lines
Quoted 1 line

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

#27 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

In reply to Rafael J. Wysocki
Quoted 93 lines · Rafael J. Wysocki

Hi,

Quoted 42 lines · Jean Pihet

Hi Rafael,

Quoted 7 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 14 lines

---
 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
===================================================================

drivers/base/power/qos.c +0 -0
@@ -30,15 +30,6 @@

...

Quoted 1 line
Quoted 9 lines

drivers/base/power/qos.c void dev_pm_qos_constraints_destroy(stru · line 206

Quoted 8 lines
drivers/base/power/qos.c +2 -2
@@ -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.

Quoted 18 lines
Quoted 18 lines
drivers/base/power/qos.c +2 -6
  */
  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);
 
Quoted 16 lines
Quoted 4 lines
drivers/base/power/qos.c +0 -4
-       /*
-        * 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

drivers/base/power/qos.c int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
drivers/base/power/qos.c +7 -3
-        */
-       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?

Quoted 1 line

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.

Quoted 3 lines

Rafael

Thanks,
Jean

#28 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

In reply to Jean Pihet

Hi,

Quoted 42 lines · Jean Pihet

Hi Rafael,

Quoted 7 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 14 lines
---
 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
===================================================================

drivers/base/power/qos.c +0 -0
@@ -30,15 +30,6 @@

...

Quoted 1 line
Quoted 9 lines

drivers/base/power/qos.c void dev_pm_qos_constraints_destroy(stru · line 206

Quoted 8 lines
drivers/base/power/qos.c +2 -2
@@ -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.

Quoted 18 lines
Quoted 18 lines
drivers/base/power/qos.c +2 -6
 */
 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);
 
Quoted 16 lines
Quoted 4 lines
drivers/base/power/qos.c +0 -4
-       /*
-        * 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

drivers/base/power/qos.c int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
drivers/base/power/qos.c +7 -3
-        */
-       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

#29 lore raw

Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()

In reply to Rafael J. Wysocki

Hi Rafael,

Quoted 7 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 14 lines

---
 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
===================================================================

drivers/base/power/qos.c +0 -0
@@ -30,15 +30,6 @@

...

drivers/base/power/qos.c void dev_pm_qos_constraints_destroy(stru · line 206

Quoted 9 lines
drivers/base/power/qos.c +2 -2
@@ -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?

Quoted 22 lines
drivers/base/power/qos.c +2 -10
  */
  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

drivers/base/power/qos.c int dev_pm_qos_add_request(struct device · line 233

Quoted 10 lines
drivers/base/power/qos.c +7 -3
-        */
-       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?

Quoted 11 lines
drivers/base/power/qos.c +10 -0
+               } 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);
+               }
+       }
 

drivers/base/power/qos.c int dev_pm_qos_add_request(struct device · line 253

Quoted 10 lines
drivers/base/power/qos.c +2 -1
        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

#30 lore raw

PATCH 3/5 PM / QoS: Add function dev_pm_qos_read_value()

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

---
 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(-)
drivers/base/power/qos.c +96 -71
Index: 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;
 }
include/linux/pm_qos.h +3 -0
Index: 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)
drivers/base/power/main.c +2 -2
Index: 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);
include/linux/pm.h +0 -8
Index: 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);
#31 lore raw

RFC PATCH 4/5 PM / Domains: Add device stop governor function

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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(-)
include/linux/pm_domain.h +46 -6
Index: 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;
drivers/base/power/domain.c +27 -8
Index: 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;
drivers/base/power/Makefile +1 -1
Index: 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
drivers/base/power/domain_governor.c new file +40 -0
Index: 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 <[email protected]>, 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,
+};
arch/arm/mach-shmobile/pm-sh7372.c +1 -1
Index: 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;
#32 lore raw

RFC PATCH 4/5 PM / Domains: Add device stop governor function

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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(-)
include/linux/pm_domain.h +46 -6
Index: 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;
drivers/base/power/domain.c +27 -8
Index: 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;
drivers/base/power/Makefile +1 -1
Index: 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
drivers/base/power/domain_governor.c new file +40 -0
Index: 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 <[email protected]>, 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,
+};
arch/arm/mach-shmobile/pm-sh7372.c +1 -1
Index: 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;
#33 lore raw

RFC PATCH 5/5 PM / Domains: Add default power off governor function

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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(+)
include/linux/pm_domain.h +7 -0
Index: 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;
 };
 
drivers/base/power/domain_governor.c +96 -0

102 lines not on this page · raw message

#34 lore raw

Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function

Rafael,

Quoted 15 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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

include/linux/pm_domain.h struct generic_pm_domain { · line 55

Quoted 7 lines
include/linux/pm_domain.h +4 -0
@@ -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?

include/linux/pm_domain.h struct gpd_link { · line 73

Quoted 10 lines
include/linux/pm_domain.h +3 -0
  };
 
  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.

include/linux/pm_domain.h struct gpd_link { · line 76

Quoted 3 lines
include/linux/pm_domain.h +0 -0
        s64 break_even_ns;
  };
 

...

Regards,
Jean

#35 lore raw

Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function

Hi,

Quoted 14 lines · Jean Pihet

Rafael,

Quoted 11 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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(+)
Quoted 13 lines

include/linux/pm_domain.h struct generic_pm_domain { · line 55

Quoted 12 lines

Index: linux/include/linux/pm_domain.h
===================================================================

include/linux/pm_domain.h +4 -0
@@ -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.

Quoted 1 line

Is there a mechanism that dynamically updates the values?

Obviously not at the moment.

Thanks,
Rafael

#36 lore raw

Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function

In reply to Jean Pihet

Hi,

Quoted 14 lines · Jean Pihet

Rafael,

Quoted 11 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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(+)
Quoted 13 lines

include/linux/pm_domain.h struct generic_pm_domain { · line 55

Quoted 12 lines

Index: linux/include/linux/pm_domain.h
===================================================================

include/linux/pm_domain.h +4 -0
@@ -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.

Quoted 1 line

Is there a mechanism that dynamically updates the values?

Obviously not at the moment.

Thanks,
Rafael

#37 lore raw

Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function

In reply to Rafael J. Wysocki

Rafael,

Quoted 16 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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
===================================================================

include/linux/pm_domain.h struct generic_pm_domain { · line 55

Quoted 7 lines
include/linux/pm_domain.h +4 -0
@@ -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?

include/linux/pm_domain.h struct gpd_link { · line 73

Quoted 10 lines
include/linux/pm_domain.h +3 -0
  };
 
  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.

include/linux/pm_domain.h struct gpd_link { · line 76

Quoted 3 lines
include/linux/pm_domain.h +0 -0
        s64 break_even_ns;
  };
 

...

Regards,
Jean

#38 lore raw

RFC PATCH 5/5 PM / Domains: Add default power off governor function

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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(+)
include/linux/pm_domain.h +7 -0
Index: 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;
 };
 
drivers/base/power/domain_governor.c +96 -0

102 lines not on this page · raw message

#39 lore raw
In reply to Rafael J. Wysocki

Rafael,

Quoted 7 lines · Rafael J. Wysocki

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.

Quoted 4 lines

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

Quoted 3 lines

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.

Quoted 7 lines

[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.

Quoted 10 lines

[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

#40 lore raw

Hi,

Quoted 11 lines · Jean Pihet

Rafael,

Quoted 7 lines · Rafael J. Wysocki

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.

Quoted 9 lines
Quoted 4 lines

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

Quoted 3 lines

Their purpose is to illustrate
an idea that I'd like to discuss at the PM miniconference during the LPC.

Quoted 9 lines

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.

Quoted 2 lines

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

#41 lore raw
In reply to Jean Pihet

Hi,

Quoted 11 lines · Jean Pihet

Rafael,

Quoted 7 lines · Rafael J. Wysocki

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.

Quoted 9 lines
Quoted 4 lines

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

Quoted 3 lines

Their purpose is to illustrate
an idea that I'd like to discuss at the PM miniconference during the LPC.

Quoted 9 lines

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.

Quoted 2 lines

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

#42 lore raw
In reply to Rafael J. Wysocki

Rafael,

Quoted 7 lines · Rafael J. Wysocki

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.

Quoted 4 lines

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

Quoted 3 lines

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.

Quoted 7 lines

[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.

Quoted 10 lines

[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

#43 lore raw

cover PM: Runtime PM and device PM QoS refinements

In reply to Rafael J. Wysocki

Hi,

Quoted 17 lines · Rafael J. Wysocki

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

Quoted 7 lines

[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

#44 lore raw

PATCH 1/3 PM / Domains: Split device PM domain data into base and need_restore

From: Rafael J. Wysocki <[email protected]>

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.

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

#45 lore raw

PATCH 2/3 PM / Runtime: Don't run callbacks under lock for power.irq_safe set

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

---
 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,

Reviewed-by on v1 2/3
#46 lore raw

Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set

Quoted 11 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 109 lines

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

(quoted diff) +25 -0
@@ -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);

(quoted diff) +2 -8
-
-               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);

(quoted diff) +12 -9
@@ -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);

Review static int rpm_resume(struct device *dev · line 525

Quoted 10 lines
(quoted diff) +7 -0
+
+                       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,

Signature · 1 line
Ming Lei
Reviewed-by on v1 2/3
#47 lore raw

Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set

In reply to Rafael J. Wysocki
Quoted 11 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

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

but...

[...]

Review static int rpm_suspend(struct device *de · line 364

Quoted 13 lines
(quoted diff) +9 -0
@@ -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

#48 lore raw

Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set

Quoted 26 lines · Kevin Hilman
Quoted 11 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

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

but...

[...]

Quoted 4 lines
(quoted diff) +0 -0
@@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
 			goto out;
 		}
 
Quoted 13 lines

Review static int rpm_suspend(struct device *de · line 364

Quoted 9 lines
(quoted diff) +9 -0
+		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.

Quoted 1 line

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

author Reviewed-by Ming Lei Reviewed-by Kevin Hilman
#49 lore raw

Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set

Quoted 51 lines · Rafael J. Wysocki
Quoted 26 lines · Kevin Hilman
Quoted 11 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

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

but...

[...]

Quoted 4 lines
(quoted diff) +0 -0
@@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
 			goto out;
 		}
 
Quoted 13 lines

Review static int rpm_suspend(struct device *de · line 364

Quoted 9 lines
(quoted diff) +9 -0
+		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.

Quoted 6 lines
Quoted 1 line

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 <[email protected]>
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).

---
 drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
 1 file changed, 46 insertions(+), 22 deletions(-)
drivers/base/power/runtime.c +46 -22
Index: 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,
#50 lore raw

Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set

Quoted 52 lines · Rafael J. Wysocki
Quoted 51 lines · Rafael J. Wysocki
Quoted 26 lines · Kevin Hilman
Quoted 11 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

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

but...

[...]

Quoted 4 lines
drivers/base/power/runtime.c +0 -0
@@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
 			goto out;
 		}
 
Quoted 13 lines

drivers/base/power/runtime.c static int rpm_suspend(struct device *de · line 364

Quoted 9 lines
drivers/base/power/runtime.c +9 -0
+		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.

Quoted 8 lines
Quoted 6 lines
Quoted 1 line

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

Quoted 143 lines

---
From: Rafael J. Wysocki <[email protected]>
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).

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

drivers/base/power/runtime.c +46 -23
@@ -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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#51 lore raw

PATCH 3/3 PM / QoS: Add function dev_pm_qos_read_value() (v2)

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

---
 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(-)
drivers/base/power/qos.c +98 -70
Index: 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;
 }
include/linux/pm_qos.h +3 -0
Index: 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)
drivers/base/power/main.c +2 -2
Index: 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);
include/linux/pm.h +0 -8
Index: 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);
Acked-by on v1 3/3
#52 lore raw

Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)

Hi Rafael,

I have a few minor remarks, inlined below.

Quoted 8 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 2 lines

Other then the minor remarks:

Quoted 10 lines

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

...

Quoted 2 lines

+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

#53 lore raw

Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)

Hi,

Quoted 3 lines · Jean Pihet

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.

Quoted 39 lines · Rafael J. Wysocki
Quoted 8 lines

From: Rafael J. Wysocki <[email protected]>

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.

Quoted 8 lines

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.

Quoted 2 lines

Other then the minor remarks:

Quoted 11 lines
---
 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
===================================================================

drivers/base/power/qos.c +0 -0

...

Quoted 1 line
Quoted 3 lines
Quoted 1 line

+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 <[email protected]>
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.

---
 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(-)
drivers/base/power/qos.c +90 -70
Index: 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;
 }
include/linux/pm_qos.h +10 -2
Index: 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
drivers/base/power/main.c +3 -3
Index: 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);
include/linux/pm.h +2 -8
Index: 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);
drivers/base/power/power.h +9 -1
Index: 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,
#54 lore raw

Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)

Hi Rafael,

Quoted 5 lines · Rafael J. Wysocki

Hi,

Quoted 2 lines · Jean Pihet

Hi Rafael,

Quoted 7 lines
Quoted 1 line

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.

...

Quoted 28 lines
Quoted 3 lines
Quoted 1 line

+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 <[email protected]>
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.

Quoted 1 line

Regards,
Jean

#55 lore raw

Re: [PATCH 3/3] PM / QoS: Add function dev_pm_qos_read_value() (v2)

Quoted 15 lines · Jean Pihet

Hi Rafael,

Quoted 5 lines · Rafael J. Wysocki

Hi,

Quoted 2 lines · Jean Pihet

Hi Rafael,

Quoted 7 lines
Quoted 1 line

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.

Quoted 5 lines

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.

Quoted 3 lines

- 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