diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2010-12-15 18:50:30 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2010-12-24 09:02:43 -0500 |
commit | 8a43a9ab7b329aa8590f8a064df9bf8c80987507 (patch) | |
tree | 7e60bb0dd5f4dcf6531aa3aad196930f02553166 /drivers/base | |
parent | 2cbb3ce1ad19e66858a4284dd6c4bb958162c483 (diff) |
PM: Use a different list of devices for each stage of device suspend
Instead of keeping all devices in the same list during system suspend
and resume, regardless of what suspend-resume callbacks have been
executed for them already, use separate lists of devices that have
had their ->prepare(), ->suspend() and ->suspend_noirq() callbacks
executed. This will allow us to simplify the core device suspend and
resume routines.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/power/main.c | 53 |
1 files changed, 19 insertions, 34 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index e6d628012654..b711867fa58e 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -42,6 +42,9 @@ | |||
42 | */ | 42 | */ |
43 | 43 | ||
44 | LIST_HEAD(dpm_list); | 44 | LIST_HEAD(dpm_list); |
45 | LIST_HEAD(dpm_prepared_list); | ||
46 | LIST_HEAD(dpm_suspended_list); | ||
47 | LIST_HEAD(dpm_noirq_list); | ||
45 | 48 | ||
46 | static DEFINE_MUTEX(dpm_list_mtx); | 49 | static DEFINE_MUTEX(dpm_list_mtx); |
47 | static pm_message_t pm_transition; | 50 | static pm_message_t pm_transition; |
@@ -476,14 +479,12 @@ End: | |||
476 | */ | 479 | */ |
477 | void dpm_resume_noirq(pm_message_t state) | 480 | void dpm_resume_noirq(pm_message_t state) |
478 | { | 481 | { |
479 | struct list_head list; | ||
480 | ktime_t starttime = ktime_get(); | 482 | ktime_t starttime = ktime_get(); |
481 | 483 | ||
482 | INIT_LIST_HEAD(&list); | ||
483 | mutex_lock(&dpm_list_mtx); | 484 | mutex_lock(&dpm_list_mtx); |
484 | transition_started = false; | 485 | transition_started = false; |
485 | while (!list_empty(&dpm_list)) { | 486 | while (!list_empty(&dpm_noirq_list)) { |
486 | struct device *dev = to_device(dpm_list.next); | 487 | struct device *dev = to_device(dpm_noirq_list.next); |
487 | 488 | ||
488 | get_device(dev); | 489 | get_device(dev); |
489 | if (dev->power.status > DPM_OFF) { | 490 | if (dev->power.status > DPM_OFF) { |
@@ -499,10 +500,9 @@ void dpm_resume_noirq(pm_message_t state) | |||
499 | pm_dev_err(dev, state, " early", error); | 500 | pm_dev_err(dev, state, " early", error); |
500 | } | 501 | } |
501 | if (!list_empty(&dev->power.entry)) | 502 | if (!list_empty(&dev->power.entry)) |
502 | list_move_tail(&dev->power.entry, &list); | 503 | list_move_tail(&dev->power.entry, &dpm_suspended_list); |
503 | put_device(dev); | 504 | put_device(dev); |
504 | } | 505 | } |
505 | list_splice(&list, &dpm_list); | ||
506 | mutex_unlock(&dpm_list_mtx); | 506 | mutex_unlock(&dpm_list_mtx); |
507 | dpm_show_time(starttime, state, "early"); | 507 | dpm_show_time(starttime, state, "early"); |
508 | resume_device_irqs(); | 508 | resume_device_irqs(); |
@@ -611,16 +611,14 @@ static bool is_async(struct device *dev) | |||
611 | */ | 611 | */ |
612 | static void dpm_resume(pm_message_t state) | 612 | static void dpm_resume(pm_message_t state) |
613 | { | 613 | { |
614 | struct list_head list; | ||
615 | struct device *dev; | 614 | struct device *dev; |
616 | ktime_t starttime = ktime_get(); | 615 | ktime_t starttime = ktime_get(); |
617 | 616 | ||
618 | INIT_LIST_HEAD(&list); | ||
619 | mutex_lock(&dpm_list_mtx); | 617 | mutex_lock(&dpm_list_mtx); |
620 | pm_transition = state; | 618 | pm_transition = state; |
621 | async_error = 0; | 619 | async_error = 0; |
622 | 620 | ||
623 | list_for_each_entry(dev, &dpm_list, power.entry) { | 621 | list_for_each_entry(dev, &dpm_suspended_list, power.entry) { |
624 | if (dev->power.status < DPM_OFF) | 622 | if (dev->power.status < DPM_OFF) |
625 | continue; | 623 | continue; |
626 | 624 | ||
@@ -631,8 +629,8 @@ static void dpm_resume(pm_message_t state) | |||
631 | } | 629 | } |
632 | } | 630 | } |
633 | 631 | ||
634 | while (!list_empty(&dpm_list)) { | 632 | while (!list_empty(&dpm_suspended_list)) { |
635 | dev = to_device(dpm_list.next); | 633 | dev = to_device(dpm_suspended_list.next); |
636 | get_device(dev); | 634 | get_device(dev); |
637 | if (dev->power.status >= DPM_OFF && !is_async(dev)) { | 635 | if (dev->power.status >= DPM_OFF && !is_async(dev)) { |
638 | int error; | 636 | int error; |
@@ -644,15 +642,11 @@ static void dpm_resume(pm_message_t state) | |||
644 | mutex_lock(&dpm_list_mtx); | 642 | mutex_lock(&dpm_list_mtx); |
645 | if (error) | 643 | if (error) |
646 | pm_dev_err(dev, state, "", error); | 644 | pm_dev_err(dev, state, "", error); |
647 | } else if (dev->power.status == DPM_SUSPENDING) { | ||
648 | /* Allow new children of the device to be registered */ | ||
649 | dev->power.status = DPM_RESUMING; | ||
650 | } | 645 | } |
651 | if (!list_empty(&dev->power.entry)) | 646 | if (!list_empty(&dev->power.entry)) |
652 | list_move_tail(&dev->power.entry, &list); | 647 | list_move_tail(&dev->power.entry, &dpm_prepared_list); |
653 | put_device(dev); | 648 | put_device(dev); |
654 | } | 649 | } |
655 | list_splice(&list, &dpm_list); | ||
656 | mutex_unlock(&dpm_list_mtx); | 650 | mutex_unlock(&dpm_list_mtx); |
657 | async_synchronize_full(); | 651 | async_synchronize_full(); |
658 | dpm_show_time(starttime, state, NULL); | 652 | dpm_show_time(starttime, state, NULL); |
@@ -699,8 +693,8 @@ static void dpm_complete(pm_message_t state) | |||
699 | INIT_LIST_HEAD(&list); | 693 | INIT_LIST_HEAD(&list); |
700 | mutex_lock(&dpm_list_mtx); | 694 | mutex_lock(&dpm_list_mtx); |
701 | transition_started = false; | 695 | transition_started = false; |
702 | while (!list_empty(&dpm_list)) { | 696 | while (!list_empty(&dpm_prepared_list)) { |
703 | struct device *dev = to_device(dpm_list.prev); | 697 | struct device *dev = to_device(dpm_prepared_list.prev); |
704 | 698 | ||
705 | get_device(dev); | 699 | get_device(dev); |
706 | if (dev->power.status > DPM_ON) { | 700 | if (dev->power.status > DPM_ON) { |
@@ -803,15 +797,13 @@ End: | |||
803 | */ | 797 | */ |
804 | int dpm_suspend_noirq(pm_message_t state) | 798 | int dpm_suspend_noirq(pm_message_t state) |
805 | { | 799 | { |
806 | struct list_head list; | ||
807 | ktime_t starttime = ktime_get(); | 800 | ktime_t starttime = ktime_get(); |
808 | int error = 0; | 801 | int error = 0; |
809 | 802 | ||
810 | INIT_LIST_HEAD(&list); | ||
811 | suspend_device_irqs(); | 803 | suspend_device_irqs(); |
812 | mutex_lock(&dpm_list_mtx); | 804 | mutex_lock(&dpm_list_mtx); |
813 | while (!list_empty(&dpm_list)) { | 805 | while (!list_empty(&dpm_suspended_list)) { |
814 | struct device *dev = to_device(dpm_list.prev); | 806 | struct device *dev = to_device(dpm_suspended_list.prev); |
815 | 807 | ||
816 | get_device(dev); | 808 | get_device(dev); |
817 | mutex_unlock(&dpm_list_mtx); | 809 | mutex_unlock(&dpm_list_mtx); |
@@ -826,10 +818,9 @@ int dpm_suspend_noirq(pm_message_t state) | |||
826 | } | 818 | } |
827 | dev->power.status = DPM_OFF_IRQ; | 819 | dev->power.status = DPM_OFF_IRQ; |
828 | if (!list_empty(&dev->power.entry)) | 820 | if (!list_empty(&dev->power.entry)) |
829 | list_move(&dev->power.entry, &list); | 821 | list_move(&dev->power.entry, &dpm_noirq_list); |
830 | put_device(dev); | 822 | put_device(dev); |
831 | } | 823 | } |
832 | list_splice_tail(&list, &dpm_list); | ||
833 | mutex_unlock(&dpm_list_mtx); | 824 | mutex_unlock(&dpm_list_mtx); |
834 | if (error) | 825 | if (error) |
835 | dpm_resume_noirq(resume_event(state)); | 826 | dpm_resume_noirq(resume_event(state)); |
@@ -957,16 +948,14 @@ static int device_suspend(struct device *dev) | |||
957 | */ | 948 | */ |
958 | static int dpm_suspend(pm_message_t state) | 949 | static int dpm_suspend(pm_message_t state) |
959 | { | 950 | { |
960 | struct list_head list; | ||
961 | ktime_t starttime = ktime_get(); | 951 | ktime_t starttime = ktime_get(); |
962 | int error = 0; | 952 | int error = 0; |
963 | 953 | ||
964 | INIT_LIST_HEAD(&list); | ||
965 | mutex_lock(&dpm_list_mtx); | 954 | mutex_lock(&dpm_list_mtx); |
966 | pm_transition = state; | 955 | pm_transition = state; |
967 | async_error = 0; | 956 | async_error = 0; |
968 | while (!list_empty(&dpm_list)) { | 957 | while (!list_empty(&dpm_prepared_list)) { |
969 | struct device *dev = to_device(dpm_list.prev); | 958 | struct device *dev = to_device(dpm_prepared_list.prev); |
970 | 959 | ||
971 | get_device(dev); | 960 | get_device(dev); |
972 | mutex_unlock(&dpm_list_mtx); | 961 | mutex_unlock(&dpm_list_mtx); |
@@ -980,12 +969,11 @@ static int dpm_suspend(pm_message_t state) | |||
980 | break; | 969 | break; |
981 | } | 970 | } |
982 | if (!list_empty(&dev->power.entry)) | 971 | if (!list_empty(&dev->power.entry)) |
983 | list_move(&dev->power.entry, &list); | 972 | list_move(&dev->power.entry, &dpm_suspended_list); |
984 | put_device(dev); | 973 | put_device(dev); |
985 | if (async_error) | 974 | if (async_error) |
986 | break; | 975 | break; |
987 | } | 976 | } |
988 | list_splice(&list, dpm_list.prev); | ||
989 | mutex_unlock(&dpm_list_mtx); | 977 | mutex_unlock(&dpm_list_mtx); |
990 | async_synchronize_full(); | 978 | async_synchronize_full(); |
991 | if (!error) | 979 | if (!error) |
@@ -1044,10 +1032,8 @@ static int device_prepare(struct device *dev, pm_message_t state) | |||
1044 | */ | 1032 | */ |
1045 | static int dpm_prepare(pm_message_t state) | 1033 | static int dpm_prepare(pm_message_t state) |
1046 | { | 1034 | { |
1047 | struct list_head list; | ||
1048 | int error = 0; | 1035 | int error = 0; |
1049 | 1036 | ||
1050 | INIT_LIST_HEAD(&list); | ||
1051 | mutex_lock(&dpm_list_mtx); | 1037 | mutex_lock(&dpm_list_mtx); |
1052 | transition_started = true; | 1038 | transition_started = true; |
1053 | while (!list_empty(&dpm_list)) { | 1039 | while (!list_empty(&dpm_list)) { |
@@ -1084,10 +1070,9 @@ static int dpm_prepare(pm_message_t state) | |||
1084 | } | 1070 | } |
1085 | dev->power.status = DPM_SUSPENDING; | 1071 | dev->power.status = DPM_SUSPENDING; |
1086 | if (!list_empty(&dev->power.entry)) | 1072 | if (!list_empty(&dev->power.entry)) |
1087 | list_move_tail(&dev->power.entry, &list); | 1073 | list_move_tail(&dev->power.entry, &dpm_prepared_list); |
1088 | put_device(dev); | 1074 | put_device(dev); |
1089 | } | 1075 | } |
1090 | list_splice(&list, &dpm_list); | ||
1091 | mutex_unlock(&dpm_list_mtx); | 1076 | mutex_unlock(&dpm_list_mtx); |
1092 | return error; | 1077 | return error; |
1093 | } | 1078 | } |