]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00133978 PM: add time sensitive debug function to suspend & resume
authorZhang Jiejing <jiejing.zhang@freescale.com>
Mon, 22 Nov 2010 09:14:32 +0000 (17:14 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:32:30 +0000 (08:32 +0200)
There was some driver is slow on suspend/resume,
but some embeded system like eReader,Cellphone
are time sensitive,this commit will report the slow
driver on suspend/resume, the default value is 500us(0.5ms)

Also, the threshold can be change by modify
'/sys/power/device_suspend_time_threshold' to change the threshold,
it is in microsecond.

The output is like:

PM: device platform:soc-audio.2 suspend too slow, takes          606.696 msecs
PM: device platform:mxc_sdc_fb.1 suspend too slow, takes         7.708 msecs

the default state of suspend driver is default off,
if you want to debug the suspend time, echo time in
microsecond(u Second) to /sys/powe/device_suspend_time_threshold

eg: I want to know which driver suspend & resume takes
more that 0.5 ms (500 us), you can just :

ehco 500 > /sys/power/device_suspend_time_threshold

Signed-off-by: Zhang Jiejing <jiejing.zhang@freescale.com>
drivers/base/power/main.c
drivers/base/power/power.h
kernel/power/Kconfig
kernel/power/main.c

index 06f09bf89cb2bab3319d800ca2dd7939dfb59a3b..f1e1a5b8fb040462cb6d9e0c0f9e11b330b45f48 100644 (file)
@@ -179,6 +179,39 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
                        error, (unsigned long long)ktime_to_ns(delta) >> 10);
        }
 }
+#ifdef CONFIG_SUSPEND_DEVICE_TIME_DEBUG
+static void suspend_time_debug_start(ktime_t *start)
+{
+       *start = ktime_get();
+}
+
+static void suspend_time_debug_report(const char *name, struct device *dev,
+                                     ktime_t starttime)
+{
+       ktime_t rettime;
+       s64 usecs64;
+       int usecs;
+
+       if (!dev->driver)
+               return;
+
+       rettime = ktime_get();
+       usecs64 = ktime_to_us(ktime_sub(rettime, starttime));
+       usecs = usecs64;
+       if (usecs == 0)
+               usecs = 1;
+
+       if (device_suspend_time_threshold
+           && usecs > device_suspend_time_threshold)
+               pr_info("PM: device %s:%s %s too slow, it takes \t %ld.%03ld msecs\n",
+                       dev->bus->name, dev_name(dev), name,
+                       usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC);
+}
+#else
+static void suspend_time_debug_start(ktime_t *start) {}
+static void suspend_time_debug_report(const char *name, struct device *dev,
+                                     ktime_t starttime) {}
+#endif /* CONFIG_SUSPEND_DEVICE_TIME_DEBUG */
 
 /**
  * dpm_wait - Wait for a PM operation to complete.
@@ -216,7 +249,7 @@ static int pm_op(struct device *dev,
                 pm_message_t state)
 {
        int error = 0;
-       ktime_t calltime;
+       ktime_t calltime, starttime;
 
        calltime = initcall_debug_start(dev);
 
@@ -224,13 +257,17 @@ static int pm_op(struct device *dev,
 #ifdef CONFIG_SUSPEND
        case PM_EVENT_SUSPEND:
                if (ops->suspend) {
+                       suspend_time_debug_start(&starttime);
                        error = ops->suspend(dev);
+                       suspend_time_debug_report("suspend", dev, starttime);
                        suspend_report_result(ops->suspend, error);
                }
                break;
        case PM_EVENT_RESUME:
                if (ops->resume) {
+                       suspend_time_debug_start(&starttime);
                        error = ops->resume(dev);
+                       suspend_time_debug_report("resume", dev, starttime);
                        suspend_report_result(ops->resume, error);
                }
                break;
index f2a25f18fde7caaa3a1949c893afae3c872b76c3..51b585a4e9d954303fb5abe575b10f53e2d88027 100644 (file)
@@ -10,6 +10,10 @@ static inline void pm_runtime_remove(struct device *dev) {}
 
 #endif /* !CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_SUSPEND_DEVICE_TIME_DEBUG
+extern int device_suspend_time_threshold;
+#endif
+
 #ifdef CONFIG_PM_SLEEP
 
 /* kernel/power/main.c */
