diff options
author | Liu, Chuansheng <chuansheng.liu@intel.com> | 2014-02-17 21:28:47 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-02-19 19:30:09 -0500 |
commit | 28b6fd6e37792b16a56d324841bdb20ab78e4522 (patch) | |
tree | 7672d173bf71b4907ea882f9842496ccd6531ff3 | |
parent | 9e5e7910df824ba02aedd2b5d2ca556426ea6d0b (diff) |
PM / sleep: Asynchronous threads for suspend_noirq
In analogy with commits 5af84b82701a and 97df8c12995, using
asynchronous threads can improve the overall suspend_noirq
time significantly.
This patch is for suspend_noirq phase.
Signed-off-by: Chuansheng Liu <chuansheng.liu@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/base/power/main.c | 68 |
1 files changed, 57 insertions, 11 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 6d41165701c4..9335b326d1f5 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -990,14 +990,24 @@ static pm_message_t resume_event(pm_message_t sleep_state) | |||
990 | * The driver of @dev will not receive interrupts while this function is being | 990 | * The driver of @dev will not receive interrupts while this function is being |
991 | * executed. | 991 | * executed. |
992 | */ | 992 | */ |
993 | static int device_suspend_noirq(struct device *dev, pm_message_t state) | 993 | static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async) |
994 | { | 994 | { |
995 | pm_callback_t callback = NULL; | 995 | pm_callback_t callback = NULL; |
996 | char *info = NULL; | 996 | char *info = NULL; |
997 | int error; | 997 | int error = 0; |
998 | |||
999 | if (async_error) | ||
1000 | goto Complete; | ||
1001 | |||
1002 | if (pm_wakeup_pending()) { | ||
1003 | async_error = -EBUSY; | ||
1004 | goto Complete; | ||
1005 | } | ||
998 | 1006 | ||
999 | if (dev->power.syscore) | 1007 | if (dev->power.syscore) |
1000 | return 0; | 1008 | goto Complete; |
1009 | |||
1010 | dpm_wait_for_children(dev, async); | ||
1001 | 1011 | ||
1002 | if (dev->pm_domain) { | 1012 | if (dev->pm_domain) { |
1003 | info = "noirq power domain "; | 1013 | info = "noirq power domain "; |
@@ -1021,10 +1031,40 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) | |||
1021 | error = dpm_run_callback(callback, dev, state, info); | 1031 | error = dpm_run_callback(callback, dev, state, info); |
1022 | if (!error) | 1032 | if (!error) |
1023 | dev->power.is_noirq_suspended = true; | 1033 | dev->power.is_noirq_suspended = true; |
1034 | else | ||
1035 | async_error = error; | ||
1024 | 1036 | ||
1037 | Complete: | ||
1038 | complete_all(&dev->power.completion); | ||
1025 | return error; | 1039 | return error; |
1026 | } | 1040 | } |
1027 | 1041 | ||
1042 | static void async_suspend_noirq(void *data, async_cookie_t cookie) | ||
1043 | { | ||
1044 | struct device *dev = (struct device *)data; | ||
1045 | int error; | ||
1046 | |||
1047 | error = __device_suspend_noirq(dev, pm_transition, true); | ||
1048 | if (error) { | ||
1049 | dpm_save_failed_dev(dev_name(dev)); | ||
1050 | pm_dev_err(dev, pm_transition, " async", error); | ||
1051 | } | ||
1052 | |||
1053 | put_device(dev); | ||
1054 | } | ||
1055 | |||
1056 | static int device_suspend_noirq(struct device *dev) | ||
1057 | { | ||
1058 | reinit_completion(&dev->power.completion); | ||
1059 | |||
1060 | if (pm_async_enabled && dev->power.async_suspend) { | ||
1061 | get_device(dev); | ||
1062 | async_schedule(async_suspend_noirq, dev); | ||
1063 | return 0; | ||
1064 | } | ||
1065 | return __device_suspend_noirq(dev, pm_transition, false); | ||
1066 | } | ||
1067 | |||
1028 | /** | 1068 | /** |
1029 | * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices. | 1069 | * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices. |
1030 | * @state: PM transition of the system being carried out. | 1070 | * @state: PM transition of the system being carried out. |
@@ -1040,19 +1080,20 @@ static int dpm_suspend_noirq(pm_message_t state) | |||
1040 | cpuidle_pause(); | 1080 | cpuidle_pause(); |
1041 | suspend_device_irqs(); | 1081 | suspend_device_irqs(); |
1042 | mutex_lock(&dpm_list_mtx); | 1082 | mutex_lock(&dpm_list_mtx); |
1083 | pm_transition = state; | ||
1084 | async_error = 0; | ||
1085 | |||
1043 | while (!list_empty(&dpm_late_early_list)) { | 1086 | while (!list_empty(&dpm_late_early_list)) { |
1044 | struct device *dev = to_device(dpm_late_early_list.prev); | 1087 | struct device *dev = to_device(dpm_late_early_list.prev); |
1045 | 1088 | ||
1046 | get_device(dev); | 1089 | get_device(dev); |
1047 | mutex_unlock(&dpm_list_mtx); | 1090 | mutex_unlock(&dpm_list_mtx); |
1048 | 1091 | ||
1049 | error = device_suspend_noirq(dev, state); | 1092 | error = device_suspend_noirq(dev); |
1050 | 1093 | ||
1051 | mutex_lock(&dpm_list_mtx); | 1094 | mutex_lock(&dpm_list_mtx); |
1052 | if (error) { | 1095 | if (error) { |
1053 | pm_dev_err(dev, state, " noirq", error); | 1096 | pm_dev_err(dev, state, " noirq", error); |
1054 | suspend_stats.failed_suspend_noirq++; | ||
1055 | dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ); | ||
1056 | dpm_save_failed_dev(dev_name(dev)); | 1097 | dpm_save_failed_dev(dev_name(dev)); |
1057 | put_device(dev); | 1098 | put_device(dev); |
1058 | break; | 1099 | break; |
@@ -1061,16 +1102,21 @@ static int dpm_suspend_noirq(pm_message_t state) | |||
1061 | list_move(&dev->power.entry, &dpm_noirq_list); | 1102 | list_move(&dev->power.entry, &dpm_noirq_list); |
1062 | put_device(dev); | 1103 | put_device(dev); |
1063 | 1104 | ||
1064 | if (pm_wakeup_pending()) { | 1105 | if (async_error) |
1065 | error = -EBUSY; | ||
1066 | break; | 1106 | break; |
1067 | } | ||
1068 | } | 1107 | } |
1069 | mutex_unlock(&dpm_list_mtx); | 1108 | mutex_unlock(&dpm_list_mtx); |
1070 | if (error) | 1109 | async_synchronize_full(); |
1110 | if (!error) | ||
1111 | error = async_error; | ||
1112 | |||
1113 | if (error) { | ||
1114 | suspend_stats.failed_suspend_noirq++; | ||
1115 | dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ); | ||
1071 | dpm_resume_noirq(resume_event(state)); | 1116 | dpm_resume_noirq(resume_event(state)); |
1072 | else | 1117 | } else { |
1073 | dpm_show_time(starttime, state, "noirq"); | 1118 | dpm_show_time(starttime, state, "noirq"); |
1119 | } | ||
1074 | return error; | 1120 | return error; |
1075 | } | 1121 | } |
1076 | 1122 | ||