Merge back earlier suspend/hibernate material for v4.1.

This commit is contained in:
Rafael J. Wysocki 2015-04-10 12:01:59 +02:00
commit be77002101
9 changed files with 106 additions and 22 deletions

View file

@ -3462,6 +3462,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
improve throughput, but will also increase the improve throughput, but will also increase the
amount of memory reserved for use by the client. amount of memory reserved for use by the client.
suspend.pm_test_delay=
[SUSPEND]
Sets the number of seconds to remain in a suspend test
mode before resuming the system (see
/sys/power/pm_test). Only available when CONFIG_PM_DEBUG
is set. Default value is 5.
swapaccount=[0|1] swapaccount=[0|1]
[KNL] Enable accounting of swap in memory resource [KNL] Enable accounting of swap in memory resource
controller if no parameter or 1 is given or disable controller if no parameter or 1 is given or disable

View file

@ -75,12 +75,14 @@ you should do the following:
# echo platform > /sys/power/disk # echo platform > /sys/power/disk
# echo disk > /sys/power/state # echo disk > /sys/power/state
Then, the kernel will try to freeze processes, suspend devices, wait 5 seconds, Then, the kernel will try to freeze processes, suspend devices, wait a few
resume devices and thaw processes. If "platform" is written to seconds (5 by default, but configurable by the suspend.pm_test_delay module
parameter), resume devices and thaw processes. If "platform" is written to
/sys/power/pm_test , then after suspending devices the kernel will additionally /sys/power/pm_test , then after suspending devices the kernel will additionally
invoke the global control methods (eg. ACPI global control methods) used to invoke the global control methods (eg. ACPI global control methods) used to
prepare the platform firmware for hibernation. Next, it will wait 5 seconds and prepare the platform firmware for hibernation. Next, it will wait a
invoke the platform (eg. ACPI) global methods used to cancel hibernation etc. configurable number of seconds and invoke the platform (eg. ACPI) global
methods used to cancel hibernation etc.
Writing "none" to /sys/power/pm_test causes the kernel to switch to the normal Writing "none" to /sys/power/pm_test causes the kernel to switch to the normal
hibernation/suspend operations. Also, when open for reading, /sys/power/pm_test hibernation/suspend operations. Also, when open for reading, /sys/power/pm_test

View file

@ -1,5 +1,5 @@
#ifndef _ASM_X86_RESUME_TRACE_H #ifndef _ASM_X86_PM_TRACE_H
#define _ASM_X86_RESUME_TRACE_H #define _ASM_X86_PM_TRACE_H
#include <asm/asm.h> #include <asm/asm.h>
@ -14,8 +14,10 @@ do { \
".previous" \ ".previous" \
:"=r" (tracedata) \ :"=r" (tracedata) \
: "i" (__LINE__), "i" (__FILE__)); \ : "i" (__LINE__), "i" (__FILE__)); \
generate_resume_trace(tracedata, user); \ generate_pm_trace(tracedata, user); \
} \ } \
} while (0) } while (0)
#endif /* _ASM_X86_RESUME_TRACE_H */ #define TRACE_SUSPEND(user) TRACE_RESUME(user)
#endif /* _ASM_X86_PM_TRACE_H */

View file

