ACPI: disable lower idle C-states across suspend/resume

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>

authored by Thomas Gleixner and committed by Linus Torvalds b04e7bdb 1f0cff6e

+22 -1
+2
drivers/acpi/processor_core.c
··· 102 102 .add = acpi_processor_add, 103 103 .remove = acpi_processor_remove, 104 104 .start = acpi_processor_start, 105 + .suspend = acpi_processor_suspend, 106 + .resume = acpi_processor_resume, 105 107 }, 106 108 }; 107 109
+18 -1
drivers/acpi/processor_idle.c
··· 325 325 326 326 #endif 327 327 328 + /* 329 + * Suspend / resume control 330 + */ 331 + static int acpi_idle_suspend; 332 + 333 + int acpi_processor_suspend(struct acpi_device * device, pm_message_t state) 334 + { 335 + acpi_idle_suspend = 1; 336 + return 0; 337 + } 338 + 339 + int acpi_processor_resume(struct acpi_device * device) 340 + { 341 + acpi_idle_suspend = 0; 342 + return 0; 343 + } 344 + 328 345 static void acpi_processor_idle(void) 329 346 { 330 347 struct acpi_processor *pr = NULL; ··· 372 355 } 373 356 374 357 cx = pr->power.state; 375 - if (!cx) { 358 + if (!cx || acpi_idle_suspend) { 376 359 if (pm_idle_save) 377 360 pm_idle_save(); 378 361 else
+2
include/acpi/processor.h
··· 320 320 int acpi_processor_cst_has_changed(struct acpi_processor *pr); 321 321 int acpi_processor_power_exit(struct acpi_processor *pr, 322 322 struct acpi_device *device); 323 + int acpi_processor_suspend(struct acpi_device * device, pm_message_t state); 324 + int acpi_processor_resume(struct acpi_device * device); 323 325 324 326 /* in processor_thermal.c */ 325 327 int acpi_processor_get_limit_info(struct acpi_processor *pr);