ACPI: disable lower idle C-states across suspend/resume
authorThomas Gleixner <tglx@linutronix.de>
Sat, 22 Sep 2007 22:29:05 +0000 (22:29 +0000)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Sun, 23 Sep 2007 00:15:34 +0000 (17:15 -0700)
device_suspend() calls ACPI suspend functions, which seems to have undesired
side effects on lower idle C-states. It took me some time to realize that
especially the VAIO BIOSes (both Andrews jinxed UP and my elfstruck SMP one)
show this effect. I'm quite sure that other bug reports against suspend/resume
about turning the system into a brick have the same root cause.

After fishing in the dark for quite some time, I realized that removing the ACPI
processor module before suspend (this removes the lower C-state functionality)
made the problem disappear. Interestingly enough the propability of having a
bricked box is influenced by various factors (interrupts, size of the ram image,
...). Even adding a bunch of printks in the wrong places made the problem go
away. The previous periodic tick implementation simply pampered over the
problem, which explains why the dyntick / clockevents changes made this more
prominent.

We avoid complex functionality during the boot process and we have to do the
same during suspend/resume. It is a similar scenario and equaly fragile.

Add suspend / resume functions to the ACPI processor code and disable the lower
idle C-states across suspend/resume. Fall back to the default idle
implementation (halt) instead.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Len Brown <lenb@kernel.org>
Cc: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/acpi/processor_core.c
drivers/acpi/processor_idle.c
include/acpi/processor.h

index 2afb3d2086b3d03b3e4858caffba20f6f456c90f..9f11dc296cdd7bc29f5545cd8524eb03ede594e3 100644 (file)
@@ -102,6 +102,8 @@ static struct acpi_driver acpi_processor_driver = {
                .add = acpi_processor_add,
                .remove = acpi_processor_remove,
                .start = acpi_processor_start,
+               .suspend = acpi_processor_suspend,
+               .resume = acpi_processor_resume,
                },
 };
 
index d9b8af763e1ec59e64671655fda51e0601b55da9..f18261368e76e87059606d8d8dfc4e753266b99a 100644 (file)
@@ -325,6 +325,23 @@ static void acpi_state_timer_broadcast(struct acpi_processor *pr,
 
 #endif
 
+/*
+ * Suspend / resume control
+ */
+static int acpi_idle_suspend;
+
+int acpi_processor_suspend(struct acpi_device * device, pm_message_t state)
+{
+       acpi_idle_suspend = 1;
+       return 0;
+}
+
+int acpi_processor_resume(struct acpi_device * device)
+{
+       acpi_idle_suspend = 0;
+       return 0;
+}
+
 static void acpi_processor_idle(void)
 {
        struct acpi_processor *pr = NULL;
@@ -355,7 +372,7 @@ static void acpi_processor_idle(void)
        }
 
        cx = pr->power.state;
-       if (!cx) {
+       if (!cx || acpi_idle_suspend) {
                if (pm_idle_save)
                        pm_idle_save();
                else
index ec3ffdadb4d257c452d091b30e8322c01c049e3f..99934a999e6651e904bc23310d7c36a17153f05f 100644 (file)
@@ -320,6 +320,8 @@ int acpi_processor_power_init(struct acpi_processor *pr,
 int acpi_processor_cst_has_changed(struct acpi_processor *pr);
 int acpi_processor_power_exit(struct acpi_processor *pr,
                              struct acpi_device *device);
+int acpi_processor_suspend(struct acpi_device * device, pm_message_t state);
+int acpi_processor_resume(struct acpi_device * device);
 
 /* in processor_thermal.c */
 int acpi_processor_get_limit_info(struct acpi_processor *pr);