aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power/domain.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-05-01 15:34:07 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2012-05-05 15:51:58 -0400
commit6ff7bb0d02f82968be13937c03e93b6c090229df (patch)
treeac2605f2f2b1602fddb88c4ba33c647608b594e6 /drivers/base/power/domain.c
parentefa6902501ffc87d69bfb10b8a09b7d6ee222d77 (diff)
PM / Domains: Cache device stop and domain power off governor results, v3
The results of the default device stop and domain power off governor functions for generic PM domains, default_stop_ok() and default_power_down_ok(), depend only on the timing data of devices, which are static, and on their PM QoS constraints. Thus, in theory, these functions only need to carry out their computations, which may be time consuming in general, when it is known that the PM QoS constraint of at least one of the devices in question has changed. Use the PM QoS notifiers of devices to implement that. First, introduce new fields, constraint_changed and max_off_time_changed, into struct gpd_timing_data and struct generic_pm_domain, respectively, and register a PM QoS notifier function when adding a device into a domain that will set those fields to 'true' whenever the device's PM QoS constraint is modified. Second, make default_stop_ok() and default_power_down_ok() use those fields to decide whether or not to carry out their computations from scratch. The device and PM domain hierarchies are taken into account in that and the expense is that the changes of PM QoS constraints of suspended devices will not be taken into account immediately, which isn't guaranteed anyway in general. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/base/power/domain.c')
-rw-r--r--drivers/base/power/domain.c120
1 files changed, 104 insertions, 16 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 6ae5672c35ab..cde5983de6c2 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -11,6 +11,7 @@
11#include <linux/io.h> 11#include <linux/io.h>
12#include <linux/pm_runtime.h> 12#include <linux/pm_runtime.h>
13#include <linux/pm_domain.h> 13#include <linux/pm_domain.h>
14#include <linux/pm_qos.h>
14#include <linux/slab.h> 15#include <linux/slab.h>
15#include <linux/err.h> 16#include <linux/err.h>
16#include <linux/sched.h> 17#include <linux/sched.h>
@@ -38,11 +39,13 @@
38 ktime_t __start = ktime_get(); \ 39 ktime_t __start = ktime_get(); \
39 type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \ 40 type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \
40 s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \ 41 s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \
41 struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev); \ 42 struct gpd_timing_data *__td = &dev_gpd_data(dev)->td; \
42 if (__elapsed > __gpd_data->td.field) { \ 43 if (!__retval && __elapsed > __td->field) { \
43 __gpd_data->td.field = __elapsed; \ 44 __td->field = __elapsed; \
44 dev_warn(dev, name " latency exceeded, new value %lld ns\n", \ 45 dev_warn(dev, name " latency exceeded, new value %lld ns\n", \
45 __elapsed); \ 46 __elapsed); \
47 genpd->max_off_time_changed = true; \
48 __td->constraint_changed = true; \
46 } \ 49 } \
47 __retval; \ 50 __retval; \
48}) 51})
@@ -211,6 +214,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd)
211 elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 214 elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
212 if (elapsed_ns > genpd->power_on_latency_ns) { 215 if (elapsed_ns > genpd->power_on_latency_ns) {
213 genpd->power_on_latency_ns = elapsed_ns; 216 genpd->power_on_latency_ns = elapsed_ns;
217 genpd->max_off_time_changed = true;
214 if (genpd->name) 218 if (genpd->name)
215 pr_warning("%s: Power-on latency exceeded, " 219 pr_warning("%s: Power-on latency exceeded, "
216 "new value %lld ns\n", genpd->name, 220 "new value %lld ns\n", genpd->name,
@@ -247,6 +251,53 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)
247 251
248#ifdef CONFIG_PM_RUNTIME 252#ifdef CONFIG_PM_RUNTIME
249 253
254static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
255 unsigned long val, void *ptr)
256{
257 struct generic_pm_domain_data *gpd_data;
258 struct device *dev;
259
260 gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
261
262 mutex_lock(&gpd_data->lock);
263 dev = gpd_data->base.dev;
264 if (!dev) {
265 mutex_unlock(&gpd_data->lock);
266 return NOTIFY_DONE;
267 }
268 mutex_unlock(&gpd_data->lock);
269
270 for (;;) {
271 struct generic_pm_domain *genpd;
272 struct pm_domain_data *pdd;
273
274 spin_lock_irq(&dev->power.lock);
275
276 pdd = dev->power.subsys_data ?
277 dev->power.subsys_data->domain_data : NULL;
278 if (pdd) {
279 to_gpd_data(pdd)->td.constraint_changed = true;
280 genpd = dev_to_genpd(dev);
281 } else {
282 genpd = ERR_PTR(-ENODATA);
283 }
284
285 spin_unlock_irq(&dev->power.lock);
286
287 if (!IS_ERR(genpd)) {
288 mutex_lock(&genpd->lock);
289 genpd->max_off_time_changed = true;
290 mutex_unlock(&genpd->lock);
291 }
292
293 dev = dev->parent;
294 if (!dev || dev->power.ignore_children)
295 break;
296 }
297
298 return NOTIFY_DONE;
299}
300
250/** 301/**
251 * __pm_genpd_save_device - Save the pre-suspend state of a device. 302 * __pm_genpd_save_device - Save the pre-suspend state of a device.
252 * @pdd: Domain data of the device to save the state of. 303 * @pdd: Domain data of the device to save the state of.
@@ -381,7 +432,6 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
381 return 0; 432 return 0;
382 } 433 }
383 434
384 genpd->max_off_time_ns = -1;
385 if (genpd->gov && genpd->gov->power_down_ok) { 435 if (genpd->gov && genpd->gov->power_down_ok) {
386 if (!genpd->gov->power_down_ok(&genpd->domain)) 436 if (!genpd->gov->power_down_ok(&genpd->domain))
387 return -EAGAIN; 437 return -EAGAIN;
@@ -436,6 +486,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
436 elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 486 elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
437 if (elapsed_ns > genpd->power_off_latency_ns) { 487 if (elapsed_ns > genpd->power_off_latency_ns) {
438 genpd->power_off_latency_ns = elapsed_ns; 488 genpd->power_off_latency_ns = elapsed_ns;
489 genpd->max_off_time_changed = true;
439 if (genpd->name) 490 if (genpd->name)
440 pr_warning("%s: Power-off latency exceeded, " 491 pr_warning("%s: Power-off latency exceeded, "
441 "new value %lld ns\n", genpd->name, 492 "new value %lld ns\n", genpd->name,
@@ -496,7 +547,6 @@ static int pm_genpd_runtime_suspend(struct device *dev)
496 if (dev_gpd_data(dev)->always_on) 547 if (dev_gpd_data(dev)->always_on)
497 return -EBUSY; 548 return -EBUSY;
498 549
499 dev_gpd_data(dev)->td.effective_constraint_ns = -1;
500 stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; 550 stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
501 if (stop_ok && !stop_ok(dev)) 551 if (stop_ok && !stop_ok(dev))
502 return -EBUSY; 552 return -EBUSY;
@@ -601,6 +651,12 @@ void pm_genpd_poweroff_unused(void)
601 651
602#else 652#else
603 653
654static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
655 unsigned long val, void *ptr)
656{
657 return NOTIFY_DONE;
658}
659
604static inline void genpd_power_off_work_fn(struct work_struct *work) {} 660static inline void genpd_power_off_work_fn(struct work_struct *work) {}
605 661
606#define pm_genpd_runtime_suspend NULL 662#define pm_genpd_runtime_suspend NULL
@@ -1197,6 +1253,14 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1197 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1253 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1198 return -EINVAL; 1254 return -EINVAL;
1199 1255
1256 gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
1257 if (!gpd_data)
1258 return -ENOMEM;
1259
1260 mutex_init(&gpd_data->lock);
1261 gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
1262 dev_pm_qos_add_notifier(dev, &gpd_data->nb);
1263
1200 genpd_acquire_lock(genpd); 1264 genpd_acquire_lock(genpd);
1201 1265
1202 if (genpd->status == GPD_STATE_POWER_OFF) { 1266 if (genpd->status == GPD_STATE_POWER_OFF) {
@@ -1215,26 +1279,35 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1215 goto out; 1279 goto out;
1216 } 1280 }
1217 1281
1218 gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
1219 if (!gpd_data) {
1220 ret = -ENOMEM;
1221 goto out;
1222 }
1223
1224 genpd->device_count++; 1282 genpd->device_count++;
1283 genpd->max_off_time_changed = true;
1225 1284
1226 dev->pm_domain = &genpd->domain;
1227 dev_pm_get_subsys_data(dev); 1285 dev_pm_get_subsys_data(dev);
1286
1287 mutex_lock(&gpd_data->lock);
1288 spin_lock_irq(&dev->power.lock);
1289 dev->pm_domain = &genpd->domain;
1228 dev->power.subsys_data->domain_data = &gpd_data->base; 1290 dev->power.subsys_data->domain_data = &gpd_data->base;
1229 gpd_data->base.dev = dev; 1291 gpd_data->base.dev = dev;
1230 gpd_data->need_restore = false;
1231 list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 1292 list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
1293 gpd_data->need_restore = false;
1232 if (td) 1294 if (td)
1233 gpd_data->td = *td; 1295 gpd_data->td = *td;
1234 1296
1297 gpd_data->td.constraint_changed = true;
1298 gpd_data->td.effective_constraint_ns = -1;
1299 spin_unlock_irq(&dev->power.lock);
1300 mutex_unlock(&gpd_data->lock);
1301
1302 genpd_release_lock(genpd);
1303
1304 return 0;
1305
1235 out: 1306 out:
1236 genpd_release_lock(genpd); 1307 genpd_release_lock(genpd);
1237 1308
1309 dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
1310 kfree(gpd_data);
1238 return ret; 1311 return ret;
1239} 1312}
1240 1313
@@ -1278,6 +1351,7 @@ int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
1278int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1351int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1279 struct device *dev) 1352 struct device *dev)
1280{ 1353{
1354 struct generic_pm_domain_data *gpd_data;
1281 struct pm_domain_data *pdd; 1355 struct pm_domain_data *pdd;
1282 int ret = 0; 1356 int ret = 0;
1283 1357
@@ -1295,14 +1369,27 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1295 goto out; 1369 goto out;
1296 } 1370 }
1297 1371
1372 genpd->device_count--;
1373 genpd->max_off_time_changed = true;
1374
1375 spin_lock_irq(&dev->power.lock);
1298 dev->pm_domain = NULL; 1376 dev->pm_domain = NULL;
1299 pdd = dev->power.subsys_data->domain_data; 1377 pdd = dev->power.subsys_data->domain_data;
1300 list_del_init(&pdd->list_node); 1378 list_del_init(&pdd->list_node);
1301 dev->power.subsys_data->domain_data = NULL; 1379 dev->power.subsys_data->domain_data = NULL;
1302 dev_pm_put_subsys_data(dev); 1380 spin_unlock_irq(&dev->power.lock);
1303 kfree(to_gpd_data(pdd));
1304 1381
1305 genpd->device_count--; 1382 gpd_data = to_gpd_data(pdd);
1383 mutex_lock(&gpd_data->lock);
1384 pdd->dev = NULL;
1385 mutex_unlock(&gpd_data->lock);
1386
1387 genpd_release_lock(genpd);
1388
1389 dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
1390 kfree(gpd_data);
1391 dev_pm_put_subsys_data(dev);
1392 return 0;
1306 1393
1307 out: 1394 out:
1308 genpd_release_lock(genpd); 1395 genpd_release_lock(genpd);
@@ -1673,6 +1760,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
1673 genpd->resume_count = 0; 1760 genpd->resume_count = 0;
1674 genpd->device_count = 0; 1761 genpd->device_count = 0;
1675 genpd->max_off_time_ns = -1; 1762 genpd->max_off_time_ns = -1;
1763 genpd->max_off_time_changed = true;
1676 genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1764 genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
1677 genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1765 genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
1678 genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 1766 genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;