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 1 Run-time Power Management Framework for I/O Devices 2 2 3 3 (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 4 + (C) 2010 Alan Stern <stern@rowland.harvard.edu> 4 5 5 6 1. Introduction 6 7 ··· 231 230 interface; it may only be modified with the help of the pm_runtime_allow() 232 231 and pm_runtime_forbid() helper functions 233 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 + 234 238 All of the above fields are members of the 'power' member of 'struct device'. 235 239 236 240 4. Run-time PM Device Helper Functions ··· 354 348 - unset the power.runtime_auto flag for the device and increase its usage 355 349 counter (used by the /sys/devices/.../power/control interface to 356 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) 357 356 358 357 It is safe to execute the following helper functions from interrupt context: 359 358 ··· 535 524 restore, and run-time resume, can achieve this with the help of the 536 525 UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its 537 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 60 61 61 extern int dpm_sysfs_add(struct device *); 62 62 extern void dpm_sysfs_remove(struct device *); 63 + extern void rpm_sysfs_remove(struct device *); 63 64 64 65 #else /* CONFIG_PM */ 65 66
+53 -1
drivers/base/power/runtime.c
··· 10 10 #include <linux/sched.h> 11 11 #include <linux/pm_runtime.h> 12 12 #include <linux/jiffies.h> 13 + #include "power.h" 13 14 14 15 static int rpm_resume(struct device *dev, int rpmflags); 16 + static int rpm_suspend(struct device *dev, int rpmflags); 15 17 16 18 /** 17 19 * update_pm_runtime_accounting - Update the time accounting of power states ··· 150 148 /* Pending requests need to be canceled. */ 151 149 dev->power.request = RPM_REQ_NONE; 152 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 + 153 157 /* Carry out an asynchronous or a synchronous idle notification. */ 154 158 if (rpmflags & RPM_ASYNC) { 155 159 dev->power.request = RPM_REQ_IDLE; ··· 262 254 goto repeat; 263 255 } 264 256 257 + dev->power.deferred_resume = false; 258 + if (dev->power.no_callbacks) 259 + goto no_callback; /* Assume success. */ 260 + 265 261 /* Carry out an asynchronous or a synchronous suspend. */ 266 262 if (rpmflags & RPM_ASYNC) { 267 263 dev->power.request = RPM_REQ_SUSPEND; ··· 277 265 } 278 266 279 267 __update_runtime_status(dev, RPM_SUSPENDING); 280 - dev->power.deferred_resume = false; 281 268 282 269 if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { 283 270 spin_unlock_irq(&dev->power.lock); ··· 316 305 pm_runtime_cancel_pending(dev); 317 306 } 318 307 } else { 308 + no_callback: 319 309 __update_runtime_status(dev, RPM_SUSPENDED); 320 310 pm_runtime_deactivate_timer(dev); 321 311 ··· 421 409 goto repeat; 422 410 } 423 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 + 424 429 /* Carry out an asynchronous or a synchronous resume. */ 425 430 if (rpmflags & RPM_ASYNC) { 426 431 dev->power.request = RPM_REQ_RESUME; ··· 478 449 goto repeat; 479 450 } 480 451 452 + if (dev->power.no_callbacks) 453 + goto no_callback; /* Assume success. */ 454 + 481 455 __update_runtime_status(dev, RPM_RESUMING); 482 456 483 457 if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { ··· 514 482 __update_runtime_status(dev, RPM_SUSPENDED); 515 483 pm_runtime_cancel_pending(dev); 516 484 } else { 485 + no_callback: 517 486 __update_runtime_status(dev, RPM_ACTIVE); 518 487 if (parent) 519 488 atomic_inc(&parent->power.child_count); ··· 986 953 spin_unlock_irq(&dev->power.lock); 987 954 } 988 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); 989 975 990 976 /** 991 977 * pm_runtime_init - Initialize run-time PM fields in given device object.
+49 -7
drivers/base/power/sysfs.c
··· 81 81 static const char enabled[] = "enabled"; 82 82 static const char disabled[] = "disabled"; 83 83 84 + const char power_group_name[] = "power"; 85 + EXPORT_SYMBOL_GPL(power_group_name); 86 + 84 87 #ifdef CONFIG_PM_RUNTIME 85 88 static const char ctrl_auto[] = "auto"; 86 89 static const char ctrl_on[] = "on"; ··· 393 390 #endif /* CONFIG_PM_ADVANCED_DEBUG */ 394 391 395 392 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 393 &dev_attr_wakeup.attr, 403 394 #ifdef CONFIG_PM_SLEEP 404 395 &dev_attr_wakeup_count.attr, ··· 406 409 #ifdef CONFIG_PM_ADVANCED_DEBUG 407 410 &dev_attr_async.attr, 408 411 #ifdef CONFIG_PM_RUNTIME 412 + &dev_attr_runtime_status.attr, 409 413 &dev_attr_runtime_usage.attr, 410 414 &dev_attr_runtime_active_kids.attr, 411 415 &dev_attr_runtime_enabled.attr, ··· 415 417 NULL, 416 418 }; 417 419 static struct attribute_group pm_attr_group = { 418 - .name = "power", 420 + .name = power_group_name, 419 421 .attrs = power_attrs, 420 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 */ 421 465 422 466 int dpm_sysfs_add(struct device * dev) 423 467 { ··· 470 430 { 471 431 sysfs_remove_group(&dev->kobj, &pm_attr_group); 472 432 } 433 + 434 + #endif
+7
include/linux/pm.h
··· 41 41 42 42 struct device; 43 43 44 + #ifdef CONFIG_PM 45 + extern const char power_group_name[]; /* = "power" */ 46 + #else 47 + #define power_group_name NULL 48 + #endif 49 + 44 50 typedef struct pm_message { 45 51 int event; 46 52 } pm_message_t; ··· 481 475 unsigned int deferred_resume:1; 482 476 unsigned int run_wake:1; 483 477 unsigned int runtime_auto:1; 478 + unsigned int no_callbacks:1; 484 479 enum rpm_request request; 485 480 enum rpm_status runtime_status; 486 481 int runtime_error;
+2
include/linux/pm_runtime.h
··· 36 36 extern int pm_generic_runtime_idle(struct device *dev); 37 37 extern int pm_generic_runtime_suspend(struct device *dev); 38 38 extern int pm_generic_runtime_resume(struct device *dev); 39 + extern void pm_runtime_no_callbacks(struct device *dev); 39 40 40 41 static inline bool pm_children_suspended(struct device *dev) 41 42 { ··· 111 110 static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } 112 111 static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } 113 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 114 115 115 #endif /* !CONFIG_PM_RUNTIME */ 116 116