@ -23,7 +23,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/resume-trace.h> #include <linux/pm-trace.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/async.h> #include <linux/async.h>
@ -1017,6 +1017,9 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
char *info = NULL; char *info = NULL;
int error = 0; int error = 0;
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
if (async_error) if (async_error)
goto Complete; goto Complete;
@ -1057,6 +1060,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
Complete: Complete:
complete_all(&dev->power.completion); complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
return error; return error;
} }
@ -1078,7 +1082,7 @@ static int device_suspend_noirq(struct device *dev)
{ {
reinit_completion(&dev->power.completion); reinit_completion(&dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(async_suspend_noirq, dev); async_schedule(async_suspend_noirq, dev);
return 0; return 0;
@ -1157,6 +1161,9 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
char *info = NULL; char *info = NULL;
int error = 0; int error = 0;
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
__pm_runtime_disable(dev, false); __pm_runtime_disable(dev, false);
if (async_error) if (async_error)
@ -1198,6 +1205,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
async_error = error; async_error = error;
Complete: Complete:
TRACE_SUSPEND(error);
complete_all(&dev->power.completion); complete_all(&dev->power.completion);
return error; return error;
} }
@ -1219,7 +1227,7 @@ static int device_suspend_late(struct device *dev)
{ {
reinit_completion(&dev->power.completion); reinit_completion(&dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(async_suspend_late, dev); async_schedule(async_suspend_late, dev);
return 0; return 0;
@ -1338,6 +1346,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
int error = 0; int error = 0;
DECLARE_DPM_WATCHDOG_ON_STACK(wd); DECLARE_DPM_WATCHDOG_ON_STACK(wd);
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
dpm_wait_for_children(dev, async); dpm_wait_for_children(dev, async);
if (async_error) if (async_error)
@ -1444,6 +1455,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
if (error) if (error)
async_error = error; async_error = error;
TRACE_SUSPEND(error);
return error; return error;
} }
@ -1465,7 +1477,7 @@ static int device_suspend(struct device *dev)
{ {
reinit_completion(&dev->power.completion); reinit_completion(&dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(async_suspend, dev); async_schedule(async_suspend, dev);
return 0; return 0;

View file

@ -7,7 +7,7 @@
* devices may be working. * devices may be working.
*/ */
#include <linux/resume-trace.h> #include <linux/pm-trace.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/rtc.h> #include <linux/rtc.h>
@ -154,7 +154,7 @@ EXPORT_SYMBOL(set_trace_device);
* it's not any guarantee, but it's a high _likelihood_ that * it's not any guarantee, but it's a high _likelihood_ that
* the match is valid). * the match is valid).
*/ */
void generate_resume_trace(const void *tracedata, unsigned int user) void generate_pm_trace(const void *tracedata, unsigned int user)
{ {
unsigned short lineno = *(unsigned short *)tracedata; unsigned short lineno = *(unsigned short *)tracedata;
const char *file = *(const char **)(tracedata + 2); const char *file = *(const char **)(tracedata + 2);
@ -164,7 +164,7 @@ void generate_resume_trace(const void *tracedata, unsigned int user)
file_hash_value = hash_string(lineno, file, FILEHASH); file_hash_value = hash_string(lineno, file, FILEHASH);
set_magic_time(user_hash_value, file_hash_value, dev_hash_value); set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
} }
EXPORT_SYMBOL(generate_resume_trace); EXPORT_SYMBOL(generate_pm_trace);
extern char __tracedata_start, __tracedata_end; extern char __tracedata_start, __tracedata_end;
static int show_file_hash(unsigned int value) static int show_file_hash(unsigned int value)

View file

@ -51,6 +51,7 @@
#define DRV_VERSION "1.11" #define DRV_VERSION "1.11"
/* Includes */ /* Includes */
#include <linux/acpi.h> /* For ACPI support */
#include <linux/module.h> /* For module specific items */ #include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */ #include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */ #include <linux/types.h> /* For standard types (like size_t) */
@ -103,6 +104,8 @@ static struct { /* this is private data for the iTCO_wdt device */
struct platform_device *dev; struct platform_device *dev;
/* the PCI-device */ /* the PCI-device */
struct pci_dev *pdev; struct pci_dev *pdev;
/* whether or not the watchdog has been suspended */
bool suspended;
} iTCO_wdt_private; } iTCO_wdt_private;
/* module parameters */ /* module parameters */
@ -571,12 +574,60 @@ static void iTCO_wdt_shutdown(struct platform_device *dev)
iTCO_wdt_stop(NULL); iTCO_wdt_stop(NULL);
} }
#ifdef CONFIG_PM_SLEEP
/*
* Suspend-to-idle requires this, because it stops the ticks and timekeeping, so
* the watchdog cannot be pinged while in that state. In ACPI sleep states the
* watchdog is stopped by the platform firmware.
*/
#ifdef CONFIG_ACPI
static inline bool need_suspend(void)
{
return acpi_target_system_state() == ACPI_STATE_S0;
}
#else
static inline bool need_suspend(void) { return true; }
#endif
static int iTCO_wdt_suspend_noirq(struct device *dev)
{
int ret = 0;
iTCO_wdt_private.suspended = false;
if (watchdog_active(&iTCO_wdt_watchdog_dev) && need_suspend()) {
ret = iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
if (!ret)
iTCO_wdt_private.suspended = true;
}
return ret;
}
static int iTCO_wdt_resume_noirq(struct device *dev)
{
if (iTCO_wdt_private.suspended)
iTCO_wdt_start(&iTCO_wdt_watchdog_dev);
return 0;
}
static struct dev_pm_ops iTCO_wdt_pm = {
.suspend_noirq = iTCO_wdt_suspend_noirq,
.resume_noirq = iTCO_wdt_resume_noirq,
};
#define ITCO_WDT_PM_OPS (&iTCO_wdt_pm)
#else
#define ITCO_WDT_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
static struct platform_driver iTCO_wdt_driver = { static struct platform_driver iTCO_wdt_driver = {
.probe = iTCO_wdt_probe, .probe = iTCO_wdt_probe,
.remove = iTCO_wdt_remove, .remove = iTCO_wdt_remove,
.shutdown = iTCO_wdt_shutdown, .shutdown = iTCO_wdt_shutdown,
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.pm = ITCO_WDT_PM_OPS,
}, },
}; };

