aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShuoX Liu <shuox.liu@intel.com>2011-08-10 17:01:26 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2011-10-16 17:27:45 -0400
commit2a77c46de1e3dace73745015635ebbc648eca69c (patch)
treea801ed6ba5623c7d963377906776ade6f02eea0a
parent9bd717c0dc8224cadfd66df7eeff98c987711d98 (diff)
PM / Suspend: Add statistics debugfs file for suspend to RAM
Record S3 failure time about each reason and the latest two failed devices' names in S3 progress. We can check it through 'suspend_stats' entry in debugfs. The motivation of the patch: We are enabling power features on Medfield. Comparing with PC/notebook, a mobile enters/exits suspend-2-ram (we call it s3 on Medfield) far more frequently. If it can't enter suspend-2-ram in time, the power might be used up soon. We often find sometimes, a device suspend fails. Then, system retries s3 over and over again. As display is off, testers and developers don't know what happens. Some testers and developers complain they don't know if system tries suspend-2-ram, and what device fails to suspend. They need such info for a quick check. The patch adds suspend_stats under debugfs for users to check suspend to RAM statistics quickly. If not using this patch, we have other methods to get info about what device fails. One is to turn on CONFIG_PM_DEBUG, but users would get too much info and testers need recompile the system. In addition, dynamic debug is another good tool to dump debug info. But it still doesn't match our utilization scenario closely. 1) user need write a user space parser to process the syslog output; 2) Our testing scenario is we leave the mobile for at least hours. Then, check its status. No serial console available during the testing. One is because console would be suspended, and the other is serial console connecting with spi or HSU devices would consume power. These devices are powered off at suspend-2-ram. Signed-off-by: ShuoX Liu <shuox.liu@intel.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r--Documentation/power/basic-pm-debugging.txt24
-rw-r--r--drivers/base/power/main.c31
-rw-r--r--include/linux/suspend.h52
-rw-r--r--kernel/power/main.c102
-rw-r--r--kernel/power/suspend.c17
5 files changed, 218 insertions, 8 deletions
diff --git a/Documentation/power/basic-pm-debugging.txt b/Documentation/power/basic-pm-debugging.txt
index ddd78172ef73..62eca080a71b 100644
--- a/Documentation/power/basic-pm-debugging.txt
+++ b/Documentation/power/basic-pm-debugging.txt
@@ -201,3 +201,27 @@ case, you may be able to search for failing drivers by following the procedure
201analogous to the one described in section 1. If you find some failing drivers, 201analogous to the one described in section 1. If you find some failing drivers,
202you will have to unload them every time before an STR transition (ie. before 202you will have to unload them every time before an STR transition (ie. before
203you run s2ram), and please report the problems with them. 203you run s2ram), and please report the problems with them.
204
205There is a debugfs entry which shows the suspend to RAM statistics. Here is an
206example of its output.
207 # mount -t debugfs none /sys/kernel/debug
208 # cat /sys/kernel/debug/suspend_stats
209 success: 20
210 fail: 5
211 failed_freeze: 0
212 failed_prepare: 0
213 failed_suspend: 5
214 failed_suspend_noirq: 0
215 failed_resume: 0
216 failed_resume_noirq: 0
217 failures:
218 last_failed_dev: alarm
219 adc
220 last_failed_errno: -16
221 -16
222 last_failed_step: suspend
223 suspend
224Field success means the success number of suspend to RAM, and field fail means
225the failure number. Others are the failure number of different steps of suspend
226to RAM. suspend_stats just lists the last 2 failed devices, error number and
227failed step of suspend.
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index c6291ab725a3..b1b58260b4ff 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -46,6 +46,7 @@ LIST_HEAD(dpm_prepared_list);
46LIST_HEAD(dpm_suspended_list); 46LIST_HEAD(dpm_suspended_list);
47LIST_HEAD(dpm_noirq_list); 47LIST_HEAD(dpm_noirq_list);
48 48
49struct suspend_stats suspend_stats;
49static DEFINE_MUTEX(dpm_list_mtx); 50static DEFINE_MUTEX(dpm_list_mtx);
50static pm_message_t pm_transition; 51static pm_message_t pm_transition;
51 52
@@ -467,8 +468,12 @@ void dpm_resume_noirq(pm_message_t state)
467 mutex_unlock(&dpm_list_mtx); 468 mutex_unlock(&dpm_list_mtx);
468 469
469 error = device_resume_noirq(dev, state); 470 error = device_resume_noirq(dev, state);
470 if (error) 471 if (error) {
472 suspend_stats.failed_resume_noirq++;
473 dpm_save_failed_step(SUSPEND_RESUME_NOIRQ);
474 dpm_save_failed_dev(dev_name(dev));
471 pm_dev_err(dev, state, " early", error); 475 pm_dev_err(dev, state, " early", error);
476 }
472 477
473 mutex_lock(&dpm_list_mtx); 478 mutex_lock(&dpm_list_mtx);
474 put_device(dev); 479 put_device(dev);
@@ -629,8 +634,12 @@ void dpm_resume(pm_message_t state)
629 mutex_unlock(&dpm_list_mtx); 634 mutex_unlock(&dpm_list_mtx);
630 635
631 error = device_resume(dev, state, false); 636 error = device_resume(dev, state, false);
632 if (error) 637 if (error) {
638 suspend_stats.failed_resume++;
639 dpm_save_failed_step(SUSPEND_RESUME);
640 dpm_save_failed_dev(dev_name(dev));
633 pm_dev_err(dev, state, "", error); 641 pm_dev_err(dev, state, "", error);
642 }
634 643
635 mutex_lock(&dpm_list_mtx); 644 mutex_lock(&dpm_list_mtx);
636 } 645 }
@@ -805,6 +814,9 @@ int dpm_suspend_noirq(pm_message_t state)
805 mutex_lock(&dpm_list_mtx); 814 mutex_lock(&dpm_list_mtx);
806 if (error) { 815 if (error) {
807 pm_dev_err(dev, state, " late", error); 816 pm_dev_err(dev, state, " late", error);
817 suspend_stats.failed_suspend_noirq++;
818 dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
819 dpm_save_failed_dev(dev_name(dev));
808 put_device(dev); 820 put_device(dev);
809 break; 821 break;
810 } 822 }
@@ -926,8 +938,10 @@ static void async_suspend(void *data, async_cookie_t cookie)
926 int error; 938 int error;
927 939
928 error = __device_suspend(dev, pm_transition, true); 940 error = __device_suspend(dev, pm_transition, true);
929 if (error) 941 if (error) {
942 dpm_save_failed_dev(dev_name(dev));
930 pm_dev_err(dev, pm_transition, " async", error); 943 pm_dev_err(dev, pm_transition, " async", error);
944 }
931 945
932 put_device(dev); 946 put_device(dev);
933} 947}
@@ -970,6 +984,7 @@ int dpm_suspend(pm_message_t state)
970 mutex_lock(&dpm_list_mtx); 984 mutex_lock(&dpm_list_mtx);
971 if (error) { 985 if (error) {
972 pm_dev_err(dev, state, "", error); 986 pm_dev_err(dev, state, "", error);
987 dpm_save_failed_dev(dev_name(dev));
973 put_device(dev); 988 put_device(dev);
974 break; 989 break;
975 } 990 }
@@ -983,7 +998,10 @@ int dpm_suspend(pm_message_t state)
983 async_synchronize_full(); 998 async_synchronize_full();
984 if (!error) 999 if (!error)
985 error = async_error; 1000 error = async_error;
986 if (!error) 1001 if (error) {
1002 suspend_stats.failed_suspend++;
1003 dpm_save_failed_step(SUSPEND_SUSPEND);
1004 } else
987 dpm_show_time(starttime, state, NULL); 1005 dpm_show_time(starttime, state, NULL);
988 return error; 1006 return error;
989} 1007}
@@ -1091,7 +1109,10 @@ int dpm_suspend_start(pm_message_t state)
1091 int error; 1109 int error;
1092 1110
1093 error = dpm_prepare(state); 1111 error = dpm_prepare(state);
1094 if (!error) 1112 if (error) {
1113 suspend_stats.failed_prepare++;
1114 dpm_save_failed_step(SUSPEND_PREPARE);
1115 } else
1095 error = dpm_suspend(state); 1116 error = dpm_suspend(state);
1096 return error; 1117 return error;
1097} 1118}
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 6bbcef22e105..76f42e49b72d 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -34,6 +34,58 @@ typedef int __bitwise suspend_state_t;
34#define PM_SUSPEND_MEM ((__force suspend_state_t) 3) 34#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)
35#define PM_SUSPEND_MAX ((__force suspend_state_t) 4) 35#define PM_SUSPEND_MAX ((__force suspend_state_t) 4)
36 36
37enum suspend_stat_step {
38 SUSPEND_FREEZE = 1,
39 SUSPEND_PREPARE,
40 SUSPEND_SUSPEND,
41 SUSPEND_SUSPEND_NOIRQ,
42 SUSPEND_RESUME_NOIRQ,
43 SUSPEND_RESUME
44};
45
46struct suspend_stats {
47 int success;
48 int fail;
49 int failed_freeze;
50 int failed_prepare;
51 int failed_suspend;
52 int failed_suspend_noirq;
53 int failed_resume;
54 int failed_resume_noirq;
55#define REC_FAILED_NUM 2
56 int last_failed_dev;
57 char failed_devs[REC_FAILED_NUM][40];
58 int last_failed_errno;
59 int errno[REC_FAILED_NUM];
60 int last_failed_step;
61 enum suspend_stat_step failed_steps[REC_FAILED_NUM];
62};
63
64extern struct suspend_stats suspend_stats;
65
66static inline void dpm_save_failed_dev(const char *name)
67{
68 strlcpy(suspend_stats.failed_devs[suspend_stats.last_failed_dev],
69 name,
70 sizeof(suspend_stats.failed_devs[0]));
71 suspend_stats.last_failed_dev++;
72 suspend_stats.last_failed_dev %= REC_FAILED_NUM;
73}
74
75static inline void dpm_save_failed_errno(int err)
76{
77 suspend_stats.errno[suspend_stats.last_failed_errno] = err;
78 suspend_stats.last_failed_errno++;
79 suspend_stats.last_failed_errno %= REC_FAILED_NUM;
80}
81
82static inline void dpm_save_failed_step(enum suspend_stat_step step)
83{
84 suspend_stats.failed_steps[suspend_stats.last_failed_step] = step;
85 suspend_stats.last_failed_step++;
86 suspend_stats.last_failed_step %= REC_FAILED_NUM;
87}
88
37/** 89/**
38 * struct platform_suspend_ops - Callbacks for managing platform dependent 90 * struct platform_suspend_ops - Callbacks for managing platform dependent
39 * system sleep states. 91 * system sleep states.
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 6c601f871964..2757acba8e8a 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -12,6 +12,8 @@
12#include <linux/string.h> 12#include <linux/string.h>
13#include <linux/resume-trace.h> 13#include <linux/resume-trace.h>
14#include <linux/workqueue.h> 14#include <linux/workqueue.h>
15#include <linux/debugfs.h>
16#include <linux/seq_file.h>
15 17
16#include "power.h" 18#include "power.h"
17 19
@@ -133,6 +135,101 @@ power_attr(pm_test);
133 135
134#endif /* CONFIG_PM_SLEEP */ 136#endif /* CONFIG_PM_SLEEP */
135 137
138#ifdef CONFIG_DEBUG_FS
139static char *suspend_step_name(enum suspend_stat_step step)
140{
141 switch (step) {
142 case SUSPEND_FREEZE:
143 return "freeze";
144 case SUSPEND_PREPARE:
145 return "prepare";
146 case SUSPEND_SUSPEND:
147 return "suspend";
148 case SUSPEND_SUSPEND_NOIRQ:
149 return "suspend_noirq";
150 case SUSPEND_RESUME_NOIRQ:
151 return "resume_noirq";
152 case SUSPEND_RESUME:
153 return "resume";
154 default:
155 return "";
156 }
157}
158
159static int suspend_stats_show(struct seq_file *s, void *unused)
160{
161 int i, index, last_dev, last_errno, last_step;
162
163 last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
164 last_dev %= REC_FAILED_NUM;
165 last_errno = suspend_stats.last_failed_errno + REC_FAILED_NUM - 1;
166 last_errno %= REC_FAILED_NUM;
167 last_step = suspend_stats.last_failed_step + REC_FAILED_NUM - 1;
168 last_step %= REC_FAILED_NUM;
169 seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n"
170 "%s: %d\n%s: %d\n%s: %d\n%s: %d\n",
171 "success", suspend_stats.success,
172 "fail", suspend_stats.fail,
173 "failed_freeze", suspend_stats.failed_freeze,
174 "failed_prepare", suspend_stats.failed_prepare,
175 "failed_suspend", suspend_stats.failed_suspend,
176 "failed_suspend_noirq",
177 suspend_stats.failed_suspend_noirq,
178 "failed_resume", suspend_stats.failed_resume,
179 "failed_resume_noirq",
180 suspend_stats.failed_resume_noirq);
181 seq_printf(s, "failures:\n last_failed_dev:\t%-s\n",
182 suspend_stats.failed_devs[last_dev]);
183 for (i = 1; i < REC_FAILED_NUM; i++) {
184 index = last_dev + REC_FAILED_NUM - i;
185 index %= REC_FAILED_NUM;
186 seq_printf(s, "\t\t\t%-s\n",
187 suspend_stats.failed_devs[index]);
188 }
189 seq_printf(s, " last_failed_errno:\t%-d\n",
190 suspend_stats.errno[last_errno]);
191 for (i = 1; i < REC_FAILED_NUM; i++) {
192 index = last_errno + REC_FAILED_NUM - i;
193 index %= REC_FAILED_NUM;
194 seq_printf(s, "\t\t\t%-d\n",
195 suspend_stats.errno[index]);
196 }
197 seq_printf(s, " last_failed_step:\t%-s\n",
198 suspend_step_name(
199 suspend_stats.failed_steps[last_step]));
200 for (i = 1; i < REC_FAILED_NUM; i++) {
201 index = last_step + REC_FAILED_NUM - i;
202 index %= REC_FAILED_NUM;
203 seq_printf(s, "\t\t\t%-s\n",
204 suspend_step_name(
205 suspend_stats.failed_steps[index]));
206 }
207
208 return 0;
209}
210
211static int suspend_stats_open(struct inode *inode, struct file *file)
212{
213 return single_open(file, suspend_stats_show, NULL);
214}
215
216static const struct file_operations suspend_stats_operations = {
217 .open = suspend_stats_open,
218 .read = seq_read,
219 .llseek = seq_lseek,
220 .release = single_release,
221};
222
223static int __init pm_debugfs_init(void)
224{
225 debugfs_create_file("suspend_stats", S_IFREG | S_IRUGO,
226 NULL, NULL, &suspend_stats_operations);
227 return 0;
228}
229
230late_initcall(pm_debugfs_init);
231#endif /* CONFIG_DEBUG_FS */
232
136struct kobject *power_kobj; 233struct kobject *power_kobj;
137 234
138/** 235/**
@@ -194,6 +291,11 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
194 } 291 }
195 if (state < PM_SUSPEND_MAX && *s) 292 if (state < PM_SUSPEND_MAX && *s)
196 error = enter_state(state); 293 error = enter_state(state);
294 if (error) {
295 suspend_stats.fail++;
296 dpm_save_failed_errno(error);
297 } else
298 suspend_stats.success++;
197#endif 299#endif
198 300
199 Exit: 301 Exit:
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index b6b71ad2208f..595a3dd56a8a 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -104,7 +104,10 @@ static int suspend_prepare(void)
104 goto Finish; 104 goto Finish;
105 105
106 error = suspend_freeze_processes(); 106 error = suspend_freeze_processes();
107 if (!error) 107 if (error) {
108 suspend_stats.failed_freeze++;
109 dpm_save_failed_step(SUSPEND_FREEZE);
110 } else
108 return 0; 111 return 0;
109 112
110 suspend_thaw_processes(); 113 suspend_thaw_processes();
@@ -315,8 +318,16 @@ int enter_state(suspend_state_t state)
315 */ 318 */
316int pm_suspend(suspend_state_t state) 319int pm_suspend(suspend_state_t state)
317{ 320{
318 if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX) 321 int ret;
319 return enter_state(state); 322 if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX) {
323 ret = enter_state(state);
324 if (ret) {
325 suspend_stats.fail++;
326 dpm_save_failed_errno(ret);
327 } else
328 suspend_stats.success++;
329 return ret;
330 }
320 return -EINVAL; 331 return -EINVAL;
321} 332}
322EXPORT_SYMBOL(pm_suspend); 333EXPORT_SYMBOL(pm_suspend);