PM: Generic PM domains and device PM QoS
Subsystems (9)
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
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--;
PATCH 1/5 PM / Domains: Split device PM domain data into base and need_restore
In reply to Rafael J. WysockiFrom: 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
@@ -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
@@ -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
@@ -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(structmutex_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(stmutex_lock (&genpd->lock); - pdd->need_restore = false; + gpd_data->need_restore = false; }/**
@@ -493,7 +495,7 @@ static int pm_genpd_runtime_resume(strucmutex_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--;
PATCH 2/5 PM / Runtime: Do not run callbacks under lock for power.irq_safe set
In reply to Rafael J. WysockiFrom: 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;
}
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()andrpm_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 thatrpm_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.
Signed-off-by Rafael J. Wysocki <[email protected]>---
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
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()andrpm_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 thatrpm_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_suspendorrpm_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_safeflag 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]
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()andrpm_resume()routines execute subsystem or PMdomain callbacks under power.lock if power.irq_safe is set for the
given device. This is inconsistent with that
rpm_idle()does aftercommit 02b2677 (PM / Runtime: Allow
_put_sync()frominterrupts-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_suspendorrpm_resume, then tasks schedulewill 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_safeflag is set.The patch doesn't cause
rpm_suspend()andrpm_resume()to turn interruptson if
irq_safeis set, it only causes them to release the lock (temporarily).
thanks,
Signature · 1 line
Ming Lei
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()andrpm_resume()routines execute subsystem or PMdomain callbacks under power.lock if power.irq_safe is set for the
given device. This is inconsistent with that
rpm_idle()does aftercommit 02b2677 (PM / Runtime: Allow
_put_sync()fromQuoted 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_suspendorrpm_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_suspendorrpm_resumeand 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
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_suspendorrpm_resume, then tasks schedulewill 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_suspendorrpm_resumeand 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 CPU1pm_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
@@ -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
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_suspendorrpm_resume, then tasks schedulewill 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_suspendorrpm_resumeand 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_safeis set before calling twopm_runtime_suspendbelow
- 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
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_suspendorrpm_resume, then tasks schedulewill 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_suspendorrpm_resumeand 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_safeis set before calling twopm_runtime_suspendbelow
- suppose this patch has been appliedQuoted 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_SUSPENDINGin theirq_safecase, which isn't possible without the patch.Good catch!
I think in that case
rpm_suspend()should just release the lock, runcpu_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:
Message will be retried for 1 more day(s)
thanks,
Signature · 1 line
Ming Lei
PATCH 2/5 PM / Runtime: Do not run callbacks under lock for power.irq_safe set
In reply to Rafael J. WysockiFrom: 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
@@ -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 ; }
PATCH 3/5 PM / QoS: Add function dev_pm_qos_read_value()
In reply to Rafael J. WysockiFrom: 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);
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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_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
Signed-off-by Rafael J. Wysocki <[email protected]>---
drivers/base/power/main.c | 4 -
drivers/base/power/qos.c | 167 ++++++++++++++++++++++++++--------------------include/linux/pm.h| 8 --
include/linux/pm_qos.h | 34 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
-1if (!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
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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_pm_lock()as well.Ok that makes sense.
Theconstraints_statefield can be replaced by a combination ofdev->power.constraintsandlist_empty(&dev->power.entry), which makes the code more compact and less redundant.Quoted 14 lines
Signed-off-by Rafael J. Wysocki <[email protected]>--- 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
-ENODEVin 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 devicereturn -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
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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_pm_lock()as well.Ok that makes sense.
Theconstraints_statefield can be replaced by a combination ofdev->power.constraintsandlist_empty(&dev->power.entry), which makes the code more compact and less redundant.Quoted 13 lines
Signed-off-by Rafael J. Wysocki <[email protected]>---
drivers/base/power/main.c | 4 -
drivers/base/power/qos.c | 167 ++++++++++++++++++++++++++--------------------include/linux/pm.h| 8 --
include/linux/pm_qos.h | 34 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
-ENODEVin 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 removedReview 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
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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_pm_lock()as well.Ok that makes sense.
Theconstraints_statefield can be replaced by a combination ofdev->power.constraintsandlist_empty(&dev->power.entry), which makes the code more compact and less redundant.Quoted 14 lines
Signed-off-by Rafael J. Wysocki <[email protected]>--- 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
-ENODEVin 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 devicereturn -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
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 removedReview
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
@@ -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 devicereturn -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 devmutex_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 devmutex_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 devicmutex_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 demutex_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
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_clint 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
@@ -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
@@ -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);
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 toadd_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 functiondev_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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_pm_lock()as well.Signed-off-by Rafael J. Wysocki <[email protected]>---
drivers/base/power/main.c | 4 -
drivers/base/power/qos.c | 170 ++++++++++++++++++++++++++--------------------include/linux/pm.h| 8 --
include/linux/pm_qos.h | 34 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.cdrivers/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.hinclude/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.cdrivers/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.hinclude/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 { #endifstruct
pm_subsys_data*subsys_data; /* Owned by the subsystem. */ structpm_qos_constraints*constraints;
- enumdev_pm_qos_stateconstraints_state;
};
Quoted 2 lines
extern void
update_pm_runtime_accounting(struct device *dev);
Thanks,
Jean
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
@@ -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 devicereturn -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 devmutex_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 devmutex_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 devicmutex_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 demutex_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
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_clint 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
@@ -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
@@ -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);
Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
In reply to Jean PihetQuoted 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
@@ -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 devicereturn -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 devmutex_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 devmutex_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 devicmutex_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 demutex_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
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_clint 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
@@ -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
@@ -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);
Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
In reply to Rafael J. WysockiHi,
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 toadd_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 functiondev_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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_pm_lock()as well.Signed-off-by Rafael J. Wysocki <[email protected]>---
drivers/base/power/main.c | 4 -
drivers/base/power/qos.c | 170 ++++++++++++++++++++++++++--------------------include/linux/pm.h| 8 --
include/linux/pm_qos.h | 34 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 { #endifstruct
pm_subsys_data*subsys_data; /* Owned by the subsystem. */ structpm_qos_constraints*constraints;
- enumdev_pm_qos_stateconstraints_state;
};
Quoted 2 lines
extern void
update_pm_runtime_accounting(struct device *dev);
Thanks,
Jean
Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
In reply to Rafael J. WysockiHi,
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 removedReview
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
@@ -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 devicereturn -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 devmutex_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 devmutex_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 devicmutex_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 demutex_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
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_clint 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
@@ -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
@@ -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);
Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
In reply to Rafael J. WysockiHi 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
*/
intdev_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 removedReview 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
Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
In reply to Rafael J. WysockiHi 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
*/
intdev_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 removedReview 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
Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
In reply to Jean PihetQuoted 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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_pm_lock()as well.Ok that makes sense.
Theconstraints_statefield can be replaced by a combination ofdev->power.constraintsandlist_empty(&dev->power.entry), which makes the code more compact and less redundant.Quoted 14 lines
Signed-off-by Rafael J. Wysocki <[email protected]>--- 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
-ENODEVin 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 devicereturn -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
Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
In reply to Rafael J. WysockiQuoted 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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_pm_lock()as well.Ok that makes sense.
Theconstraints_statefield can be replaced by a combination ofdev->power.constraintsandlist_empty(&dev->power.entry), which makes the code more compact and less redundant.Quoted 14 lines
Signed-off-by Rafael J. Wysocki <[email protected]>---
drivers/base/power/main.c | 4 -
drivers/base/power/qos.c | 167 ++++++++++++++++++++++++++--------------------include/linux/pm.h| 8 --
include/linux/pm_qos.h | 34 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
-ENODEVin 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
Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
In reply to Jean PihetHi,
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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_pm_lock()as well.Ok that makes sense.
Theconstraints_statefield can be replaced by a combination ofdev->power.constraintsandlist_empty(&dev->power.entry), which makes the code more compact and less redundant.Quoted 14 lines
Signed-off-by Rafael J. Wysocki <[email protected]>--- 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
-ENODEVin 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 devicereturn -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
Re: [PATCH 3/5] PM / QoS: Add function dev_pm_qos_read_value()
In reply to Rafael J. WysockiHi 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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_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
Signed-off-by Rafael J. Wysocki <[email protected]>---
drivers/base/power/main.c | 4 -
drivers/base/power/qos.c | 167 ++++++++++++++++++++++++++--------------------include/linux/pm.h| 8 --
include/linux/pm_qos.h | 34 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
-1if (!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
PATCH 3/5 PM / QoS: Add function dev_pm_qos_read_value()
In reply to Rafael J. WysockiFrom: 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
@@ -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 devicereturn -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 devmutex_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 devmutex_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 devicmutex_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 demutex_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
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_clint 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
@@ -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
@@ -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);
RFC PATCH 4/5 PM / Domains: Add device stop governor function
In reply to Rafael J. WysockiFrom: 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
@@ -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
@@ -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(strumight_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(strucmutex_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
@@ -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
@@ -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
@@ -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;
RFC PATCH 4/5 PM / Domains: Add device stop governor function
In reply to Rafael J. WysockiFrom: 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
@@ -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
@@ -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(strumight_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(strucmutex_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
@@ -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
@@ -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
@@ -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;
RFC PATCH 5/5 PM / Domains: Add default power off governor function
In reply to Rafael J. WysockiFrom: 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
@@ -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
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
-0s64 break_even_ns; };
...
Regards,
Jean
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
Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
In reply to Jean PihetHi,
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
Re: [RFC][PATCH 5/5] PM / Domains: Add default power off governor function
In reply to Rafael J. WysockiRafael,
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
-0s64 break_even_ns; };
...
Regards,
Jean
RFC PATCH 5/5 PM / Domains: Add default power off governor function
In reply to Rafael J. WysockiFrom: 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
@@ -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
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_restorefield 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
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_deactivateinarch/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
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_deactivateinarch/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
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_restorefield 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
cover PM: Runtime PM and device PM QoS refinements
In reply to Rafael J. WysockiHi,
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
(oneneed_restorefield 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
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--;
PATCH 2/3 PM / Runtime: Don't run callbacks under lock for power.irq_safe set
In reply to Rafael J. WysockiFrom: 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,
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()andrpm_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 thatrpm_idle()does after commit 02b2677 (PM / Runtime: Allow_put_sync()from interrupts-disabled context) and is problematic for subsystems and PM domains wanting to use power.lock for synchronization in their runtime PM callbacks.Signed-off-by Rafael J. Wysocki <[email protected]>
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
Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
In reply to Rafael J. WysockiQuoted 11 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
The
rpm_suspend()andrpm_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 thatrpm_idle()does after commit 02b2677 (PM / Runtime: Allow_put_sync()from interrupts-disabled context) and is problematic for subsystems and PM domains wanting to use power.lock for synchronization in their runtime PM callbacks.Signed-off-by Rafael J. Wysocki <[email protected]>
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
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()andrpm_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 thatrpm_idle()does after commit 02b2677 (PM / Runtime: Allow_put_sync()from interrupts-disabled context) and is problematic for subsystems and PM domains wanting to use power.lock for synchronization in their runtime PM callbacks.Signed-off-by Rafael J. Wysocki <[email protected]>The part described here looks right, and is much better for consistency.
Reviewed-by Kevin Hilman <[email protected]>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
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()andrpm_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 thatrpm_idle()does after commit 02b2677 (PM / Runtime: Allow_put_sync()from interrupts-disabled context) and is problematic for subsystems and PM domains wanting to use power.lock for synchronization in their runtime PM callbacks.Signed-off-by Rafael J. Wysocki <[email protected]>The part described here looks right, and is much better for consistency.
Reviewed-by Kevin Hilman <[email protected]>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 ifdev->power.irq_safeis set (previously, it would block on the dev's power.lock) and it may seedev->power.runtime_status==RPM_RESUMINGordev->power.runtime_status==RPM_SUSPENDING, while previously, it wouldn't reach the relevant code. Thus we have to modify that code to take thedev->power.irq_safecase 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
@@ -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,
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()andrpm_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 thatrpm_idle()does after commit 02b2677 (PM / Runtime: Allow_put_sync()from interrupts-disabled context) and is problematic for subsystems and PM domains wanting to use power.lock for synchronization in their runtime PM callbacks.Signed-off-by Rafael J. Wysocki <[email protected]>The part described here looks right, and is much better for consistency.
Reviewed-by Kevin Hilman <[email protected]>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 ifdev->power.irq_safeis set (previously, it would block on the dev's power.lock) and it may seedev->power.runtime_status=RPM_RESUMINGordev->power.runtime_status=RPM_SUSPENDING, while previously, it wouldn't reach the relevant code. Thus we have to modify that code to take thedev->power.irq_safecase 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 setThe
rpm_suspend()andrpm_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 thatrpm_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_SUSPENDINGorRPM_RESUMINGto be modified too, to take the power.irq_safe set case into account (that code wasn't reachable before with power.irq_safe set, because it's executed with the device's power.lock held).Signed-off-by Rafael J. Wysocki <[email protected]>Reviewed-by Ming Lei <[email protected]>Reviewed-by Kevin Hilman <[email protected]>--- 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.cdrivers/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/
PATCH 3/3 PM / QoS: Add function dev_pm_qos_read_value() (v2)
In reply to Rafael J. WysockiFrom: 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
@@ -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 devicereturn -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 devmutex_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 devmutex_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 devicmutex_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 demutex_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
@@ -77,6 +77,7 @@ int pm_qos_remove_notifier(int pm_qos_clint 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
@@ -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
@@ -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);
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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_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
Signed-off-by Rafael J. Wysocki <[email protected]>
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 | 34 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
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.lockand 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 usedevice_pm_lock()and the device PM QoS initialization and destruction should be done underdevice_pm_lock()as well.It might be good to mention
dev_pm_qos_mtxwhich is used internally by the per-device PM QoS code.Quoted 2 lines
Signed-off-by Rafael J. Wysocki <[email protected]>Other then the minor remarks:
Acked-by Jean Pihet <[email protected]>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
@@ -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 devicereturn -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 devmutex_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 devmutex_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 devicmutex_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 demutex_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
@@ -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_clint 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
@@ -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
@@ -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
@@ -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 dstatic 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,
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_SLEEPunset (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 functiondev_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.lockand 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
Signed-off-by Rafael J. Wysocki <[email protected]>
Regards,
Jean
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_SLEEPunset (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 thepower_statefield in the suspend/resume operation, IIUC without conflict. Can you confirm?
Yes, I can. There are no conflicts.
Quoted 3 lines
- the
power_statefield 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