View file

@ -1,8 +1,8 @@
#ifndef RESUME_TRACE_H #ifndef PM_TRACE_H
#define RESUME_TRACE_H #define PM_TRACE_H
#ifdef CONFIG_PM_TRACE #ifdef CONFIG_PM_TRACE
#include <asm/resume-trace.h> #include <asm/pm-trace.h>
#include <linux/types.h> #include <linux/types.h>
extern int pm_trace_enabled; extern int pm_trace_enabled;
@ -14,7 +14,7 @@ static inline int pm_trace_is_enabled(void)
struct device; struct device;
extern void set_trace_device(struct device *); extern void set_trace_device(struct device *);
extern void generate_resume_trace(const void *tracedata, unsigned int user); extern void generate_pm_trace(const void *tracedata, unsigned int user);
extern int show_trace_dev_match(char *buf, size_t size); extern int show_trace_dev_match(char *buf, size_t size);
#define TRACE_DEVICE(dev) do { \ #define TRACE_DEVICE(dev) do { \
@ -28,6 +28,7 @@ static inline int pm_trace_is_enabled(void) { return 0; }
#define TRACE_DEVICE(dev) do { } while (0) #define TRACE_DEVICE(dev) do { } while (0)
#define TRACE_RESUME(dev) do { } while (0) #define TRACE_RESUME(dev) do { } while (0)
#define TRACE_SUSPEND(dev) do { } while (0)
#endif #endif

View file

@ -11,7 +11,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/resume-trace.h> #include <linux/pm-trace.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>

View file

@ -28,6 +28,7 @@
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/moduleparam.h>
#include "power.h" #include "power.h"
@ -233,12 +234,20 @@ static bool platform_suspend_again(suspend_state_t state)
suspend_ops->suspend_again() : false; suspend_ops->suspend_again() : false;
} }
#ifdef CONFIG_PM_DEBUG
static unsigned int pm_test_delay = 5;
module_param(pm_test_delay, uint, 0644);
MODULE_PARM_DESC(pm_test_delay,
"Number of seconds to wait before resuming from suspend test");
#endif
static int suspend_test(int level) static int suspend_test(int level)
{ {
#ifdef CONFIG_PM_DEBUG #ifdef CONFIG_PM_DEBUG
if (pm_test_level == level) { if (pm_test_level == level) {
printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); printk(KERN_INFO "suspend debug: Waiting for %d second(s).\n",
mdelay(5000); pm_test_delay);
mdelay(pm_test_delay * 1000);
return 1; return 1;
} }
#endif /* !CONFIG_PM_DEBUG */ #endif /* !CONFIG_PM_DEBUG */