Commit 06a79b82b2a3e4bebb9a20638ca208c780e9e507
Exists in
master
and in
4 other branches
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6: PM / Hibernate: Fix preallocating of memory PM / Hibernate: Remove swsusp.c finally PM / Hibernate: Remove trailing space in message PM: Allow SCSI devices to suspend/resume asynchronously PM: Allow USB devices to suspend/resume asynchronously USB: implement non-tree resume ordering constraints for PCI host controllers PM: Allow PCI devices to suspend/resume asynchronously PM / Hibernate: Swap, remove useless check from swsusp_read() PM / Hibernate: Really deprecate deprecated user ioctls PM: Allow device drivers to use dpm_wait() PM: Start asynchronous resume threads upfront PM: Add facility for advanced testing of async suspend/resume PM: Add a switch for disabling/enabling asynchronous suspend/resume PM: Asynchronous suspend and resume of devices PM: Add parent information to timing messages PM: Document device power attributes in sysfs PM / Runtime: Add sysfs switch for disabling device run-time PM
Showing 28 changed files Side-by-side Diff
- Documentation/ABI/testing/sysfs-devices-power
- Documentation/ABI/testing/sysfs-power
- Documentation/feature-removal-schedule.txt
- drivers/base/power/main.c
- drivers/base/power/power.h
- drivers/base/power/runtime.c
- drivers/base/power/sysfs.c
- drivers/pci/pci.c
- drivers/pci/pcie/portdrv_core.c
- drivers/pci/probe.c
- drivers/scsi/hosts.c
- drivers/scsi/scsi_sysfs.c
- drivers/usb/core/driver.c
- drivers/usb/core/endpoint.c
- drivers/usb/core/hcd-pci.c
- drivers/usb/core/hub.c
- drivers/usb/core/message.c
- include/linux/device.h
- include/linux/pm.h
- include/linux/pm_runtime.h
- include/linux/resume-trace.h
- include/linux/usb.h
- kernel/power/Kconfig
- kernel/power/main.c
- kernel/power/snapshot.c
- kernel/power/swap.c
- kernel/power/swsusp.c
- kernel/power/user.c
Documentation/ABI/testing/sysfs-devices-power
1 | +What: /sys/devices/.../power/ | |
2 | +Date: January 2009 | |
3 | +Contact: Rafael J. Wysocki <rjw@sisk.pl> | |
4 | +Description: | |
5 | + The /sys/devices/.../power directory contains attributes | |
6 | + allowing the user space to check and modify some power | |
7 | + management related properties of given device. | |
8 | + | |
9 | +What: /sys/devices/.../power/wakeup | |
10 | +Date: January 2009 | |
11 | +Contact: Rafael J. Wysocki <rjw@sisk.pl> | |
12 | +Description: | |
13 | + The /sys/devices/.../power/wakeup attribute allows the user | |
14 | + space to check if the device is enabled to wake up the system | |
15 | + from sleep states, such as the memory sleep state (suspend to | |
16 | + RAM) and hibernation (suspend to disk), and to enable or disable | |
17 | + it to do that as desired. | |
18 | + | |
19 | + Some devices support "wakeup" events, which are hardware signals | |
20 | + used to activate the system from a sleep state. Such devices | |
21 | + have one of the following two values for the sysfs power/wakeup | |
22 | + file: | |
23 | + | |
24 | + + "enabled\n" to issue the events; | |
25 | + + "disabled\n" not to do so; | |
26 | + | |
27 | + In that cases the user space can change the setting represented | |
28 | + by the contents of this file by writing either "enabled", or | |
29 | + "disabled" to it. | |
30 | + | |
31 | + For the devices that are not capable of generating system wakeup | |
32 | + events this file contains "\n". In that cases the user space | |
33 | + cannot modify the contents of this file and the device cannot be | |
34 | + enabled to wake up the system. | |
35 | + | |
36 | +What: /sys/devices/.../power/control | |
37 | +Date: January 2009 | |
38 | +Contact: Rafael J. Wysocki <rjw@sisk.pl> | |
39 | +Description: | |
40 | + The /sys/devices/.../power/control attribute allows the user | |
41 | + space to control the run-time power management of the device. | |
42 | + | |
43 | + All devices have one of the following two values for the | |
44 | + power/control file: | |
45 | + | |
46 | + + "auto\n" to allow the device to be power managed at run time; | |
47 | + + "on\n" to prevent the device from being power managed; | |
48 | + | |
49 | + The default for all devices is "auto", which means that they may | |
50 | + be subject to automatic power management, depending on their | |
51 | + drivers. Changing this attribute to "on" prevents the driver | |
52 | + from power managing the device at run time. Doing that while | |
53 | + the device is suspended causes it to be woken up. | |
54 | + | |
55 | +What: /sys/devices/.../power/async | |
56 | +Date: January 2009 | |
57 | +Contact: Rafael J. Wysocki <rjw@sisk.pl> | |
58 | +Description: | |
59 | + The /sys/devices/.../async attribute allows the user space to | |
60 | + enable or diasble the device's suspend and resume callbacks to | |
61 | + be executed asynchronously (ie. in separate threads, in parallel | |
62 | + with the main suspend/resume thread) during system-wide power | |
63 | + transitions (eg. suspend to RAM, hibernation). | |
64 | + | |
65 | + All devices have one of the following two values for the | |
66 | + power/async file: | |
67 | + | |
68 | + + "enabled\n" to permit the asynchronous suspend/resume; | |
69 | + + "disabled\n" to forbid it; | |
70 | + | |
71 | + The value of this attribute may be changed by writing either | |
72 | + "enabled", or "disabled" to it. | |
73 | + | |
74 | + It generally is unsafe to permit the asynchronous suspend/resume | |
75 | + of a device unless it is certain that all of the PM dependencies | |
76 | + of the device are known to the PM core. However, for some | |
77 | + devices this attribute is set to "enabled" by bus type code or | |
78 | + device drivers and in that cases it should be safe to leave the | |
79 | + default value. |
Documentation/ABI/testing/sysfs-power
... | ... | @@ -101,4 +101,17 @@ |
101 | 101 | |
102 | 102 | CAUTION: Using it will cause your machine's real-time (CMOS) |
103 | 103 | clock to be set to a random invalid time after a resume. |
104 | + | |
105 | +What: /sys/power/pm_async | |
106 | +Date: January 2009 | |
107 | +Contact: Rafael J. Wysocki <rjw@sisk.pl> | |
108 | +Description: | |
109 | + The /sys/power/pm_async file controls the switch allowing the | |
110 | + user space to enable or disable asynchronous suspend and resume | |
111 | + of devices. If enabled, this feature will cause some device | |
112 | + drivers' suspend and resume callbacks to be executed in parallel | |
113 | + with each other and with the main suspend thread. It is enabled | |
114 | + if this file contains "1", which is the default. It may be | |
115 | + disabled by writing "0" to this file, in which case all devices | |
116 | + will be suspended and resumed synchronously. |
Documentation/feature-removal-schedule.txt
... | ... | @@ -64,6 +64,17 @@ |
64 | 64 | |
65 | 65 | --------------------------- |
66 | 66 | |
67 | +What: Deprecated snapshot ioctls | |
68 | +When: 2.6.36 | |
69 | + | |
70 | +Why: The ioctls in kernel/power/user.c were marked as deprecated long time | |
71 | + ago. Now they notify users about that so that they need to replace | |
72 | + their userspace. After some more time, remove them completely. | |
73 | + | |
74 | +Who: Jiri Slaby <jirislaby@gmail.com> | |
75 | + | |
76 | +--------------------------- | |
77 | + | |
67 | 78 | What: The ieee80211_regdom module parameter |
68 | 79 | When: March 2010 / desktop catchup |
69 | 80 |
drivers/base/power/main.c
... | ... | @@ -25,6 +25,7 @@ |
25 | 25 | #include <linux/resume-trace.h> |
26 | 26 | #include <linux/interrupt.h> |
27 | 27 | #include <linux/sched.h> |
28 | +#include <linux/async.h> | |
28 | 29 | |
29 | 30 | #include "../base.h" |
30 | 31 | #include "power.h" |
... | ... | @@ -42,6 +43,7 @@ |
42 | 43 | LIST_HEAD(dpm_list); |
43 | 44 | |
44 | 45 | static DEFINE_MUTEX(dpm_list_mtx); |
46 | +static pm_message_t pm_transition; | |
45 | 47 | |
46 | 48 | /* |
47 | 49 | * Set once the preparation of devices for a PM transition has started, reset |
... | ... | @@ -56,6 +58,7 @@ |
56 | 58 | void device_pm_init(struct device *dev) |
57 | 59 | { |
58 | 60 | dev->power.status = DPM_ON; |
61 | + init_completion(&dev->power.completion); | |
59 | 62 | pm_runtime_init(dev); |
60 | 63 | } |
61 | 64 | |
... | ... | @@ -111,6 +114,7 @@ |
111 | 114 | pr_debug("PM: Removing info for %s:%s\n", |
112 | 115 | dev->bus ? dev->bus->name : "No Bus", |
113 | 116 | kobject_name(&dev->kobj)); |
117 | + complete_all(&dev->power.completion); | |
114 | 118 | mutex_lock(&dpm_list_mtx); |
115 | 119 | list_del_init(&dev->power.entry); |
116 | 120 | mutex_unlock(&dpm_list_mtx); |
... | ... | @@ -188,6 +192,31 @@ |
188 | 192 | } |
189 | 193 | |
190 | 194 | /** |
195 | + * dpm_wait - Wait for a PM operation to complete. | |
196 | + * @dev: Device to wait for. | |
197 | + * @async: If unset, wait only if the device's power.async_suspend flag is set. | |
198 | + */ | |
199 | +static void dpm_wait(struct device *dev, bool async) | |
200 | +{ | |
201 | + if (!dev) | |
202 | + return; | |
203 | + | |
204 | + if (async || (pm_async_enabled && dev->power.async_suspend)) | |
205 | + wait_for_completion(&dev->power.completion); | |
206 | +} | |
207 | + | |
208 | +static int dpm_wait_fn(struct device *dev, void *async_ptr) | |
209 | +{ | |
210 | + dpm_wait(dev, *((bool *)async_ptr)); | |
211 | + return 0; | |
212 | +} | |
213 | + | |
214 | +static void dpm_wait_for_children(struct device *dev, bool async) | |
215 | +{ | |
216 | + device_for_each_child(dev, &async, dpm_wait_fn); | |
217 | +} | |
218 | + | |
219 | +/** | |
191 | 220 | * pm_op - Execute the PM operation appropriate for given PM event. |
192 | 221 | * @dev: Device to handle. |
193 | 222 | * @ops: PM operations to choose from. |
... | ... | @@ -271,8 +300,9 @@ |
271 | 300 | ktime_t calltime, delta, rettime; |
272 | 301 | |
273 | 302 | if (initcall_debug) { |
274 | - pr_info("calling %s_i+ @ %i\n", | |
275 | - dev_name(dev), task_pid_nr(current)); | |
303 | + pr_info("calling %s+ @ %i, parent: %s\n", | |
304 | + dev_name(dev), task_pid_nr(current), | |
305 | + dev->parent ? dev_name(dev->parent) : "none"); | |
276 | 306 | calltime = ktime_get(); |
277 | 307 | } |
278 | 308 | |
279 | 309 | |
280 | 310 | |
281 | 311 | |
... | ... | @@ -468,16 +498,20 @@ |
468 | 498 | * device_resume - Execute "resume" callbacks for given device. |
469 | 499 | * @dev: Device to handle. |
470 | 500 | * @state: PM transition of the system being carried out. |
501 | + * @async: If true, the device is being resumed asynchronously. | |
471 | 502 | */ |
472 | -static int device_resume(struct device *dev, pm_message_t state) | |
503 | +static int device_resume(struct device *dev, pm_message_t state, bool async) | |
473 | 504 | { |
474 | 505 | int error = 0; |
475 | 506 | |
476 | 507 | TRACE_DEVICE(dev); |
477 | 508 | TRACE_RESUME(0); |
478 | 509 | |
510 | + dpm_wait(dev->parent, async); | |
479 | 511 | down(&dev->sem); |
480 | 512 | |
513 | + dev->power.status = DPM_RESUMING; | |
514 | + | |
481 | 515 | if (dev->bus) { |
482 | 516 | if (dev->bus->pm) { |
483 | 517 | pm_dev_dbg(dev, state, ""); |
484 | 518 | |
... | ... | @@ -510,11 +544,29 @@ |
510 | 544 | } |
511 | 545 | End: |
512 | 546 | up(&dev->sem); |
547 | + complete_all(&dev->power.completion); | |
513 | 548 | |
514 | 549 | TRACE_RESUME(error); |
515 | 550 | return error; |
516 | 551 | } |
517 | 552 | |
553 | +static void async_resume(void *data, async_cookie_t cookie) | |
554 | +{ | |
555 | + struct device *dev = (struct device *)data; | |
556 | + int error; | |
557 | + | |
558 | + error = device_resume(dev, pm_transition, true); | |
559 | + if (error) | |
560 | + pm_dev_err(dev, pm_transition, " async", error); | |
561 | + put_device(dev); | |
562 | +} | |
563 | + | |
564 | +static bool is_async(struct device *dev) | |
565 | +{ | |
566 | + return dev->power.async_suspend && pm_async_enabled | |
567 | + && !pm_trace_is_enabled(); | |
568 | +} | |
569 | + | |
518 | 570 | /** |
519 | 571 | * dpm_resume - Execute "resume" callbacks for non-sysdev devices. |
520 | 572 | * @state: PM transition of the system being carried out. |
521 | 573 | |
522 | 574 | |
523 | 575 | |
524 | 576 | |
525 | 577 | |
... | ... | @@ -525,21 +577,33 @@ |
525 | 577 | static void dpm_resume(pm_message_t state) |
526 | 578 | { |
527 | 579 | struct list_head list; |
580 | + struct device *dev; | |
528 | 581 | ktime_t starttime = ktime_get(); |
529 | 582 | |
530 | 583 | INIT_LIST_HEAD(&list); |
531 | 584 | mutex_lock(&dpm_list_mtx); |
532 | - while (!list_empty(&dpm_list)) { | |
533 | - struct device *dev = to_device(dpm_list.next); | |
585 | + pm_transition = state; | |
534 | 586 | |
587 | + list_for_each_entry(dev, &dpm_list, power.entry) { | |
588 | + if (dev->power.status < DPM_OFF) | |
589 | + continue; | |
590 | + | |
591 | + INIT_COMPLETION(dev->power.completion); | |
592 | + if (is_async(dev)) { | |
593 | + get_device(dev); | |
594 | + async_schedule(async_resume, dev); | |
595 | + } | |
596 | + } | |
597 | + | |
598 | + while (!list_empty(&dpm_list)) { | |
599 | + dev = to_device(dpm_list.next); | |
535 | 600 | get_device(dev); |
536 | - if (dev->power.status >= DPM_OFF) { | |
601 | + if (dev->power.status >= DPM_OFF && !is_async(dev)) { | |
537 | 602 | int error; |
538 | 603 | |
539 | - dev->power.status = DPM_RESUMING; | |
540 | 604 | mutex_unlock(&dpm_list_mtx); |
541 | 605 | |
542 | - error = device_resume(dev, state); | |
606 | + error = device_resume(dev, state, false); | |
543 | 607 | |
544 | 608 | mutex_lock(&dpm_list_mtx); |
545 | 609 | if (error) |
... | ... | @@ -554,6 +618,7 @@ |
554 | 618 | } |
555 | 619 | list_splice(&list, &dpm_list); |
556 | 620 | mutex_unlock(&dpm_list_mtx); |
621 | + async_synchronize_full(); | |
557 | 622 | dpm_show_time(starttime, state, NULL); |
558 | 623 | } |
559 | 624 | |
560 | 625 | |
561 | 626 | |
562 | 627 | |
563 | 628 | |
... | ... | @@ -731,17 +796,24 @@ |
731 | 796 | return error; |
732 | 797 | } |
733 | 798 | |
799 | +static int async_error; | |
800 | + | |
734 | 801 | /** |
735 | 802 | * device_suspend - Execute "suspend" callbacks for given device. |
736 | 803 | * @dev: Device to handle. |
737 | 804 | * @state: PM transition of the system being carried out. |
805 | + * @async: If true, the device is being suspended asynchronously. | |
738 | 806 | */ |
739 | -static int device_suspend(struct device *dev, pm_message_t state) | |
807 | +static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |
740 | 808 | { |
741 | 809 | int error = 0; |
742 | 810 | |
811 | + dpm_wait_for_children(dev, async); | |
743 | 812 | down(&dev->sem); |
744 | 813 | |
814 | + if (async_error) | |
815 | + goto End; | |
816 | + | |
745 | 817 | if (dev->class) { |
746 | 818 | if (dev->class->pm) { |
747 | 819 | pm_dev_dbg(dev, state, "class "); |
748 | 820 | |
749 | 821 | |
... | ... | @@ -772,12 +844,44 @@ |
772 | 844 | error = legacy_suspend(dev, state, dev->bus->suspend); |
773 | 845 | } |
774 | 846 | } |
847 | + | |
848 | + if (!error) | |
849 | + dev->power.status = DPM_OFF; | |
850 | + | |
775 | 851 | End: |
776 | 852 | up(&dev->sem); |
853 | + complete_all(&dev->power.completion); | |
777 | 854 | |
778 | 855 | return error; |
779 | 856 | } |
780 | 857 | |
858 | +static void async_suspend(void *data, async_cookie_t cookie) | |
859 | +{ | |
860 | + struct device *dev = (struct device *)data; | |
861 | + int error; | |
862 | + | |
863 | + error = __device_suspend(dev, pm_transition, true); | |
864 | + if (error) { | |
865 | + pm_dev_err(dev, pm_transition, " async", error); | |
866 | + async_error = error; | |
867 | + } | |
868 | + | |
869 | + put_device(dev); | |
870 | +} | |
871 | + | |
872 | +static int device_suspend(struct device *dev) | |
873 | +{ | |
874 | + INIT_COMPLETION(dev->power.completion); | |
875 | + | |
876 | + if (pm_async_enabled && dev->power.async_suspend) { | |
877 | + get_device(dev); | |
878 | + async_schedule(async_suspend, dev); | |
879 | + return 0; | |
880 | + } | |
881 | + | |
882 | + return __device_suspend(dev, pm_transition, false); | |
883 | +} | |
884 | + | |
781 | 885 | /** |
782 | 886 | * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. |
783 | 887 | * @state: PM transition of the system being carried out. |
784 | 888 | |
... | ... | @@ -790,13 +894,15 @@ |
790 | 894 | |
791 | 895 | INIT_LIST_HEAD(&list); |
792 | 896 | mutex_lock(&dpm_list_mtx); |
897 | + pm_transition = state; | |
898 | + async_error = 0; | |
793 | 899 | while (!list_empty(&dpm_list)) { |
794 | 900 | struct device *dev = to_device(dpm_list.prev); |
795 | 901 | |
796 | 902 | get_device(dev); |
797 | 903 | mutex_unlock(&dpm_list_mtx); |
798 | 904 | |
799 | - error = device_suspend(dev, state); | |
905 | + error = device_suspend(dev); | |
800 | 906 | |
801 | 907 | mutex_lock(&dpm_list_mtx); |
802 | 908 | if (error) { |
803 | 909 | |
804 | 910 | |
805 | 911 | |
... | ... | @@ -804,14 +910,18 @@ |
804 | 910 | put_device(dev); |
805 | 911 | break; |
806 | 912 | } |
807 | - dev->power.status = DPM_OFF; | |
808 | 913 | if (!list_empty(&dev->power.entry)) |
809 | 914 | list_move(&dev->power.entry, &list); |
810 | 915 | put_device(dev); |
916 | + if (async_error) | |
917 | + break; | |
811 | 918 | } |
812 | 919 | list_splice(&list, dpm_list.prev); |
813 | 920 | mutex_unlock(&dpm_list_mtx); |
921 | + async_synchronize_full(); | |
814 | 922 | if (!error) |
923 | + error = async_error; | |
924 | + if (!error) | |
815 | 925 | dpm_show_time(starttime, state, NULL); |
816 | 926 | return error; |
817 | 927 | } |
... | ... | @@ -936,4 +1046,15 @@ |
936 | 1046 | printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret); |
937 | 1047 | } |
938 | 1048 | EXPORT_SYMBOL_GPL(__suspend_report_result); |
1049 | + | |
1050 | +/** | |
1051 | + * device_pm_wait_for_dev - Wait for suspend/resume of a device to complete. | |
1052 | + * @dev: Device to wait for. | |
1053 | + * @subordinate: Device that needs to wait for @dev. | |
1054 | + */ | |
1055 | +void device_pm_wait_for_dev(struct device *subordinate, struct device *dev) | |
1056 | +{ | |
1057 | + dpm_wait(dev, subordinate->power.async_suspend); | |
1058 | +} | |
1059 | +EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); |
drivers/base/power/power.h
... | ... | @@ -12,10 +12,10 @@ |
12 | 12 | |
13 | 13 | #ifdef CONFIG_PM_SLEEP |
14 | 14 | |
15 | -/* | |
16 | - * main.c | |
17 | - */ | |
15 | +/* kernel/power/main.c */ | |
16 | +extern int pm_async_enabled; | |
18 | 17 | |
18 | +/* drivers/base/power/main.c */ | |
19 | 19 | extern struct list_head dpm_list; /* The active device list */ |
20 | 20 | |
21 | 21 | static inline struct device *to_device(struct list_head *entry) |
drivers/base/power/runtime.c
... | ... | @@ -1011,6 +1011,50 @@ |
1011 | 1011 | EXPORT_SYMBOL_GPL(pm_runtime_enable); |
1012 | 1012 | |
1013 | 1013 | /** |
1014 | + * pm_runtime_forbid - Block run-time PM of a device. | |
1015 | + * @dev: Device to handle. | |
1016 | + * | |
1017 | + * Increase the device's usage count and clear its power.runtime_auto flag, | |
1018 | + * so that it cannot be suspended at run time until pm_runtime_allow() is called | |
1019 | + * for it. | |
1020 | + */ | |
1021 | +void pm_runtime_forbid(struct device *dev) | |
1022 | +{ | |
1023 | + spin_lock_irq(&dev->power.lock); | |
1024 | + if (!dev->power.runtime_auto) | |
1025 | + goto out; | |
1026 | + | |
1027 | + dev->power.runtime_auto = false; | |
1028 | + atomic_inc(&dev->power.usage_count); | |
1029 | + __pm_runtime_resume(dev, false); | |
1030 | + | |
1031 | + out: | |
1032 | + spin_unlock_irq(&dev->power.lock); | |
1033 | +} | |
1034 | +EXPORT_SYMBOL_GPL(pm_runtime_forbid); | |
1035 | + | |
1036 | +/** | |
1037 | + * pm_runtime_allow - Unblock run-time PM of a device. | |
1038 | + * @dev: Device to handle. | |
1039 | + * | |
1040 | + * Decrease the device's usage count and set its power.runtime_auto flag. | |
1041 | + */ | |
1042 | +void pm_runtime_allow(struct device *dev) | |
1043 | +{ | |
1044 | + spin_lock_irq(&dev->power.lock); | |
1045 | + if (dev->power.runtime_auto) | |
1046 | + goto out; | |
1047 | + | |
1048 | + dev->power.runtime_auto = true; | |
1049 | + if (atomic_dec_and_test(&dev->power.usage_count)) | |
1050 | + __pm_runtime_idle(dev); | |
1051 | + | |
1052 | + out: | |
1053 | + spin_unlock_irq(&dev->power.lock); | |
1054 | +} | |
1055 | +EXPORT_SYMBOL_GPL(pm_runtime_allow); | |
1056 | + | |
1057 | +/** | |
1014 | 1058 | * pm_runtime_init - Initialize run-time PM fields in given device object. |
1015 | 1059 | * @dev: Device object to initialize. |
1016 | 1060 | */ |
... | ... | @@ -1028,6 +1072,7 @@ |
1028 | 1072 | |
1029 | 1073 | atomic_set(&dev->power.child_count, 0); |
1030 | 1074 | pm_suspend_ignore_children(dev, false); |
1075 | + dev->power.runtime_auto = true; | |
1031 | 1076 | |
1032 | 1077 | dev->power.request_pending = false; |
1033 | 1078 | dev->power.request = RPM_REQ_NONE; |
drivers/base/power/sysfs.c
... | ... | @@ -4,9 +4,25 @@ |
4 | 4 | |
5 | 5 | #include <linux/device.h> |
6 | 6 | #include <linux/string.h> |
7 | +#include <linux/pm_runtime.h> | |
7 | 8 | #include "power.h" |
8 | 9 | |
9 | 10 | /* |
11 | + * control - Report/change current runtime PM setting of the device | |
12 | + * | |
13 | + * Runtime power management of a device can be blocked with the help of | |
14 | + * this attribute. All devices have one of the following two values for | |
15 | + * the power/control file: | |
16 | + * | |
17 | + * + "auto\n" to allow the device to be power managed at run time; | |
18 | + * + "on\n" to prevent the device from being power managed at run time; | |
19 | + * | |
20 | + * The default for all devices is "auto", which means that devices may be | |
21 | + * subject to automatic power management, depending on their drivers. | |
22 | + * Changing this attribute to "on" prevents the driver from power managing | |
23 | + * the device at run time. Doing that while the device is suspended causes | |
24 | + * it to be woken up. | |
25 | + * | |
10 | 26 | * wakeup - Report/change current wakeup option for device |
11 | 27 | * |
12 | 28 | * Some devices support "wakeup" events, which are hardware signals |
13 | 29 | |
... | ... | @@ -38,11 +54,61 @@ |
38 | 54 | * wakeup events internally (unless they are disabled), keeping |
39 | 55 | * their hardware in low power modes whenever they're unused. This |
40 | 56 | * saves runtime power, without requiring system-wide sleep states. |
57 | + * | |
58 | + * async - Report/change current async suspend setting for the device | |
59 | + * | |
60 | + * Asynchronous suspend and resume of the device during system-wide power | |
61 | + * state transitions can be enabled by writing "enabled" to this file. | |
62 | + * Analogously, if "disabled" is written to this file, the device will be | |
63 | + * suspended and resumed synchronously. | |
64 | + * | |
65 | + * All devices have one of the following two values for power/async: | |
66 | + * | |
67 | + * + "enabled\n" to permit the asynchronous suspend/resume of the device; | |
68 | + * + "disabled\n" to forbid it; | |
69 | + * | |
70 | + * NOTE: It generally is unsafe to permit the asynchronous suspend/resume | |
71 | + * of a device unless it is certain that all of the PM dependencies of the | |
72 | + * device are known to the PM core. However, for some devices this | |
73 | + * attribute is set to "enabled" by bus type code or device drivers and in | |
74 | + * that cases it should be safe to leave the default value. | |
41 | 75 | */ |
42 | 76 | |
43 | 77 | static const char enabled[] = "enabled"; |
44 | 78 | static const char disabled[] = "disabled"; |
45 | 79 | |
80 | +#ifdef CONFIG_PM_RUNTIME | |
81 | +static const char ctrl_auto[] = "auto"; | |
82 | +static const char ctrl_on[] = "on"; | |
83 | + | |
84 | +static ssize_t control_show(struct device *dev, struct device_attribute *attr, | |
85 | + char *buf) | |
86 | +{ | |
87 | + return sprintf(buf, "%s\n", | |
88 | + dev->power.runtime_auto ? ctrl_auto : ctrl_on); | |
89 | +} | |
90 | + | |
91 | +static ssize_t control_store(struct device * dev, struct device_attribute *attr, | |
92 | + const char * buf, size_t n) | |
93 | +{ | |
94 | + char *cp; | |
95 | + int len = n; | |
96 | + | |
97 | + cp = memchr(buf, '\n', n); | |
98 | + if (cp) | |
99 | + len = cp - buf; | |
100 | + if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0) | |
101 | + pm_runtime_allow(dev); | |
102 | + else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0) | |
103 | + pm_runtime_forbid(dev); | |
104 | + else | |
105 | + return -EINVAL; | |
106 | + return n; | |
107 | +} | |
108 | + | |
109 | +static DEVICE_ATTR(control, 0644, control_show, control_store); | |
110 | +#endif | |
111 | + | |
46 | 112 | static ssize_t |
47 | 113 | wake_show(struct device * dev, struct device_attribute *attr, char * buf) |
48 | 114 | { |
49 | 115 | |
50 | 116 | |
51 | 117 | |
... | ... | @@ -77,9 +143,43 @@ |
77 | 143 | |
78 | 144 | static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); |
79 | 145 | |
146 | +#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG | |
147 | +static ssize_t async_show(struct device *dev, struct device_attribute *attr, | |
148 | + char *buf) | |
149 | +{ | |
150 | + return sprintf(buf, "%s\n", | |
151 | + device_async_suspend_enabled(dev) ? enabled : disabled); | |
152 | +} | |
80 | 153 | |
154 | +static ssize_t async_store(struct device *dev, struct device_attribute *attr, | |
155 | + const char *buf, size_t n) | |
156 | +{ | |
157 | + char *cp; | |
158 | + int len = n; | |
159 | + | |
160 | + cp = memchr(buf, '\n', n); | |
161 | + if (cp) | |
162 | + len = cp - buf; | |
163 | + if (len == sizeof enabled - 1 && strncmp(buf, enabled, len) == 0) | |
164 | + device_enable_async_suspend(dev); | |
165 | + else if (len == sizeof disabled - 1 && strncmp(buf, disabled, len) == 0) | |
166 | + device_disable_async_suspend(dev); | |
167 | + else | |
168 | + return -EINVAL; | |
169 | + return n; | |
170 | +} | |
171 | + | |
172 | +static DEVICE_ATTR(async, 0644, async_show, async_store); | |
173 | +#endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */ | |
174 | + | |
81 | 175 | static struct attribute * power_attrs[] = { |
176 | +#ifdef CONFIG_PM_RUNTIME | |
177 | + &dev_attr_control.attr, | |
178 | +#endif | |
82 | 179 | &dev_attr_wakeup.attr, |
180 | +#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG | |
181 | + &dev_attr_async.attr, | |
182 | +#endif | |
83 | 183 | NULL, |
84 | 184 | }; |
85 | 185 | static struct attribute_group pm_attr_group = { |
drivers/pci/pci.c
drivers/pci/pcie/portdrv_core.c
drivers/pci/probe.c
drivers/scsi/hosts.c
... | ... | @@ -215,12 +215,16 @@ |
215 | 215 | shost->shost_gendev.parent = dev ? dev : &platform_bus; |
216 | 216 | shost->dma_dev = dma_dev; |
217 | 217 | |
218 | + device_enable_async_suspend(&shost->shost_gendev); | |
219 | + | |
218 | 220 | error = device_add(&shost->shost_gendev); |
219 | 221 | if (error) |
220 | 222 | goto out; |
221 | 223 | |
222 | 224 | scsi_host_set_state(shost, SHOST_RUNNING); |
223 | 225 | get_device(shost->shost_gendev.parent); |
226 | + | |
227 | + device_enable_async_suspend(&shost->shost_dev); | |
224 | 228 | |
225 | 229 | error = device_add(&shost->shost_dev); |
226 | 230 | if (error) |
drivers/scsi/scsi_sysfs.c
... | ... | @@ -847,6 +847,8 @@ |
847 | 847 | if (starget->state != STARGET_CREATED) |
848 | 848 | return 0; |
849 | 849 | |
850 | + device_enable_async_suspend(&starget->dev); | |
851 | + | |
850 | 852 | error = device_add(&starget->dev); |
851 | 853 | if (error) { |
852 | 854 | dev_err(&starget->dev, "target device_add failed, error %d\n", error); |
853 | 855 | |
... | ... | @@ -887,11 +889,13 @@ |
887 | 889 | return error; |
888 | 890 | |
889 | 891 | transport_configure_device(&starget->dev); |
892 | + device_enable_async_suspend(&sdev->sdev_gendev); | |
890 | 893 | error = device_add(&sdev->sdev_gendev); |
891 | 894 | if (error) { |
892 | 895 | printk(KERN_INFO "error 1\n"); |
893 | 896 | return error; |
894 | 897 | } |
898 | + device_enable_async_suspend(&sdev->sdev_dev); | |
895 | 899 | error = device_add(&sdev->sdev_dev); |
896 | 900 | if (error) { |
897 | 901 | printk(KERN_INFO "error 2\n"); |
drivers/usb/core/driver.c
... | ... | @@ -1022,6 +1022,14 @@ |
1022 | 1022 | goto done; |
1023 | 1023 | } |
1024 | 1024 | |
1025 | + /* Non-root devices on a full/low-speed bus must wait for their | |
1026 | + * companion high-speed root hub, in case a handoff is needed. | |
1027 | + */ | |
1028 | + if (!(msg.event & PM_EVENT_AUTO) && udev->parent && | |
1029 | + udev->bus->hs_companion) | |
1030 | + device_pm_wait_for_dev(&udev->dev, | |
1031 | + &udev->bus->hs_companion->root_hub->dev); | |
1032 | + | |
1025 | 1033 | if (udev->quirks & USB_QUIRK_RESET_RESUME) |
1026 | 1034 | udev->reset_resume = 1; |
1027 | 1035 |
drivers/usb/core/endpoint.c
... | ... | @@ -186,6 +186,7 @@ |
186 | 186 | ep_dev->dev.parent = parent; |
187 | 187 | ep_dev->dev.release = ep_device_release; |
188 | 188 | dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress); |
189 | + device_enable_async_suspend(&ep_dev->dev); | |
189 | 190 | |
190 | 191 | retval = device_register(&ep_dev->dev); |
191 | 192 | if (retval) |
drivers/usb/core/hcd-pci.c
... | ... | @@ -19,6 +19,7 @@ |
19 | 19 | #include <linux/kernel.h> |
20 | 20 | #include <linux/module.h> |
21 | 21 | #include <linux/pci.h> |
22 | +#include <linux/pm_runtime.h> | |
22 | 23 | #include <linux/usb.h> |
23 | 24 | |
24 | 25 | #include <asm/io.h> |
25 | 26 | |
... | ... | @@ -37,7 +38,123 @@ |
37 | 38 | |
38 | 39 | /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ |
39 | 40 | |
41 | +#ifdef CONFIG_PM_SLEEP | |
40 | 42 | |
43 | +/* Coordinate handoffs between EHCI and companion controllers | |
44 | + * during system resume | |
45 | + */ | |
46 | + | |
47 | +static DEFINE_MUTEX(companions_mutex); | |
48 | + | |
49 | +#define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI | |
50 | +#define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI | |
51 | +#define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI | |
52 | + | |
53 | +enum companion_action { | |
54 | + SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS | |
55 | +}; | |
56 | + | |
57 | +static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd, | |
58 | + enum companion_action action) | |
59 | +{ | |
60 | + struct pci_dev *companion; | |
61 | + struct usb_hcd *companion_hcd; | |
62 | + unsigned int slot = PCI_SLOT(pdev->devfn); | |
63 | + | |
64 | + /* Iterate through other PCI functions in the same slot. | |
65 | + * If pdev is OHCI or UHCI then we are looking for EHCI, and | |
66 | + * vice versa. | |
67 | + */ | |
68 | + companion = NULL; | |
69 | + for (;;) { | |
70 | + companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion); | |
71 | + if (!companion) | |
72 | + break; | |
73 | + if (companion->bus != pdev->bus || | |
74 | + PCI_SLOT(companion->devfn) != slot) | |
75 | + continue; | |
76 | + | |
77 | + companion_hcd = pci_get_drvdata(companion); | |
78 | + if (!companion_hcd) | |
79 | + continue; | |
80 | + | |
81 | + /* For SET_HS_COMPANION, store a pointer to the EHCI bus in | |
82 | + * the OHCI/UHCI companion bus structure. | |
83 | + * For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus | |
84 | + * in the OHCI/UHCI companion bus structure. | |
85 | + * For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI | |
86 | + * companion controllers have fully resumed. | |
87 | + */ | |
88 | + | |
89 | + if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) && | |
90 | + companion->class == CL_EHCI) { | |
91 | + /* action must be SET_HS_COMPANION */ | |
92 | + dev_dbg(&companion->dev, "HS companion for %s\n", | |
93 | + dev_name(&pdev->dev)); | |
94 | + hcd->self.hs_companion = &companion_hcd->self; | |
95 | + | |
96 | + } else if (pdev->class == CL_EHCI && | |
97 | + (companion->class == CL_OHCI || | |
98 | + companion->class == CL_UHCI)) { | |
99 | + switch (action) { | |
100 | + case SET_HS_COMPANION: | |
101 | + dev_dbg(&pdev->dev, "HS companion for %s\n", | |
102 | + dev_name(&companion->dev)); | |
103 | + companion_hcd->self.hs_companion = &hcd->self; | |
104 | + break; | |
105 | + case CLEAR_HS_COMPANION: | |
106 | + companion_hcd->self.hs_companion = NULL; | |
107 | + break; | |
108 | + case WAIT_FOR_COMPANIONS: | |
109 | + device_pm_wait_for_dev(&pdev->dev, | |
110 | + &companion->dev); | |
111 | + break; | |
112 | + } | |
113 | + } | |
114 | + } | |
115 | +} | |
116 | + | |
117 | +static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd) | |
118 | +{ | |
119 | + mutex_lock(&companions_mutex); | |
120 | + dev_set_drvdata(&pdev->dev, hcd); | |
121 | + companion_common(pdev, hcd, SET_HS_COMPANION); | |
122 | + mutex_unlock(&companions_mutex); | |
123 | +} | |
124 | + | |
125 | +static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd) | |
126 | +{ | |
127 | + mutex_lock(&companions_mutex); | |
128 | + dev_set_drvdata(&pdev->dev, NULL); | |
129 | + | |
130 | + /* If pdev is OHCI or UHCI, just clear its hs_companion pointer */ | |
131 | + if (pdev->class == CL_OHCI || pdev->class == CL_UHCI) | |
132 | + hcd->self.hs_companion = NULL; | |
133 | + | |
134 | + /* Otherwise search for companion buses and clear their pointers */ | |
135 | + else | |
136 | + companion_common(pdev, hcd, CLEAR_HS_COMPANION); | |
137 | + mutex_unlock(&companions_mutex); | |
138 | +} | |
139 | + | |
140 | +static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd) | |
141 | +{ | |
142 | + /* Only EHCI controllers need to wait. | |
143 | + * No locking is needed because a controller cannot be resumed | |
144 | + * while one of its companions is getting unbound. | |
145 | + */ | |
146 | + if (pdev->class == CL_EHCI) | |
147 | + companion_common(pdev, hcd, WAIT_FOR_COMPANIONS); | |
148 | +} | |
149 | + | |
150 | +#else /* !CONFIG_PM_SLEEP */ | |
151 | + | |
152 | +static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} | |
153 | +static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} | |
154 | +static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {} | |
155 | + | |
156 | +#endif /* !CONFIG_PM_SLEEP */ | |
157 | + | |
41 | 158 | /*-------------------------------------------------------------------------*/ |
42 | 159 | |
43 | 160 | /* configure so an HC device and id are always provided */ |
... | ... | @@ -123,7 +240,7 @@ |
123 | 240 | if (region == PCI_ROM_RESOURCE) { |
124 | 241 | dev_dbg(&dev->dev, "no i/o regions available\n"); |
125 | 242 | retval = -EBUSY; |
126 | - goto err1; | |
243 | + goto err2; | |
127 | 244 | } |
128 | 245 | } |
129 | 246 | |
... | ... | @@ -132,6 +249,7 @@ |
132 | 249 | retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); |
133 | 250 | if (retval != 0) |
134 | 251 | goto err4; |
252 | + set_hs_companion(dev, hcd); | |
135 | 253 | return retval; |
136 | 254 | |
137 | 255 | err4: |
... | ... | @@ -142,6 +260,7 @@ |
142 | 260 | } else |
143 | 261 | release_region(hcd->rsrc_start, hcd->rsrc_len); |
144 | 262 | err2: |
263 | + clear_hs_companion(dev, hcd); | |
145 | 264 | usb_put_hcd(hcd); |
146 | 265 | err1: |
147 | 266 | pci_disable_device(dev); |
... | ... | @@ -180,6 +299,7 @@ |
180 | 299 | } else { |
181 | 300 | release_region(hcd->rsrc_start, hcd->rsrc_len); |
182 | 301 | } |
302 | + clear_hs_companion(dev, hcd); | |
183 | 303 | usb_put_hcd(hcd); |
184 | 304 | pci_disable_device(dev); |
185 | 305 | } |
... | ... | @@ -344,6 +464,11 @@ |
344 | 464 | clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); |
345 | 465 | |
346 | 466 | if (hcd->driver->pci_resume) { |
467 | + /* This call should be made only during system resume, | |
468 | + * not during runtime resume. | |
469 | + */ | |
470 | + wait_for_companions(pci_dev, hcd); | |
471 | + | |
347 | 472 | retval = hcd->driver->pci_resume(hcd, hibernated); |
348 | 473 | if (retval) { |
349 | 474 | dev_err(dev, "PCI post-resume error %d!\n", retval); |
drivers/usb/core/hub.c
... | ... | @@ -1817,6 +1817,7 @@ |
1817 | 1817 | /* Tell the world! */ |
1818 | 1818 | announce_device(udev); |
1819 | 1819 | |
1820 | + device_enable_async_suspend(&udev->dev); | |
1820 | 1821 | /* Register the device. The device driver is responsible |
1821 | 1822 | * for configuring the device and invoking the add-device |
1822 | 1823 | * notifier chain (used by usbfs and possibly others). |
drivers/usb/core/message.c
... | ... | @@ -1867,6 +1867,7 @@ |
1867 | 1867 | "adding %s (config #%d, interface %d)\n", |
1868 | 1868 | dev_name(&intf->dev), configuration, |
1869 | 1869 | intf->cur_altsetting->desc.bInterfaceNumber); |
1870 | + device_enable_async_suspend(&intf->dev); | |
1870 | 1871 | ret = device_add(&intf->dev); |
1871 | 1872 | if (ret != 0) { |
1872 | 1873 | dev_err(&dev->dev, "device_add(%s) --> %d\n", |
include/linux/device.h
... | ... | @@ -472,6 +472,23 @@ |
472 | 472 | return dev->kobj.state_in_sysfs; |
473 | 473 | } |
474 | 474 | |
475 | +static inline void device_enable_async_suspend(struct device *dev) | |
476 | +{ | |
477 | + if (dev->power.status == DPM_ON) | |
478 | + dev->power.async_suspend = true; | |
479 | +} | |
480 | + | |
481 | +static inline void device_disable_async_suspend(struct device *dev) | |
482 | +{ | |
483 | + if (dev->power.status == DPM_ON) | |
484 | + dev->power.async_suspend = false; | |
485 | +} | |
486 | + | |
487 | +static inline bool device_async_suspend_enabled(struct device *dev) | |
488 | +{ | |
489 | + return !!dev->power.async_suspend; | |
490 | +} | |
491 | + | |
475 | 492 | void driver_init(void); |
476 | 493 | |
477 | 494 | /* |
include/linux/pm.h
... | ... | @@ -26,6 +26,7 @@ |
26 | 26 | #include <linux/spinlock.h> |
27 | 27 | #include <linux/wait.h> |
28 | 28 | #include <linux/timer.h> |
29 | +#include <linux/completion.h> | |
29 | 30 | |
30 | 31 | /* |
31 | 32 | * Callbacks for platform drivers to implement. |
32 | 33 | |
... | ... | @@ -412,9 +413,11 @@ |
412 | 413 | pm_message_t power_state; |
413 | 414 | unsigned int can_wakeup:1; |
414 | 415 | unsigned int should_wakeup:1; |
416 | + unsigned async_suspend:1; | |
415 | 417 | enum dpm_state status; /* Owned by the PM core */ |
416 | 418 | #ifdef CONFIG_PM_SLEEP |
417 | 419 | struct list_head entry; |
420 | + struct completion completion; | |
418 | 421 | #endif |
419 | 422 | #ifdef CONFIG_PM_RUNTIME |
420 | 423 | struct timer_list suspend_timer; |
... | ... | @@ -430,6 +433,7 @@ |
430 | 433 | unsigned int request_pending:1; |
431 | 434 | unsigned int deferred_resume:1; |
432 | 435 | unsigned int run_wake:1; |
436 | + unsigned int runtime_auto:1; | |
433 | 437 | enum rpm_request request; |
434 | 438 | enum rpm_status runtime_status; |
435 | 439 | int runtime_error; |
... | ... | @@ -508,6 +512,7 @@ |
508 | 512 | __suspend_report_result(__func__, fn, ret); \ |
509 | 513 | } while (0) |
510 | 514 | |
515 | +extern void device_pm_wait_for_dev(struct device *sub, struct device *dev); | |
511 | 516 | #else /* !CONFIG_PM_SLEEP */ |
512 | 517 | |
513 | 518 | #define device_pm_lock() do {} while (0) |
... | ... | @@ -520,6 +525,7 @@ |
520 | 525 | |
521 | 526 | #define suspend_report_result(fn, ret) do {} while (0) |
522 | 527 | |
528 | +static inline void device_pm_wait_for_dev(struct device *a, struct device *b) {} | |
523 | 529 | #endif /* !CONFIG_PM_SLEEP */ |
524 | 530 | |
525 | 531 | /* How to reorder dpm_list after device_move() */ |
include/linux/pm_runtime.h
... | ... | @@ -28,6 +28,8 @@ |
28 | 28 | extern int pm_runtime_barrier(struct device *dev); |
29 | 29 | extern void pm_runtime_enable(struct device *dev); |
30 | 30 | extern void __pm_runtime_disable(struct device *dev, bool check_resume); |
31 | +extern void pm_runtime_allow(struct device *dev); | |
32 | +extern void pm_runtime_forbid(struct device *dev); | |
31 | 33 | |
32 | 34 | static inline bool pm_children_suspended(struct device *dev) |
33 | 35 | { |
... | ... | @@ -78,6 +80,8 @@ |
78 | 80 | static inline int pm_runtime_barrier(struct device *dev) { return 0; } |
79 | 81 | static inline void pm_runtime_enable(struct device *dev) {} |
80 | 82 | static inline void __pm_runtime_disable(struct device *dev, bool c) {} |
83 | +static inline void pm_runtime_allow(struct device *dev) {} | |
84 | +static inline void pm_runtime_forbid(struct device *dev) {} | |
81 | 85 | |
82 | 86 | static inline bool pm_children_suspended(struct device *dev) { return false; } |
83 | 87 | static inline void pm_suspend_ignore_children(struct device *dev, bool en) {} |
include/linux/resume-trace.h
... | ... | @@ -6,6 +6,11 @@ |
6 | 6 | |
7 | 7 | extern int pm_trace_enabled; |
8 | 8 | |
9 | +static inline int pm_trace_is_enabled(void) | |
10 | +{ | |
11 | + return pm_trace_enabled; | |
12 | +} | |
13 | + | |
9 | 14 | struct device; |
10 | 15 | extern void set_trace_device(struct device *); |
11 | 16 | extern void generate_resume_trace(const void *tracedata, unsigned int user); |
... | ... | @@ -16,6 +21,8 @@ |
16 | 21 | } while(0) |
17 | 22 | |
18 | 23 | #else |
24 | + | |
25 | +static inline int pm_trace_is_enabled(void) { return 0; } | |
19 | 26 | |
20 | 27 | #define TRACE_DEVICE(dev) do { } while (0) |
21 | 28 | #define TRACE_RESUME(dev) do { } while (0) |
include/linux/usb.h
... | ... | @@ -339,6 +339,7 @@ |
339 | 339 | |
340 | 340 | struct usb_devmap devmap; /* device address allocation map */ |
341 | 341 | struct usb_device *root_hub; /* Root hub */ |
342 | + struct usb_bus *hs_companion; /* Companion EHCI bus, if any */ | |
342 | 343 | struct list_head bus_list; /* list of busses */ |
343 | 344 | |
344 | 345 | int bandwidth_allocated; /* on this bus: how much of the time |
kernel/power/Kconfig
... | ... | @@ -27,6 +27,15 @@ |
27 | 27 | code. This is helpful when debugging and reporting PM bugs, like |
28 | 28 | suspend support. |
29 | 29 | |
30 | +config PM_ADVANCED_DEBUG | |
31 | + bool "Extra PM attributes in sysfs for low-level debugging/testing" | |
32 | + depends on PM_DEBUG | |
33 | + default n | |
34 | + ---help--- | |
35 | + Add extra sysfs attributes allowing one to access some Power Management | |
36 | + fields of device objects from user space. If you are not a kernel | |
37 | + developer interested in debugging/testing Power Management, say "no". | |
38 | + | |
30 | 39 | config PM_VERBOSE |
31 | 40 | bool "Verbose Power Management debugging" |
32 | 41 | depends on PM_DEBUG |
... | ... | @@ -84,6 +93,11 @@ |
84 | 93 | bool |
85 | 94 | depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE |
86 | 95 | default y |
96 | + | |
97 | +config PM_SLEEP_ADVANCED_DEBUG | |
98 | + bool | |
99 | + depends on PM_ADVANCED_DEBUG | |
100 | + default n | |
87 | 101 | |
88 | 102 | config SUSPEND |
89 | 103 | bool "Suspend to RAM and standby" |
kernel/power/main.c
... | ... | @@ -44,6 +44,32 @@ |
44 | 44 | == NOTIFY_BAD) ? -EINVAL : 0; |
45 | 45 | } |
46 | 46 | |
47 | +/* If set, devices may be suspended and resumed asynchronously. */ | |
48 | +int pm_async_enabled = 1; | |
49 | + | |
50 | +static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr, | |
51 | + char *buf) | |
52 | +{ | |
53 | + return sprintf(buf, "%d\n", pm_async_enabled); | |
54 | +} | |
55 | + | |
56 | +static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr, | |
57 | + const char *buf, size_t n) | |
58 | +{ | |
59 | + unsigned long val; | |
60 | + | |
61 | + if (strict_strtoul(buf, 10, &val)) | |
62 | + return -EINVAL; | |
63 | + | |
64 | + if (val > 1) | |
65 | + return -EINVAL; | |
66 | + | |
67 | + pm_async_enabled = val; | |
68 | + return n; | |
69 | +} | |
70 | + | |
71 | +power_attr(pm_async); | |
72 | + | |
47 | 73 | #ifdef CONFIG_PM_DEBUG |
48 | 74 | int pm_test_level = TEST_NONE; |
49 | 75 | |
50 | 76 | |
... | ... | @@ -208,8 +234,11 @@ |
208 | 234 | #ifdef CONFIG_PM_TRACE |
209 | 235 | &pm_trace_attr.attr, |
210 | 236 | #endif |
211 | -#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG) | |
237 | +#ifdef CONFIG_PM_SLEEP | |
238 | + &pm_async_attr.attr, | |
239 | +#ifdef CONFIG_PM_DEBUG | |
212 | 240 | &pm_test_attr.attr, |
241 | +#endif | |
213 | 242 | #endif |
214 | 243 | NULL, |
215 | 244 | }; |
kernel/power/snapshot.c
... | ... | @@ -1181,7 +1181,7 @@ |
1181 | 1181 | |
1182 | 1182 | memory_bm_position_reset(©_bm); |
1183 | 1183 | |
1184 | - while (to_free_normal > 0 && to_free_highmem > 0) { | |
1184 | + while (to_free_normal > 0 || to_free_highmem > 0) { | |
1185 | 1185 | unsigned long pfn = memory_bm_next_pfn(©_bm); |
1186 | 1186 | struct page *page = pfn_to_page(pfn); |
1187 | 1187 | |
... | ... | @@ -1500,7 +1500,7 @@ |
1500 | 1500 | { |
1501 | 1501 | unsigned int nr_pages, nr_highmem; |
1502 | 1502 | |
1503 | - printk(KERN_INFO "PM: Creating hibernation image: \n"); | |
1503 | + printk(KERN_INFO "PM: Creating hibernation image:\n"); | |
1504 | 1504 | |
1505 | 1505 | drain_local_pages(NULL); |
1506 | 1506 | nr_pages = count_data_pages(); |
kernel/power/swap.c
... | ... | @@ -657,10 +657,6 @@ |
657 | 657 | struct swsusp_info *header; |
658 | 658 | |
659 | 659 | *flags_p = swsusp_header->flags; |
660 | - if (IS_ERR(resume_bdev)) { | |
661 | - pr_debug("PM: Image device not initialised\n"); | |
662 | - return PTR_ERR(resume_bdev); | |
663 | - } | |
664 | 660 | |
665 | 661 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); |
666 | 662 | error = snapshot_write_next(&snapshot, PAGE_SIZE); |
kernel/power/swsusp.c
1 | -/* | |
2 | - * linux/kernel/power/swsusp.c | |
3 | - * | |
4 | - * This file provides code to write suspend image to swap and read it back. | |
5 | - * | |
6 | - * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu> | |
7 | - * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.cz> | |
8 | - * | |
9 | - * This file is released under the GPLv2. | |
10 | - * | |
11 | - * I'd like to thank the following people for their work: | |
12 | - * | |
13 | - * Pavel Machek <pavel@ucw.cz>: | |
14 | - * Modifications, defectiveness pointing, being with me at the very beginning, | |
15 | - * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17. | |
16 | - * | |
17 | - * Steve Doddi <dirk@loth.demon.co.uk>: | |
18 | - * Support the possibility of hardware state restoring. | |
19 | - * | |
20 | - * Raph <grey.havens@earthling.net>: | |
21 | - * Support for preserving states of network devices and virtual console | |
22 | - * (including X and svgatextmode) | |
23 | - * | |
24 | - * Kurt Garloff <garloff@suse.de>: | |
25 | - * Straightened the critical function in order to prevent compilers from | |
26 | - * playing tricks with local variables. | |
27 | - * | |
28 | - * Andreas Mohr <a.mohr@mailto.de> | |
29 | - * | |
30 | - * Alex Badea <vampire@go.ro>: | |
31 | - * Fixed runaway init | |
32 | - * | |
33 | - * Rafael J. Wysocki <rjw@sisk.pl> | |
34 | - * Reworked the freeing of memory and the handling of swap | |
35 | - * | |
36 | - * More state savers are welcome. Especially for the scsi layer... | |
37 | - * | |
38 | - * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt | |
39 | - */ | |
40 | - | |
41 | -#include <linux/mm.h> | |
42 | -#include <linux/suspend.h> | |
43 | -#include <linux/spinlock.h> | |
44 | -#include <linux/kernel.h> | |
45 | -#include <linux/major.h> | |
46 | -#include <linux/swap.h> | |
47 | -#include <linux/pm.h> | |
48 | -#include <linux/swapops.h> | |
49 | -#include <linux/bootmem.h> | |
50 | -#include <linux/syscalls.h> | |
51 | -#include <linux/highmem.h> | |
52 | -#include <linux/time.h> | |
53 | -#include <linux/rbtree.h> | |
54 | -#include <linux/io.h> | |
55 | - | |
56 | -#include "power.h" | |
57 | - | |
58 | -int in_suspend __nosavedata = 0; |
kernel/power/user.c
... | ... | @@ -195,6 +195,15 @@ |
195 | 195 | return res; |
196 | 196 | } |
197 | 197 | |
198 | +static void snapshot_deprecated_ioctl(unsigned int cmd) | |
199 | +{ | |
200 | + if (printk_ratelimit()) | |
201 | + printk(KERN_NOTICE "%pf: ioctl '%.8x' is deprecated and will " | |
202 | + "be removed soon, update your suspend-to-disk " | |
203 | + "utilities\n", | |
204 | + __builtin_return_address(0), cmd); | |
205 | +} | |
206 | + | |
198 | 207 | static long snapshot_ioctl(struct file *filp, unsigned int cmd, |
199 | 208 | unsigned long arg) |
200 | 209 | { |
201 | 210 | |
... | ... | @@ -246,8 +255,9 @@ |
246 | 255 | data->frozen = 0; |
247 | 256 | break; |
248 | 257 | |
249 | - case SNAPSHOT_CREATE_IMAGE: | |
250 | 258 | case SNAPSHOT_ATOMIC_SNAPSHOT: |
259 | + snapshot_deprecated_ioctl(cmd); | |
260 | + case SNAPSHOT_CREATE_IMAGE: | |
251 | 261 | if (data->mode != O_RDONLY || !data->frozen || data->ready) { |
252 | 262 | error = -EPERM; |
253 | 263 | break; |
254 | 264 | |
... | ... | @@ -275,8 +285,9 @@ |
275 | 285 | data->ready = 0; |
276 | 286 | break; |
277 | 287 | |
278 | - case SNAPSHOT_PREF_IMAGE_SIZE: | |
279 | 288 | case SNAPSHOT_SET_IMAGE_SIZE: |
289 | + snapshot_deprecated_ioctl(cmd); | |
290 | + case SNAPSHOT_PREF_IMAGE_SIZE: | |
280 | 291 | image_size = arg; |
281 | 292 | break; |
282 | 293 | |
283 | 294 | |
284 | 295 | |
285 | 296 | |
... | ... | @@ -290,15 +301,17 @@ |
290 | 301 | error = put_user(size, (loff_t __user *)arg); |
291 | 302 | break; |
292 | 303 | |
293 | - case SNAPSHOT_AVAIL_SWAP_SIZE: | |
294 | 304 | case SNAPSHOT_AVAIL_SWAP: |
305 | + snapshot_deprecated_ioctl(cmd); | |
306 | + case SNAPSHOT_AVAIL_SWAP_SIZE: | |
295 | 307 | size = count_swap_pages(data->swap, 1); |
296 | 308 | size <<= PAGE_SHIFT; |
297 | 309 | error = put_user(size, (loff_t __user *)arg); |
298 | 310 | break; |
299 | 311 | |
300 | - case SNAPSHOT_ALLOC_SWAP_PAGE: | |
301 | 312 | case SNAPSHOT_GET_SWAP_PAGE: |
313 | + snapshot_deprecated_ioctl(cmd); | |
314 | + case SNAPSHOT_ALLOC_SWAP_PAGE: | |
302 | 315 | if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { |
303 | 316 | error = -ENODEV; |
304 | 317 | break; |
... | ... | @@ -321,6 +334,7 @@ |
321 | 334 | break; |
322 | 335 | |
323 | 336 | case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */ |
337 | + snapshot_deprecated_ioctl(cmd); | |
324 | 338 | if (!swsusp_swap_in_use()) { |
325 | 339 | /* |
326 | 340 | * User space encodes device types as two-byte values, |
... | ... | @@ -362,6 +376,7 @@ |
362 | 376 | break; |
363 | 377 | |
364 | 378 | case SNAPSHOT_PMOPS: /* This ioctl is deprecated */ |
379 | + snapshot_deprecated_ioctl(cmd); | |
365 | 380 | error = -EINVAL; |
366 | 381 | |
367 | 382 | switch (arg) { |