aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power
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 /kernel/power
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>
Diffstat (limited to 'kernel/power')
-rw-r--r--kernel/power/main.c102
-rw-r--r--kernel/power/suspend.c17
2 files changed, 116 insertions, 3 deletions
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);