ACPI / PM: Hold acpi_scan_lock over system PM transitions
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 19 Aug 2013 23:42:32 +0000 (01:42 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 20 Aug 2013 23:23:53 +0000 (01:23 +0200)
Bad things happen if ACPI hotplug events are handled during system
PM transitions, especially if devices are removed as a result.
To prevent those bad things from happening, acquire acpi_scan_lock
when a PM transition is started and release it when that transition
is complete or has been aborted.

This fixes resume lockup on my test-bed Acer Aspire S5 that happens
when Thunderbolt devices are disconnected from the machine while
suspended.

Also fixes the analogous problem for Mika Westerberg on an
Intel DZ77RE-75K board.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Toshi Kani <toshi.kani@hp.com>
drivers/acpi/sleep.c

index 81b0f03d97db75a66fa649ec6ce505f5c0a6afb8..1dec53decffaf4dc922e94fb01dc820e2127d42d 100644 (file)
@@ -420,10 +420,21 @@ static void acpi_pm_finish(void)
 }
 
 /**
- *     acpi_pm_end - Finish up suspend sequence.
+ * acpi_pm_start - Start system PM transition.
+ */
+static void acpi_pm_start(u32 acpi_state)
+{
+       acpi_target_sleep_state = acpi_state;
+       acpi_sleep_tts_switch(acpi_target_sleep_state);
+       acpi_scan_lock_acquire();
+}
+
+/**
+ * acpi_pm_end - Finish up system PM transition.
  */
 static void acpi_pm_end(void)
 {
+       acpi_scan_lock_release();
        /*
         * This is necessary in case acpi_pm_finish() is not called during a
         * failing transition to a sleep state.
@@ -451,21 +462,19 @@ static u32 acpi_suspend_states[] = {
 static int acpi_suspend_begin(suspend_state_t pm_state)
 {
        u32 acpi_state = acpi_suspend_states[pm_state];
-       int error = 0;
+       int error;
 
        error = (nvs_nosave || nvs_nosave_s3) ? 0 : suspend_nvs_alloc();
        if (error)
                return error;
 
-       if (sleep_states[acpi_state]) {
-               acpi_target_sleep_state = acpi_state;
-               acpi_sleep_tts_switch(acpi_target_sleep_state);
-       } else {
-               printk(KERN_ERR "ACPI does not support this state: %d\n",
-                       pm_state);
-               error = -ENOSYS;
+       if (!sleep_states[acpi_state]) {
+               pr_err("ACPI does not support sleep state S%u\n", acpi_state);
+               return -ENOSYS;
        }
-       return error;
+
+       acpi_pm_start(acpi_state);
+       return 0;
 }
 
 /**
@@ -631,10 +640,8 @@ static int acpi_hibernation_begin(void)
        int error;
 
        error = nvs_nosave ? 0 : suspend_nvs_alloc();
-       if (!error) {
-               acpi_target_sleep_state = ACPI_STATE_S4;
-               acpi_sleep_tts_switch(acpi_target_sleep_state);
-       }
+       if (!error)
+               acpi_pm_start(ACPI_STATE_S4);
 
        return error;
 }
@@ -713,8 +720,10 @@ static int acpi_hibernation_begin_old(void)
        if (!error) {
                if (!nvs_nosave)
                        error = suspend_nvs_alloc();
-               if (!error)
+               if (!error) {
                        acpi_target_sleep_state = ACPI_STATE_S4;
+                       acpi_scan_lock_acquire();
+               }
        }
        return error;
 }