index 87f4d24b55b02872388661fad0e4a51757a4fc76..c1a820a36861c51385e07493af6b5b38714995db 100644 (file)
@@ -7,6 +7,31 @@ config SUSPEND
          powered and thus its contents are preserved, such as the
          suspend-to-RAM state (e.g. the ACPI S3 state).
 
+config PM_TEST_SUSPEND
+       bool "Test suspend/resume and wakealarm during bootup"
+       depends on SUSPEND && PM_DEBUG && RTC_CLASS=y
+       ---help---
+       This option will let you suspend your machine during bootup, and
+       make it wake up a few seconds later using an RTC wakeup alarm.
+       Enable this with a kernel parameter like "test_suspend=mem".
+
+       You probably want to have your system's RTC driver statically
+       linked, ensuring that it's available when this test runs.
+
+config SUSPEND_DEVICE_TIME_DEBUG
+        bool "Warnning device suspend/resume takes too much time"
+       depends on SUSPEND && PM_DEBUG
+       default n
+       ---help---
+       This option will enable a timing function to check device
+        suspend time consumption, If the device takes more time that
+        the threshold(default 0.5 ms), it will print the device and
+        bus name on the console.  You can change the threshold
+        on-the-fly by modify /sys/power/time_threshold the time unit
+        is in microsecond.
+
+       This options only for debug proprose, If in doubt, say N.
+
 config SUSPEND_FREEZER
        bool "Enable freezer for suspend to RAM/standby" \
                if ARCH_WANTS_FREEZER_CONTROL || BROKEN
index 2981af4ce7cbbce49314e2fb2fef9fa13496f41e..b7d0e301e43b5b08bf71baef5090ada6f782b192 100644 (file)
@@ -297,12 +297,48 @@ power_attr(pm_trace_dev_match);
 
 #endif /* CONFIG_PM_TRACE */
 
+#ifdef CONFIG_SUSPEND_DEVICE_TIME_DEBUG
+/*
+ * threshold of device suspend time consumption in microsecond(0.5ms), the
+ * driver suspend/resume time longer than this threshold will be
+ * print to console, 0 to disable */
+int device_suspend_time_threshold;
+
+static ssize_t
+device_suspend_time_threshold_show(struct kobject *kobj,
+                                  struct kobj_attribute *attr, char *buf)
+{
+       if (device_suspend_time_threshold == 0)
+               return sprintf(buf, "off\n");
+       else
+               return sprintf(buf, "%d usecs\n",
+                              device_suspend_time_threshold);
+}
+
+static ssize_t
+device_suspend_time_threshold_store(struct kobject *kobj,
+                                   struct kobj_attribute *attr,
+                                   const char *buf, size_t n)
+{
+       int val;
+       if (sscanf(buf, "%d", &val) > 0) {
+               device_suspend_time_threshold = val;
+               return n;
+       }
+       return -EINVAL;
+}
+power_attr(device_suspend_time_threshold);
+#endif
+
 static struct attribute * g[] = {
        &state_attr.attr,
 #ifdef CONFIG_PM_TRACE
        &pm_trace_attr.attr,
        &pm_trace_dev_match_attr.attr,
 #endif
+#ifdef CONFIG_SUSPEND_DEVICE_TIME_DEBUG
+       &device_suspend_time_threshold_attr.attr,
+#endif
 #ifdef CONFIG_PM_SLEEP
        &pm_async_attr.attr,
        &wakeup_count_attr.attr,