aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-05-17 17:26:00 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2011-05-17 17:26:00 -0400
commit91e7c75ba93c48a82670d630b9daac92ff70095d (patch)
treeb44ac3dca1d03cce99cccc3f4ce55d4b5475d373 /drivers/base
parentc650da23d59d2c82307380414606774c6d49b8bd (diff)
PM: Allow drivers to allocate memory from .prepare() callbacks safely
If device drivers allocate substantial amounts of memory (above 1 MB) in their hibernate .freeze() callbacks (or in their legacy suspend callbcks during hibernation), the subsequent creation of hibernate image may fail due to the lack of memory. This is the case, because the drivers' .freeze() callbacks are executed after the hibernate memory preallocation has been carried out and the preallocated amount of memory may be too small to cover the new driver allocations. Unfortunately, the drivers' .prepare() callbacks also are executed after the hibernate memory preallocation has completed, so they are not suitable for allocating additional memory either. Thus the only way a driver can safely allocate memory during hibernation is to use a hibernate/suspend notifier. However, the notifiers are called before the freezing of user space and the drivers wanting to use them for allocating additional memory may not know how much memory needs to be allocated at that point. To let device drivers overcome this difficulty rework the hibernation sequence so that the memory preallocation is carried out after the drivers' .prepare() callbacks have been executed, so that the .prepare() callbacks can be used for allocating additional memory to be used by the drivers' .freeze() callbacks. Update documentation to match the new behavior of the code. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/power/main.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 3b354560f306..aa6320207745 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -579,11 +579,13 @@ static bool is_async(struct device *dev)
579 * Execute the appropriate "resume" callback for all devices whose status 579 * Execute the appropriate "resume" callback for all devices whose status
580 * indicates that they are suspended. 580 * indicates that they are suspended.
581 */ 581 */
582static void dpm_resume(pm_message_t state) 582void dpm_resume(pm_message_t state)
583{ 583{
584 struct device *dev; 584 struct device *dev;
585 ktime_t starttime = ktime_get(); 585 ktime_t starttime = ktime_get();
586 586
587 might_sleep();
588
587 mutex_lock(&dpm_list_mtx); 589 mutex_lock(&dpm_list_mtx);
588 pm_transition = state; 590 pm_transition = state;
589 async_error = 0; 591 async_error = 0;
@@ -656,10 +658,12 @@ static void device_complete(struct device *dev, pm_message_t state)
656 * Execute the ->complete() callbacks for all devices whose PM status is not 658 * Execute the ->complete() callbacks for all devices whose PM status is not
657 * DPM_ON (this allows new devices to be registered). 659 * DPM_ON (this allows new devices to be registered).
658 */ 660 */
659static void dpm_complete(pm_message_t state) 661void dpm_complete(pm_message_t state)
660{ 662{
661 struct list_head list; 663 struct list_head list;
662 664
665 might_sleep();
666
663 INIT_LIST_HEAD(&list); 667 INIT_LIST_HEAD(&list);
664 mutex_lock(&dpm_list_mtx); 668 mutex_lock(&dpm_list_mtx);
665 while (!list_empty(&dpm_prepared_list)) { 669 while (!list_empty(&dpm_prepared_list)) {
@@ -688,7 +692,6 @@ static void dpm_complete(pm_message_t state)
688 */ 692 */
689void dpm_resume_end(pm_message_t state) 693void dpm_resume_end(pm_message_t state)
690{ 694{
691 might_sleep();
692 dpm_resume(state); 695 dpm_resume(state);
693 dpm_complete(state); 696 dpm_complete(state);
694} 697}
@@ -912,11 +915,13 @@ static int device_suspend(struct device *dev)
912 * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. 915 * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
913 * @state: PM transition of the system being carried out. 916 * @state: PM transition of the system being carried out.
914 */ 917 */
915static int dpm_suspend(pm_message_t state) 918int dpm_suspend(pm_message_t state)
916{ 919{
917 ktime_t starttime = ktime_get(); 920 ktime_t starttime = ktime_get();
918 int error = 0; 921 int error = 0;
919 922
923 might_sleep();
924
920 mutex_lock(&dpm_list_mtx); 925 mutex_lock(&dpm_list_mtx);
921 pm_transition = state; 926 pm_transition = state;
922 async_error = 0; 927 async_error = 0;
@@ -1003,10 +1008,12 @@ static int device_prepare(struct device *dev, pm_message_t state)
1003 * 1008 *
1004 * Execute the ->prepare() callback(s) for all devices. 1009 * Execute the ->prepare() callback(s) for all devices.
1005 */ 1010 */
1006static int dpm_prepare(pm_message_t state) 1011int dpm_prepare(pm_message_t state)
1007{ 1012{
1008 int error = 0; 1013 int error = 0;
1009 1014
1015 might_sleep();
1016
1010 mutex_lock(&dpm_list_mtx); 1017 mutex_lock(&dpm_list_mtx);
1011 while (!list_empty(&dpm_list)) { 1018 while (!list_empty(&dpm_list)) {
1012 struct device *dev = to_device(dpm_list.next); 1019 struct device *dev = to_device(dpm_list.next);
@@ -1055,7 +1062,6 @@ int dpm_suspend_start(pm_message_t state)
1055{ 1062{
1056 int error; 1063 int error;
1057 1064
1058 might_sleep();
1059 error = dpm_prepare(state); 1065 error = dpm_prepare(state);
1060 if (!error) 1066 if (!error)
1061 error = dpm_suspend(state); 1067 error = dpm_suspend(state);