PM / Runtime: Add no_callbacks flag

Some devices, such as USB interfaces, cannot be power-managed
independently of their parents, i.e., they cannot be put in low power
while the parent remains at full power. This patch (as1425) creates a
new "no_callbacks" flag, which tells the PM core not to invoke the
runtime-PM callback routines for the such devices but instead to
assume that the callbacks always succeed. In addition, the
non-debugging runtime-PM sysfs attributes for the devices are removed,
since they are pretty much meaningless.

The advantage of this scheme comes not so much from avoiding the
callbacks themselves, but rather from the fact that without the need
for a process context in which to run the callbacks, more work can be
done in interrupt context.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

authored by Alan Stern and committed by Rafael J. Wysocki 7490e442 140a6c94

+149 -8
+37
Documentation/power/runtime_pm.txt
··· 1 Run-time Power Management Framework for I/O Devices 2 3 (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 4 5 1. Introduction 6 ··· 231 interface; it may only be modified with the help of the pm_runtime_allow() 232 and pm_runtime_forbid() helper functions 233 234 All of the above fields are members of the 'power' member of 'struct device'. 235 236 4. Run-time PM Device Helper Functions ··· 354 - unset the power.runtime_auto flag for the device and increase its usage 355 counter (used by the /sys/devices/.../power/control interface to 356 effectively prevent the device from being power managed at run time) 357 358 It is safe to execute the following helper functions from interrupt context: 359 ··· 535 restore, and run-time resume, can achieve this with the help of the 536 UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its 537 last argument to NULL).
··· 1 Run-time Power Management Framework for I/O Devices 2 3 (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 4 + (C) 2010 Alan Stern <stern@rowland.harvard.edu> 5 6 1. Introduction 7 ··· 230 interface; it may only be modified with the help of the pm_runtime_allow() 231 and pm_runtime_forbid() helper functions 232 233 + unsigned int no_callbacks; 234 + - indicates that the device does not use the run-time PM callbacks (see 235 + Section 8); it may be modified only by the pm_runtime_no_callbacks() 236 + helper function 237 + 238 All of the above fields are members of the 'power' member of 'struct device'. 239 240 4. Run-time PM Device Helper Functions ··· 348 - unset the power.runtime_auto flag for the device and increase its usage 349 counter (used by the /sys/devices/.../power/control interface to 350 effectively prevent the device from being power managed at run time) 351 + 352 + void pm_runtime_no_callbacks(struct device *dev); 353 + - set the power.no_callbacks flag for the device and remove the run-time 354 + PM attributes from /sys/devices/.../power (or prevent them from being 355 + added when the device is registered) 356 357 It is safe to execute the following helper functions from interrupt context: 358 ··· 524 restore, and run-time resume, can achieve this with the help of the 525 UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its 526 last argument to NULL). 527 + 528 + 8. "No-Callback" Devices 529 + 530 + Some "devices" are only logical sub-devices of their parent and cannot be 531 + power-managed on their own. (The prototype example is a USB interface. Entire 532 + USB devices can go into low-power mode or send wake-up requests, but neither is 533 + possible for individual interfaces.) The drivers for these devices have no 534 + need of run-time PM callbacks; if the callbacks did exist, ->runtime_suspend() 535 + and ->runtime_resume() would always return 0 without doing anything else and 536 + ->runtime_idle() would always call pm_runtime_suspend(). 537 + 538 + Subsystems can tell the PM core about these devices by calling 539 + pm_runtime_no_callbacks(). This should be done after the device structure is 540 + initialized and before it is registered (although after device registration is 541 + also okay). The routine will set the device's power.no_callbacks flag and 542 + prevent the non-debugging run-time PM sysfs attributes from being created. 543 + 544 + When power.no_callbacks is set, the PM core will not invoke the 545 + ->runtime_idle(), ->runtime_suspend(), or ->runtime_resume() callbacks. 546 + Instead it will assume that suspends and resumes always succeed and that idle 547 + devices should be suspended. 548 + 549 + As a consequence, the PM core will never directly inform the device's subsystem 550 + or driver about run-time power changes. Instead, the driver for the device's 551 + parent must take responsibility for telling the device's driver when the 552 + parent's power state changes.
+1
drivers/base/power/power.h
··· 60 61 extern int dpm_sysfs_add(struct device *); 62 extern void dpm_sysfs_remove(struct device *); 63 64 #else /* CONFIG_PM */ 65
··· 60 61 extern int dpm_sysfs_add(struct device *); 62 extern void dpm_sysfs_remove(struct device *); 63 + extern void rpm_sysfs_remove(struct device *); 64 65 #else /* CONFIG_PM */ 66
+53 -1
drivers/base/power/runtime.c
··· 10 #include <linux/sched.h> 11 #include <linux/pm_runtime.h> 12 #include <linux/jiffies.h> 13 14 static int rpm_resume(struct device *dev, int rpmflags); 15 16 /** 17 * update_pm_runtime_accounting - Update the time accounting of power states ··· 150 /* Pending requests need to be canceled. */ 151 dev->power.request = RPM_REQ_NONE; 152 153 /* Carry out an asynchronous or a synchronous idle notification. */ 154 if (rpmflags & RPM_ASYNC) { 155 dev->power.request = RPM_REQ_IDLE; ··· 262 goto repeat; 263 } 264 265 /* Carry out an asynchronous or a synchronous suspend. */ 266 if (rpmflags & RPM_ASYNC) { 267 dev->power.request = RPM_REQ_SUSPEND; ··· 277 } 278 279 __update_runtime_status(dev, RPM_SUSPENDING); 280 - dev->power.deferred_resume = false; 281 282 if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { 283 spin_unlock_irq(&dev->power.lock); ··· 316 pm_runtime_cancel_pending(dev); 317 } 318 } else { 319 __update_runtime_status(dev, RPM_SUSPENDED); 320 pm_runtime_deactivate_timer(dev); 321 ··· 421 goto repeat; 422 } 423 424 /* Carry out an asynchronous or a synchronous resume. */ 425 if (rpmflags & RPM_ASYNC) { 426 dev->power.request = RPM_REQ_RESUME; ··· 478 goto repeat; 479 } 480 481 __update_runtime_status(dev, RPM_RESUMING); 482 483 if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { ··· 514 __update_runtime_status(dev, RPM_SUSPENDED); 515 pm_runtime_cancel_pending(dev); 516 } else { 517 __update_runtime_status(dev, RPM_ACTIVE); 518 if (parent) 519 atomic_inc(&parent->power.child_count); ··· 986 spin_unlock_irq(&dev->power.lock); 987 } 988 EXPORT_SYMBOL_GPL(pm_runtime_allow); 989 990 /** 991 * pm_runtime_init - Initialize run-time PM fields in given device object.
··· 10 #include <linux/sched.h> 11 #include <linux/pm_runtime.h> 12 #include <linux/jiffies.h> 13 + #include "power.h" 14 15 static int rpm_resume(struct device *dev, int rpmflags); 16 + static int rpm_suspend(struct device *dev, int rpmflags); 17 18 /** 19 * update_pm_runtime_accounting - Update the time accounting of power states ··· 148 /* Pending requests need to be canceled. */ 149 dev->power.request = RPM_REQ_NONE; 150 151 + if (dev->power.no_callbacks) { 152 + /* Assume ->runtime_idle() callback would have suspended. */ 153 + retval = rpm_suspend(dev, rpmflags); 154 + goto out; 155 + } 156 + 157 /* Carry out an asynchronous or a synchronous idle notification. */ 158 if (rpmflags & RPM_ASYNC) { 159 dev->power.request = RPM_REQ_IDLE; ··· 254 goto repeat; 255 } 256 257 + dev->power.deferred_resume = false; 258 + if (dev->power.no_callbacks) 259 + goto no_callback; /* Assume success. */ 260 + 261 /* Carry out an asynchronous or a synchronous suspend. */ 262 if (rpmflags & RPM_ASYNC) { 263 dev->power.request = RPM_REQ_SUSPEND; ··· 265 } 266 267 __update_runtime_status(dev, RPM_SUSPENDING); 268 269 if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { 270 spin_unlock_irq(&dev->power.lock); ··· 305 pm_runtime_cancel_pending(dev); 306 } 307 } else { 308 + no_callback: 309 __update_runtime_status(dev, RPM_SUSPENDED); 310 pm_runtime_deactivate_timer(dev); 311 ··· 409 goto repeat; 410 } 411 412 + /* 413 + * See if we can skip waking up the parent. This is safe only if 414 + * power.no_callbacks is set, because otherwise we don't know whether 415 + * the resume will actually succeed. 416 + */ 417 + if (dev->power.no_callbacks && !parent && dev->parent) { 418 + spin_lock(&dev->parent->power.lock); 419 + if (dev->parent->power.disable_depth > 0 420 + || dev->parent->power.ignore_children 421 + || dev->parent->power.runtime_status == RPM_ACTIVE) { 422 + atomic_inc(&dev->parent->power.child_count); 423 + spin_unlock(&dev->parent->power.lock); 424 + goto no_callback; /* Assume success. */ 425 + } 426 + spin_unlock(&dev->parent->power.lock); 427 + } 428 + 429 /* Carry out an asynchronous or a synchronous resume. */ 430 if (rpmflags & RPM_ASYNC) { 431 dev->power.request = RPM_REQ_RESUME; ··· 449 goto repeat; 450 } 451 452 + if (dev->power.no_callbacks) 453 + goto no_callback; /* Assume success. */ 454 + 455 __update_runtime_status(dev, RPM_RESUMING); 456 457 if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { ··· 482 __update_runtime_status(dev, RPM_SUSPENDED); 483 pm_runtime_cancel_pending(dev); 484 } else { 485 + no_callback: 486 __update_runtime_status(dev, RPM_ACTIVE); 487 if (parent) 488 atomic_inc(&parent->power.child_count); ··· 953 spin_unlock_irq(&dev->power.lock); 954 } 955 EXPORT_SYMBOL_GPL(pm_runtime_allow); 956 + 957 + /** 958 + * pm_runtime_no_callbacks - Ignore run-time PM callbacks for a device. 959 + * @dev: Device to handle. 960 + * 961 + * Set the power.no_callbacks flag, which tells the PM core that this 962 + * device is power-managed through its parent and has no run-time PM 963 + * callbacks of its own. The run-time sysfs attributes will be removed. 964 + * 965 + */ 966 + void pm_runtime_no_callbacks(struct device *dev) 967 + { 968 + spin_lock_irq(&dev->power.lock); 969 + dev->power.no_callbacks = 1; 970 + spin_unlock_irq(&dev->power.lock); 971 + if (device_is_registered(dev)) 972 + rpm_sysfs_remove(dev); 973 + } 974 + EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); 975 976 /** 977 * pm_runtime_init - Initialize run-time PM fields in given device object.
+49 -7
drivers/base/power/sysfs.c
··· 81 static const char enabled[] = "enabled"; 82 static const char disabled[] = "disabled"; 83 84 #ifdef CONFIG_PM_RUNTIME 85 static const char ctrl_auto[] = "auto"; 86 static const char ctrl_on[] = "on"; ··· 393 #endif /* CONFIG_PM_ADVANCED_DEBUG */ 394 395 static struct attribute * power_attrs[] = { 396 - #ifdef CONFIG_PM_RUNTIME 397 - &dev_attr_control.attr, 398 - &dev_attr_runtime_status.attr, 399 - &dev_attr_runtime_suspended_time.attr, 400 - &dev_attr_runtime_active_time.attr, 401 - #endif 402 &dev_attr_wakeup.attr, 403 #ifdef CONFIG_PM_SLEEP 404 &dev_attr_wakeup_count.attr, ··· 406 #ifdef CONFIG_PM_ADVANCED_DEBUG 407 &dev_attr_async.attr, 408 #ifdef CONFIG_PM_RUNTIME 409 &dev_attr_runtime_usage.attr, 410 &dev_attr_runtime_active_kids.attr, 411 &dev_attr_runtime_enabled.attr, ··· 415 NULL, 416 }; 417 static struct attribute_group pm_attr_group = { 418 - .name = "power", 419 .attrs = power_attrs, 420 }; 421 422 int dpm_sysfs_add(struct device * dev) 423 { ··· 470 { 471 sysfs_remove_group(&dev->kobj, &pm_attr_group); 472 }
··· 81 static const char enabled[] = "enabled"; 82 static const char disabled[] = "disabled"; 83 84 + const char power_group_name[] = "power"; 85 + EXPORT_SYMBOL_GPL(power_group_name); 86 + 87 #ifdef CONFIG_PM_RUNTIME 88 static const char ctrl_auto[] = "auto"; 89 static const char ctrl_on[] = "on"; ··· 390 #endif /* CONFIG_PM_ADVANCED_DEBUG */ 391 392 static struct attribute * power_attrs[] = { 393 &dev_attr_wakeup.attr, 394 #ifdef CONFIG_PM_SLEEP 395 &dev_attr_wakeup_count.attr, ··· 409 #ifdef CONFIG_PM_ADVANCED_DEBUG 410 &dev_attr_async.attr, 411 #ifdef CONFIG_PM_RUNTIME 412 + &dev_attr_runtime_status.attr, 413 &dev_attr_runtime_usage.attr, 414 &dev_attr_runtime_active_kids.attr, 415 &dev_attr_runtime_enabled.attr, ··· 417 NULL, 418 }; 419 static struct attribute_group pm_attr_group = { 420 + .name = power_group_name, 421 .attrs = power_attrs, 422 }; 423 + 424 + #ifdef CONFIG_PM_RUNTIME 425 + 426 + static struct attribute *runtime_attrs[] = { 427 + #ifndef CONFIG_PM_ADVANCED_DEBUG 428 + &dev_attr_runtime_status.attr, 429 + #endif 430 + &dev_attr_control.attr, 431 + &dev_attr_runtime_suspended_time.attr, 432 + &dev_attr_runtime_active_time.attr, 433 + NULL, 434 + }; 435 + static struct attribute_group pm_runtime_attr_group = { 436 + .name = power_group_name, 437 + .attrs = runtime_attrs, 438 + }; 439 + 440 + int dpm_sysfs_add(struct device *dev) 441 + { 442 + int rc; 443 + 444 + rc = sysfs_create_group(&dev->kobj, &pm_attr_group); 445 + if (rc == 0 && !dev->power.no_callbacks) { 446 + rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group); 447 + if (rc) 448 + sysfs_remove_group(&dev->kobj, &pm_attr_group); 449 + } 450 + return rc; 451 + } 452 + 453 + void rpm_sysfs_remove(struct device *dev) 454 + { 455 + sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); 456 + } 457 + 458 + void dpm_sysfs_remove(struct device *dev) 459 + { 460 + rpm_sysfs_remove(dev); 461 + sysfs_remove_group(&dev->kobj, &pm_attr_group); 462 + } 463 + 464 + #else /* CONFIG_PM_RUNTIME */ 465 466 int dpm_sysfs_add(struct device * dev) 467 { ··· 430 { 431 sysfs_remove_group(&dev->kobj, &pm_attr_group); 432 } 433 + 434 + #endif
+7
include/linux/pm.h
··· 41 42 struct device; 43 44 typedef struct pm_message { 45 int event; 46 } pm_message_t; ··· 481 unsigned int deferred_resume:1; 482 unsigned int run_wake:1; 483 unsigned int runtime_auto:1; 484 enum rpm_request request; 485 enum rpm_status runtime_status; 486 int runtime_error;
··· 41 42 struct device; 43 44 + #ifdef CONFIG_PM 45 + extern const char power_group_name[]; /* = "power" */ 46 + #else 47 + #define power_group_name NULL 48 + #endif 49 + 50 typedef struct pm_message { 51 int event; 52 } pm_message_t; ··· 475 unsigned int deferred_resume:1; 476 unsigned int run_wake:1; 477 unsigned int runtime_auto:1; 478 + unsigned int no_callbacks:1; 479 enum rpm_request request; 480 enum rpm_status runtime_status; 481 int runtime_error;
+2
include/linux/pm_runtime.h
··· 36 extern int pm_generic_runtime_idle(struct device *dev); 37 extern int pm_generic_runtime_suspend(struct device *dev); 38 extern int pm_generic_runtime_resume(struct device *dev); 39 40 static inline bool pm_children_suspended(struct device *dev) 41 { ··· 111 static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } 112 static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } 113 static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } 114 115 #endif /* !CONFIG_PM_RUNTIME */ 116
··· 36 extern int pm_generic_runtime_idle(struct device *dev); 37 extern int pm_generic_runtime_suspend(struct device *dev); 38 extern int pm_generic_runtime_resume(struct device *dev); 39 + extern void pm_runtime_no_callbacks(struct device *dev); 40 41 static inline bool pm_children_suspended(struct device *dev) 42 { ··· 110 static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } 111 static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } 112 static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } 113 + static inline void pm_runtime_no_callbacks(struct device *dev) {} 114 115 #endif /* !CONFIG_PM_RUNTIME */ 116