ACPI / PM: Hold ACPI scan lock over the "freeze" sleep state
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 15 May 2014 21:29:57 +0000 (23:29 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 16 May 2014 10:18:27 +0000 (12:18 +0200)
The "freeze" sleep state suffers from the same issue that was
addressed by commit ad07277e82de (ACPI / PM: Hold acpi_scan_lock over
system PM transitions) for ACPI sleep states, that is, things break
if ->remove() is called for devices whose system resume callbacks
haven't been executed yet.

It also can be addressed in the same way, by holding the ACPI scan
lock over the "freeze" sleep state and PM transitions to and from
that state, but ->begin() and ->end() platform operations for the
"freeze" sleep state are needed for this purpose.

This change has been tested on Acer Aspire S5 with Thunderbolt.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/sleep.c
include/linux/suspend.h
kernel/power/suspend.c

index 2281ca31c1bca4d0dd8977ef821a541842b86771..c11e3795431b2e686b098cd4f14f2cbcdcd0c2f4 100644 (file)
@@ -612,6 +612,22 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = {
        .recover = acpi_pm_finish,
 };
 
+static int acpi_freeze_begin(void)
+{
+       acpi_scan_lock_acquire();
+       return 0;
+}
+
+static void acpi_freeze_end(void)
+{
+       acpi_scan_lock_release();
+}
+
+static const struct platform_freeze_ops acpi_freeze_ops = {
+       .begin = acpi_freeze_begin,
+       .end = acpi_freeze_end,
+};
+
 static void acpi_sleep_suspend_setup(void)
 {
        int i;
@@ -622,7 +638,9 @@ static void acpi_sleep_suspend_setup(void)
 
        suspend_set_ops(old_suspend_ordering ?
                &acpi_suspend_ops_old : &acpi_suspend_ops);
+       freeze_set_ops(&acpi_freeze_ops);
 }
+
 #else /* !CONFIG_SUSPEND */
 static inline void acpi_sleep_suspend_setup(void) {}
 #endif /* !CONFIG_SUSPEND */
index f73cabf59012f1ead45eee1eb7aa97600b1bb902..91d66fd8dce10fa66659ef017a0afa367d0ea572 100644 (file)
@@ -187,6 +187,11 @@ struct platform_suspend_ops {
        void (*recover)(void);
 };
 
+struct platform_freeze_ops {
+       int (*begin)(void);
+       void (*end)(void);
+};
+
 #ifdef CONFIG_SUSPEND
 /**
  * suspend_set_ops - set platform dependent suspend operations
@@ -194,6 +199,7 @@ struct platform_suspend_ops {
  */
 extern void suspend_set_ops(const struct platform_suspend_ops *ops);
 extern int suspend_valid_only_mem(suspend_state_t state);
+extern void freeze_set_ops(const struct platform_freeze_ops *ops);
 extern void freeze_wake(void);
 
 /**
@@ -220,6 +226,7 @@ extern int pm_suspend(suspend_state_t state);
 
 static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {}
 static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; }
+static inline void freeze_set_ops(const struct platform_freeze_ops *ops) {}
 static inline void freeze_wake(void) {}
 #endif /* !CONFIG_SUSPEND */
 
index 8233cd4047d776c311ef71800479f1e2b637e5da..73a905f83972048617b34bd22cf418df3527e8f4 100644 (file)
@@ -38,6 +38,7 @@ const char *const pm_states[PM_SUSPEND_MAX] = {
 };
 
 static const struct platform_suspend_ops *suspend_ops;
+static const struct platform_freeze_ops *freeze_ops;
 
 static bool need_suspend_ops(suspend_state_t state)
 {
@@ -47,6 +48,13 @@ static bool need_suspend_ops(suspend_state_t state)
 static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head);
 static bool suspend_freeze_wake;
 
+void freeze_set_ops(const struct platform_freeze_ops *ops)
+{
+       lock_system_sleep();
+       freeze_ops = ops;
+       unlock_system_sleep();
+}
+
 static void freeze_begin(void)
 {
        suspend_freeze_wake = false;
@@ -269,6 +277,10 @@ int suspend_devices_and_enter(suspend_state_t state)
                error = suspend_ops->begin(state);
                if (error)
                        goto Close;
+       } else if (state == PM_SUSPEND_FREEZE && freeze_ops->begin) {
+               error = freeze_ops->begin();
+               if (error)
+                       goto Close;
        }
        suspend_console();
        suspend_test_start();
@@ -294,6 +306,9 @@ int suspend_devices_and_enter(suspend_state_t state)
  Close:
        if (need_suspend_ops(state) && suspend_ops->end)
                suspend_ops->end();
+       else if (state == PM_SUSPEND_FREEZE && freeze_ops->end)
+               freeze_ops->end();
+
        trace_machine_suspend(PWR_EVENT_EXIT);
        return error;