aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/sleep.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2010-04-08 19:39:40 -0400
committerLen Brown <len.brown@intel.com>2010-05-28 23:35:55 -0400
commitd5a64513c6a171262082c250592c062e97a2c693 (patch)
treeebcbe81e8ea4fd4ba773fe4d35698b71a88f77ee /drivers/acpi/sleep.c
parente40152ee1e1c7a63f4777791863215e3faa37a86 (diff)
ACPI / EC / PM: Fix race between EC transactions and system suspend
There still is a race that may result in suspending the system in the middle of an EC transaction in progress, which leads to problems (like the kernel thinking that the ACPI global lock is held during resume while in fact it's not). To remove the race condition, modify the ACPI platform suspend and hibernate callbacks so that EC transactions are blocked right after executing the _PTS global control method and are allowed to happen again right after the low-level wakeup. Introduce acpi_pm_freeze() that will disable GPEs, wait until the event queues are empty and block EC transactions. Use it wherever GPEs are disabled in preparation for switching local interrupts off. Introduce acpi_pm_thaw() that will allow EC transactions to happen again and enable runtime GPEs. Use it to balance acpi_pm_freeze() wherever necessary. In addition to that use acpi_ec_resume_transactions_early() to unblock EC transactions as early as reasonably possible during resume. Also unblock EC transactions in acpi_hibernation_finish() and in the analogous suspend routine to make sure that the EC transactions are enabled in all error paths. Fixes https://bugzilla.kernel.org/show_bug.cgi?id=14668 Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Reported-and-tested-by: Maxim Levitsky <maximlevitsky@gmail.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/sleep.c')
-rw-r--r--drivers/acpi/sleep.c55
1 files changed, 29 insertions, 26 deletions
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index baa76bbf244a..24741ac65897 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -110,11 +110,13 @@ void __init acpi_old_suspend_ordering(void)
110} 110}
111 111
112/** 112/**
113 * acpi_pm_disable_gpes - Disable the GPEs. 113 * acpi_pm_freeze - Disable the GPEs and suspend EC transactions.
114 */ 114 */
115static int acpi_pm_disable_gpes(void) 115static int acpi_pm_freeze(void)
116{ 116{
117 acpi_disable_all_gpes(); 117 acpi_disable_all_gpes();
118 acpi_os_wait_events_complete(NULL);
119 acpi_ec_suspend_transactions();
118 return 0; 120 return 0;
119} 121}
120 122
@@ -142,7 +144,8 @@ static int acpi_pm_prepare(void)
142 int error = __acpi_pm_prepare(); 144 int error = __acpi_pm_prepare();
143 145
144 if (!error) 146 if (!error)
145 acpi_disable_all_gpes(); 147 acpi_pm_freeze();
148
146 return error; 149 return error;
147} 150}
148 151
@@ -275,6 +278,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
275 * acpi_leave_sleep_state will reenable specific GPEs later 278 * acpi_leave_sleep_state will reenable specific GPEs later
276 */ 279 */
277 acpi_disable_all_gpes(); 280 acpi_disable_all_gpes();
281 /* Allow EC transactions to happen. */
282 acpi_ec_resume_transactions_early();
278 283
279 local_irq_restore(flags); 284 local_irq_restore(flags);
280 printk(KERN_DEBUG "Back to C!\n"); 285 printk(KERN_DEBUG "Back to C!\n");
@@ -286,6 +291,12 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
286 return ACPI_SUCCESS(status) ? 0 : -EFAULT; 291 return ACPI_SUCCESS(status) ? 0 : -EFAULT;
287} 292}
288 293
294static void acpi_suspend_finish(void)
295{
296 acpi_ec_resume_transactions();
297 acpi_pm_finish();
298}
299
289static int acpi_suspend_state_valid(suspend_state_t pm_state) 300static int acpi_suspend_state_valid(suspend_state_t pm_state)
290{ 301{
291 u32 acpi_state; 302 u32 acpi_state;
@@ -307,7 +318,7 @@ static struct platform_suspend_ops acpi_suspend_ops = {
307 .begin = acpi_suspend_begin, 318 .begin = acpi_suspend_begin,
308 .prepare_late = acpi_pm_prepare, 319 .prepare_late = acpi_pm_prepare,
309 .enter = acpi_suspend_enter, 320 .enter = acpi_suspend_enter,
310 .wake = acpi_pm_finish, 321 .wake = acpi_suspend_finish,
311 .end = acpi_pm_end, 322 .end = acpi_pm_end,
312}; 323};
313 324
@@ -333,9 +344,9 @@ static int acpi_suspend_begin_old(suspend_state_t pm_state)
333static struct platform_suspend_ops acpi_suspend_ops_old = { 344static struct platform_suspend_ops acpi_suspend_ops_old = {
334 .valid = acpi_suspend_state_valid, 345 .valid = acpi_suspend_state_valid,
335 .begin = acpi_suspend_begin_old, 346 .begin = acpi_suspend_begin_old,
336 .prepare_late = acpi_pm_disable_gpes, 347 .prepare_late = acpi_pm_freeze,
337 .enter = acpi_suspend_enter, 348 .enter = acpi_suspend_enter,
338 .wake = acpi_pm_finish, 349 .wake = acpi_suspend_finish,
339 .end = acpi_pm_end, 350 .end = acpi_pm_end,
340 .recover = acpi_pm_finish, 351 .recover = acpi_pm_finish,
341}; 352};
@@ -586,6 +597,7 @@ static int acpi_hibernation_enter(void)
586static void acpi_hibernation_finish(void) 597static void acpi_hibernation_finish(void)
587{ 598{
588 hibernate_nvs_free(); 599 hibernate_nvs_free();
600 acpi_ec_resume_transactions();
589 acpi_pm_finish(); 601 acpi_pm_finish();
590} 602}
591 603
@@ -606,17 +618,11 @@ static void acpi_hibernation_leave(void)
606 } 618 }
607 /* Restore the NVS memory area */ 619 /* Restore the NVS memory area */
608 hibernate_nvs_restore(); 620 hibernate_nvs_restore();
621 /* Allow EC transactions to happen. */
622 acpi_ec_resume_transactions_early();
609} 623}
610 624
611static int acpi_pm_pre_restore(void) 625static void acpi_pm_thaw(void)
612{
613 acpi_disable_all_gpes();
614 acpi_os_wait_events_complete(NULL);
615 acpi_ec_suspend_transactions();
616 return 0;
617}
618
619static void acpi_pm_restore_cleanup(void)
620{ 626{
621 acpi_ec_resume_transactions(); 627 acpi_ec_resume_transactions();
622 acpi_enable_all_runtime_gpes(); 628 acpi_enable_all_runtime_gpes();
@@ -630,8 +636,8 @@ static struct platform_hibernation_ops acpi_hibernation_ops = {
630 .prepare = acpi_pm_prepare, 636 .prepare = acpi_pm_prepare,
631 .enter = acpi_hibernation_enter, 637 .enter = acpi_hibernation_enter,
632 .leave = acpi_hibernation_leave, 638 .leave = acpi_hibernation_leave,
633 .pre_restore = acpi_pm_pre_restore, 639 .pre_restore = acpi_pm_freeze,
634 .restore_cleanup = acpi_pm_restore_cleanup, 640 .restore_cleanup = acpi_pm_thaw,
635}; 641};
636 642
637/** 643/**
@@ -663,12 +669,9 @@ static int acpi_hibernation_begin_old(void)
663 669
664static int acpi_hibernation_pre_snapshot_old(void) 670static int acpi_hibernation_pre_snapshot_old(void)
665{ 671{
666 int error = acpi_pm_disable_gpes(); 672 acpi_pm_freeze();
667 673 hibernate_nvs_save();
668 if (!error) 674 return 0;
669 hibernate_nvs_save();
670
671 return error;
672} 675}
673 676
674/* 677/*
@@ -680,11 +683,11 @@ static struct platform_hibernation_ops acpi_hibernation_ops_old = {
680 .end = acpi_pm_end, 683 .end = acpi_pm_end,
681 .pre_snapshot = acpi_hibernation_pre_snapshot_old, 684 .pre_snapshot = acpi_hibernation_pre_snapshot_old,
682 .finish = acpi_hibernation_finish, 685 .finish = acpi_hibernation_finish,
683 .prepare = acpi_pm_disable_gpes, 686 .prepare = acpi_pm_freeze,
684 .enter = acpi_hibernation_enter, 687 .enter = acpi_hibernation_enter,
685 .leave = acpi_hibernation_leave, 688 .leave = acpi_hibernation_leave,
686 .pre_restore = acpi_pm_pre_restore, 689 .pre_restore = acpi_pm_freeze,
687 .restore_cleanup = acpi_pm_restore_cleanup, 690 .restore_cleanup = acpi_pm_thaw,
688 .recover = acpi_pm_finish, 691 .recover = acpi_pm_finish,
689}; 692};
690#endif /* CONFIG_HIBERNATION */ 693#endif /* CONFIG_HIBERNATION */