diff options
-rw-r--r-- | drivers/base/power/main.c | 39 | ||||
-rw-r--r-- | drivers/base/power/power.h | 4 | ||||
-rw-r--r-- | kernel/power/Kconfig | 25 | ||||
-rw-r--r-- | kernel/power/main.c | 36 |
4 files changed, 103 insertions, 1 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 06f09bf89cb2..f1e1a5b8fb04 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -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; diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index f2a25f18fde7..51b585a4e9d9 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -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 */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 87f4d24b55b0..c1a820a36861 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -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 diff --git a/kernel/power/main.c b/kernel/power/main.c index 2981af4ce7cb..b7d0e301e43b 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -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, |