From a906b0b5b4ff3414ceb9fc7a69a3d7c9d66e46b1 Mon Sep 17 00:00:00 2001
From: ShuoX Liu <shuox.liu@xxxxxxxxx>
Date: Thu, 28 Jul 2011 10:54:22 +0800
Subject: [PATCH] PM: add statistics sysfs file for suspend to ram.
Record S3 failure time about each reason and the latest two failed
devices' name in S3 progress.
We can check it through /sys/power/suspend_stats.
Change-Id: Ieed7fd74e27d3b482675a20cb0bb26b9054a1624
Signed-off-by: ShuoX Liu <shuox.liu@xxxxxxxxx>
---
drivers/base/power/main.c | 31 +++++++++++++++++++++++++--
include/linux/suspend.h | 16 ++++++++++++++
kernel/power/main.c | 49 +++++++++++++++++++++++++++++++++++++++++++++
kernel/power/suspend.c | 13 ++++++++++-
4 files changed, 104 insertions(+), 5 deletions(-)
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index a854591..da1c561 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -46,6 +46,7 @@ LIST_HEAD(dpm_prepared_list);
LIST_HEAD(dpm_suspended_list);
LIST_HEAD(dpm_noirq_list);
+struct suspend_stats suspend_stats;
static DEFINE_MUTEX(dpm_list_mtx);
static pm_message_t pm_transition;
@@ -180,6 +181,15 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
}
}
+static void dpm_save_dev_name(const char *name)
+{
+ strlcpy(suspend_stats.failed_devs[suspend_stats.last_failed],
+ name,
+ sizeof(suspend_stats.failed_devs[0]));
+ suspend_stats.last_failed++;
+ suspend_stats.last_failed %= REC_FAILED_DEV_NUM;
+}
+
/**
* dpm_wait - Wait for a PM operation to complete.
* @dev: Device to wait for.
@@ -464,8 +474,11 @@ void dpm_resume_noirq(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
error = device_resume_noirq(dev, state);
- if (error)
+ if (error) {
+ suspend_stats.failed_resume_noirq++;
+ dpm_save_dev_name(dev_name(dev));
pm_dev_err(dev, state, " early", error);
+ }
mutex_lock(&dpm_list_mtx);
put_device(dev);
@@ -626,8 +639,11 @@ void dpm_resume(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
error = device_resume(dev, state, false);
- if (error)
+ if (error) {
+ suspend_stats.failed_resume++;
+ dpm_save_dev_name(dev_name(dev));
pm_dev_err(dev, state, "", error);
+ }
mutex_lock(&dpm_list_mtx);
}
@@ -802,6 +818,8 @@ int dpm_suspend_noirq(pm_message_t state)
mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, " late", error);
+ suspend_stats.failed_suspend_noirq++;
+ dpm_save_dev_name(dev_name(dev));
put_device(dev);
break;
}
@@ -923,8 +941,10 @@ static void async_suspend(void *data, async_cookie_t cookie)
int error;
error = __device_suspend(dev, pm_transition, true);
- if (error)
+ if (error) {
+ dpm_save_dev_name(dev_name(dev));
pm_dev_err(dev, pm_transition, " async", error);
+ }
put_device(dev);
}
@@ -967,6 +987,7 @@ int dpm_suspend(pm_message_t state)
mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, "", error);
+ dpm_save_dev_name(dev_name(dev));
put_device(dev);
break;
}
@@ -982,6 +1003,8 @@ int dpm_suspend(pm_message_t state)
error = async_error;
if (!error)
dpm_show_time(starttime, state, NULL);
+ else
+ suspend_stats.failed_suspend++;
return error;
}
@@ -1090,6 +1113,8 @@ int dpm_suspend_start(pm_message_t state)
error = dpm_prepare(state);
if (!error)
error = dpm_suspend(state);
+ else
+ suspend_stats.failed_prepare++;
return error;
}
EXPORT_SYMBOL_GPL(dpm_suspend_start);
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 6bbcef2..6a8ff23 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -34,6 +34,22 @@ typedef int __bitwise suspend_state_t;
#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)
#define PM_SUSPEND_MAX ((__force suspend_state_t) 4)
+struct suspend_stats {
+ int success;
+ int fail;
+ int failed_freeze;
+ int failed_prepare;
+ int failed_suspend;
+ int failed_suspend_noirq;
+ int failed_resume;
+ int failed_resume_noirq;
+#define REC_FAILED_DEV_NUM 2
+ char failed_devs[REC_FAILED_DEV_NUM][40];
+ int last_failed;
+};
+
+extern struct suspend_stats suspend_stats;
+
/**
* struct platform_suspend_ops - Callbacks for managing platform dependent
* system sleep states.
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 6c601f8..32eb67b 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -133,6 +133,50 @@ power_attr(pm_test);
#endif /* CONFIG_PM_SLEEP */
+static ssize_t suspend_stats_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i, index, last_index;
+ char *s = buf;
+
+ last_index = suspend_stats.last_failed + REC_FAILED_DEV_NUM - 1;
+ last_index %= REC_FAILED_DEV_NUM;
+ s += sprintf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n"
+ "%s: %d\n%s: %d\n%s: %d\n%s: %d\n",
+ "success", suspend_stats.success,
+ "fail", suspend_stats.fail,
+ "failed_freeze", suspend_stats.failed_freeze,
+ "failed_prepare", suspend_stats.failed_prepare,
+ "failed_suspend", suspend_stats.failed_suspend,
+ "failed_suspend_noirq",
+ suspend_stats.failed_suspend_noirq,
+ "failed_resume", suspend_stats.failed_resume,
+ "failed_resume_noirq",
+ suspend_stats.failed_resume_noirq);
+ s += sprintf(s, "failed_devs:\n last_failed:\t%s\n",
+ suspend_stats.failed_devs[last_index]);
+ for (i = 1; i < REC_FAILED_DEV_NUM; i++) {
+ index = last_index + REC_FAILED_DEV_NUM - i;
+ index %= REC_FAILED_DEV_NUM;
+ s += sprintf(s, "\t\t%s\n",
+ suspend_stats.failed_devs[index]);
+ }
+
+ if (s != buf)
+ /* convert the last space to a newline */
+ *(s-1) = '\n';
+
+ return s - buf;
+}
+
+static ssize_t suspend_stats_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ return n;
+}
+
+power_attr(suspend_stats);
+
struct kobject *power_kobj;
/**
@@ -194,6 +238,10 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
}
if (state < PM_SUSPEND_MAX && *s)
error = enter_state(state);
+ if (error)
+ suspend_stats.fail++;
+ else
+ suspend_stats.success++;
#endif
Exit:
@@ -310,6 +358,7 @@ static struct attribute * g[] = {
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr,
#endif
+ &suspend_stats_attr.attr,
#endif
NULL,
};
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index b6b71ad..9bb4281 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -106,6 +106,8 @@ static int suspend_prepare(void)
error = suspend_freeze_processes();
if (!error)
return 0;
+ else
+ suspend_stats.failed_freeze++;
suspend_thaw_processes();
usermodehelper_enable();
@@ -315,8 +317,15 @@ int enter_state(suspend_state_t state)
*/
int pm_suspend(suspend_state_t state)
{
- if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
- return enter_state(state);
+ int ret;
+ if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX) {
+ ret = enter_state(state);
+ if (ret)
+ suspend_stats.fail++;
+ else
+ suspend_stats.success++;
+ return ret;
+ }
return -EINVAL;
}
EXPORT_SYMBOL(pm_suspend);
--
1.7.1