]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
PM / reboot: Eliminate race between reboot and suspend
authorPingfan Liu <kernelfans@gmail.com>
Tue, 31 Jul 2018 08:51:32 +0000 (16:51 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 6 Aug 2018 10:35:20 +0000 (12:35 +0200)
At present, "systemctl suspend" and "shutdown" can run in parrallel. A
system can suspend after devices_shutdown(), and resume. Then the shutdown
task goes on to power off. This causes many devices are not really shut
off. Hence replacing reboot_mutex with system_transition_mutex (renamed
from pm_mutex) to achieve the exclusion. The renaming of pm_mutex as
system_transition_mutex can be better to reflect the purpose of the mutex.

Signed-off-by: Pingfan Liu <kernelfans@gmail.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Documentation/power/freezing-of-tasks.txt
Documentation/power/suspend-and-cpuhotplug.txt
include/linux/suspend.h
kernel/freezer.c
kernel/power/hibernate.c
kernel/power/main.c
kernel/power/suspend.c
kernel/power/user.c
kernel/reboot.c
mm/page_alloc.c

index af005770e76762f8ba513f42b563ddd7d3b21736..cd283190855a8998d2b7b8ba23aac82a530e80cb 100644 (file)
@@ -204,26 +204,26 @@ VI. Are there any precautions to be taken to prevent freezing failures?
 
 Yes, there are.
 
 
 Yes, there are.
 
-First of all, grabbing the 'pm_mutex' lock to mutually exclude a piece of code
+First of all, grabbing the 'system_transition_mutex' lock to mutually exclude a piece of code
 from system-wide sleep such as suspend/hibernation is not encouraged.
 If possible, that piece of code must instead hook onto the suspend/hibernation
 notifiers to achieve mutual exclusion. Look at the CPU-Hotplug code
 (kernel/cpu.c) for an example.
 
 from system-wide sleep such as suspend/hibernation is not encouraged.
 If possible, that piece of code must instead hook onto the suspend/hibernation
 notifiers to achieve mutual exclusion. Look at the CPU-Hotplug code
 (kernel/cpu.c) for an example.
 
-However, if that is not feasible, and grabbing 'pm_mutex' is deemed necessary,
-it is strongly discouraged to directly call mutex_[un]lock(&pm_mutex) since
+However, if that is not feasible, and grabbing 'system_transition_mutex' is deemed necessary,
+it is strongly discouraged to directly call mutex_[un]lock(&system_transition_mutex) since
 that could lead to freezing failures, because if the suspend/hibernate code
 that could lead to freezing failures, because if the suspend/hibernate code
-successfully acquired the 'pm_mutex' lock, and hence that other entity failed
+successfully acquired the 'system_transition_mutex' lock, and hence that other entity failed
 to acquire the lock, then that task would get blocked in TASK_UNINTERRUPTIBLE
 state. As a consequence, the freezer would not be able to freeze that task,
 leading to freezing failure.
 
 However, the [un]lock_system_sleep() APIs are safe to use in this scenario,
 since they ask the freezer to skip freezing this task, since it is anyway
 to acquire the lock, then that task would get blocked in TASK_UNINTERRUPTIBLE
 state. As a consequence, the freezer would not be able to freeze that task,
 leading to freezing failure.
 
 However, the [un]lock_system_sleep() APIs are safe to use in this scenario,
 since they ask the freezer to skip freezing this task, since it is anyway
-"frozen enough" as it is blocked on 'pm_mutex', which will be released
+"frozen enough" as it is blocked on 'system_transition_mutex', which will be released
 only after the entire suspend/hibernation sequence is complete.
 So, to summarize, use [un]lock_system_sleep() instead of directly using
 only after the entire suspend/hibernation sequence is complete.
 So, to summarize, use [un]lock_system_sleep() instead of directly using
-mutex_[un]lock(&pm_mutex). That would prevent freezing failures.
+mutex_[un]lock(&system_transition_mutex). That would prevent freezing failures.
 
 V. Miscellaneous
 /sys/power/pm_freeze_timeout controls how long it will cost at most to freeze
 
 V. Miscellaneous
 /sys/power/pm_freeze_timeout controls how long it will cost at most to freeze
index 6f55eb960a6dc642159cb8b027c000c764d554a5..a8751b8df10ec86d020fd1a4a718f2cedfa8ed66 100644 (file)
@@ -32,7 +32,7 @@ More details follow:
                                     sysfs file
                                         |
                                         v
                                     sysfs file
                                         |
                                         v
-                               Acquire pm_mutex lock
+                               Acquire system_transition_mutex lock
                                         |
                                         v
                              Send PM_SUSPEND_PREPARE
                                         |
                                         v
                              Send PM_SUSPEND_PREPARE
@@ -96,10 +96,10 @@ execution during resume):
 
 * thaw tasks
 * send PM_POST_SUSPEND notifications
 
 * thaw tasks
 * send PM_POST_SUSPEND notifications
-* Release pm_mutex lock.
+* Release system_transition_mutex lock.
 
 
 
 
-It is to be noted here that the pm_mutex lock is acquired at the very
+It is to be noted here that the system_transition_mutex lock is acquired at the very
 beginning, when we are just starting out to suspend, and then released only
 after the entire cycle is complete (i.e., suspend + resume).
 
 beginning, when we are just starting out to suspend, and then released only
 after the entire cycle is complete (i.e., suspend + resume).
 
index 440b62f7502ebe4cc2cc5161f511cc427a30e3d5..5a28ac9284f0e1d10f639564ee046c6171d676c0 100644 (file)
@@ -414,7 +414,7 @@ static inline bool hibernation_available(void) { return false; }
 #define PM_RESTORE_PREPARE     0x0005 /* Going to restore a saved image */
 #define PM_POST_RESTORE                0x0006 /* Restore failed */
 
 #define PM_RESTORE_PREPARE     0x0005 /* Going to restore a saved image */
 #define PM_POST_RESTORE                0x0006 /* Restore failed */
 
-extern struct mutex pm_mutex;
+extern struct mutex system_transition_mutex;
 
 #ifdef CONFIG_PM_SLEEP
 void save_processor_state(void);
 
 #ifdef CONFIG_PM_SLEEP
 void save_processor_state(void);
index 6f56a9e219facf003d802bd1e9c0cd2dea9824d3..b162b74611e475e611723aacb04c857f7638ab53 100644 (file)
@@ -15,7 +15,9 @@
 atomic_t system_freezing_cnt = ATOMIC_INIT(0);
 EXPORT_SYMBOL(system_freezing_cnt);
 
 atomic_t system_freezing_cnt = ATOMIC_INIT(0);
 EXPORT_SYMBOL(system_freezing_cnt);
 
-/* indicate whether PM freezing is in effect, protected by pm_mutex */
+/* indicate whether PM freezing is in effect, protected by
+ * system_transition_mutex
+ */
 bool pm_freezing;
 bool pm_nosig_freezing;
 
 bool pm_freezing;
 bool pm_nosig_freezing;
 
index 38f3f96b4b1213b64f7248f7ecc1bbe1ea1e6295..abef759de7c8fb4a8ece278fd7b7730d5b5e41ab 100644 (file)
@@ -338,7 +338,7 @@ static int create_image(int platform_mode)
  * hibernation_snapshot - Quiesce devices and create a hibernation image.
  * @platform_mode: If set, use platform driver to prepare for the transition.
  *
  * hibernation_snapshot - Quiesce devices and create a hibernation image.
  * @platform_mode: If set, use platform driver to prepare for the transition.
  *
- * This routine must be called with pm_mutex held.
+ * This routine must be called with system_transition_mutex held.
  */
 int hibernation_snapshot(int platform_mode)
 {
  */
 int hibernation_snapshot(int platform_mode)
 {
@@ -500,8 +500,9 @@ static int resume_target_kernel(bool platform_mode)
  * hibernation_restore - Quiesce devices and restore from a hibernation image.
  * @platform_mode: If set, use platform driver to prepare for the transition.
  *
  * hibernation_restore - Quiesce devices and restore from a hibernation image.
  * @platform_mode: If set, use platform driver to prepare for the transition.
  *
- * This routine must be called with pm_mutex held.  If it is successful, control
- * reappears in the restored target kernel in hibernation_snapshot().
+ * This routine must be called with system_transition_mutex held.  If it is
+ * successful, control reappears in the restored target kernel in
+ * hibernation_snapshot().
  */
 int hibernation_restore(int platform_mode)
 {
  */
 int hibernation_restore(int platform_mode)
 {
@@ -806,13 +807,13 @@ static int software_resume(void)
         * name_to_dev_t() below takes a sysfs buffer mutex when sysfs
         * is configured into the kernel. Since the regular hibernate
         * trigger path is via sysfs which takes a buffer mutex before
         * name_to_dev_t() below takes a sysfs buffer mutex when sysfs
         * is configured into the kernel. Since the regular hibernate
         * trigger path is via sysfs which takes a buffer mutex before
-        * calling hibernate functions (which take pm_mutex) this can
-        * cause lockdep to complain about a possible ABBA deadlock
+        * calling hibernate functions (which take system_transition_mutex)
+        * this can cause lockdep to complain about a possible ABBA deadlock
         * which cannot happen since we're in the boot code here and
         * sysfs can't be invoked yet. Therefore, we use a subclass
         * here to avoid lockdep complaining.
         */
         * which cannot happen since we're in the boot code here and
         * sysfs can't be invoked yet. Therefore, we use a subclass
         * here to avoid lockdep complaining.
         */
-       mutex_lock_nested(&pm_mutex, SINGLE_DEPTH_NESTING);
+       mutex_lock_nested(&system_transition_mutex, SINGLE_DEPTH_NESTING);
 
        if (swsusp_resume_device)
                goto Check_image;
 
        if (swsusp_resume_device)
                goto Check_image;
@@ -900,7 +901,7 @@ static int software_resume(void)
        atomic_inc(&snapshot_device_available);
        /* For success case, the suspend path will release the lock */
  Unlock:
        atomic_inc(&snapshot_device_available);
        /* For success case, the suspend path will release the lock */
  Unlock:
-       mutex_unlock(&pm_mutex);
+       mutex_unlock(&system_transition_mutex);
        pm_pr_dbg("Hibernation image not present or could not be loaded.\n");
        return error;
  Close_Finish:
        pm_pr_dbg("Hibernation image not present or could not be loaded.\n");
        return error;
  Close_Finish:
index d9706da1093036dbdbce61e7cf74b2f410c8a99d..35b50823d83be811552c8e800844bb86fac29a66 100644 (file)
 #include <linux/workqueue.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/workqueue.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
+#include <linux/suspend.h>
 
 #include "power.h"
 
 
 #include "power.h"
 
-DEFINE_MUTEX(pm_mutex);
-
 #ifdef CONFIG_PM_SLEEP
 
 void lock_system_sleep(void)
 {
        current->flags |= PF_FREEZER_SKIP;
 #ifdef CONFIG_PM_SLEEP
 
 void lock_system_sleep(void)
 {
        current->flags |= PF_FREEZER_SKIP;
-       mutex_lock(&pm_mutex);
+       mutex_lock(&system_transition_mutex);
 }
 EXPORT_SYMBOL_GPL(lock_system_sleep);
 
 }
 EXPORT_SYMBOL_GPL(lock_system_sleep);
 
@@ -37,8 +36,9 @@ void unlock_system_sleep(void)
         *
         * Reason:
         * Fundamentally, we just don't need it, because freezing condition
         *
         * Reason:
         * Fundamentally, we just don't need it, because freezing condition
-        * doesn't come into effect until we release the pm_mutex lock,
-        * since the freezer always works with pm_mutex held.
+        * doesn't come into effect until we release the
+        * system_transition_mutex lock, since the freezer always works with
+        * system_transition_mutex held.
         *
         * More importantly, in the case of hibernation,
         * unlock_system_sleep() gets called in snapshot_read() and
         *
         * More importantly, in the case of hibernation,
         * unlock_system_sleep() gets called in snapshot_read() and
@@ -47,7 +47,7 @@ void unlock_system_sleep(void)
         * enter the refrigerator, thus causing hibernation to lockup.
         */
        current->flags &= ~PF_FREEZER_SKIP;
         * enter the refrigerator, thus causing hibernation to lockup.
         */
        current->flags &= ~PF_FREEZER_SKIP;
-       mutex_unlock(&pm_mutex);
+       mutex_unlock(&system_transition_mutex);
 }
 EXPORT_SYMBOL_GPL(unlock_system_sleep);
 
 }
 EXPORT_SYMBOL_GPL(unlock_system_sleep);
 
index 87331565e5050a296aca3fec37df3dc229b3c0cc..9e13afe65a145efc99218391b1aa8ba8845c8fb9 100644 (file)
@@ -556,7 +556,7 @@ static int enter_state(suspend_state_t state)
        } else if (!valid_state(state)) {
                return -EINVAL;
        }
        } else if (!valid_state(state)) {
                return -EINVAL;
        }
-       if (!mutex_trylock(&pm_mutex))
+       if (!mutex_trylock(&system_transition_mutex))
                return -EBUSY;
 
        if (state == PM_SUSPEND_TO_IDLE)
                return -EBUSY;
 
        if (state == PM_SUSPEND_TO_IDLE)
@@ -590,7 +590,7 @@ static int enter_state(suspend_state_t state)
        pm_pr_dbg("Finishing wakeup.\n");
        suspend_finish();
  Unlock:
        pm_pr_dbg("Finishing wakeup.\n");
        suspend_finish();
  Unlock:
-       mutex_unlock(&pm_mutex);
+       mutex_unlock(&system_transition_mutex);
        return error;
 }
 
        return error;
 }
 
index abd225550271352ddb4306c9d9b506c013be80ae..2d8b60a3c86b9f768cbe74e82f843c776a00619c 100644 (file)
@@ -216,7 +216,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
-       if (!mutex_trylock(&pm_mutex))
+       if (!mutex_trylock(&system_transition_mutex))
                return -EBUSY;
 
        lock_device_hotplug();
                return -EBUSY;
 
        lock_device_hotplug();
@@ -394,7 +394,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
        }
 
        unlock_device_hotplug();
        }
 
        unlock_device_hotplug();
-       mutex_unlock(&pm_mutex);
+       mutex_unlock(&system_transition_mutex);
 
        return error;
 }
 
        return error;
 }
index e4ced883d8de1ca9280fd18c43fafa4ce1c20e76..8fb44dec9ad75799e9269504ddea7083db654f9b 100644 (file)
@@ -294,7 +294,7 @@ void kernel_power_off(void)
 }
 EXPORT_SYMBOL_GPL(kernel_power_off);
 
 }
 EXPORT_SYMBOL_GPL(kernel_power_off);
 
-static DEFINE_MUTEX(reboot_mutex);
+DEFINE_MUTEX(system_transition_mutex);
 
 /*
  * Reboot system call: for obvious reasons only root may call it,
 
 /*
  * Reboot system call: for obvious reasons only root may call it,
@@ -338,7 +338,7 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
        if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
                cmd = LINUX_REBOOT_CMD_HALT;
 
        if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
                cmd = LINUX_REBOOT_CMD_HALT;
 
-       mutex_lock(&reboot_mutex);
+       mutex_lock(&system_transition_mutex);
        switch (cmd) {
        case LINUX_REBOOT_CMD_RESTART:
                kernel_restart(NULL);
        switch (cmd) {
        case LINUX_REBOOT_CMD_RESTART:
                kernel_restart(NULL);
@@ -389,7 +389,7 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
                ret = -EINVAL;
                break;
        }
                ret = -EINVAL;
                break;
        }
-       mutex_unlock(&reboot_mutex);
+       mutex_unlock(&system_transition_mutex);
        return ret;
 }
 
        return ret;
 }
 
index 1521100f1e63b729bba37e21723a312950d688d8..e47f9e4674082445cb4ea6c5ea74398bc9bc9136 100644 (file)
@@ -155,16 +155,17 @@ static inline void set_pcppage_migratetype(struct page *page, int migratetype)
  * The following functions are used by the suspend/hibernate code to temporarily
  * change gfp_allowed_mask in order to avoid using I/O during memory allocations
  * while devices are suspended.  To avoid races with the suspend/hibernate code,
  * The following functions are used by the suspend/hibernate code to temporarily
  * change gfp_allowed_mask in order to avoid using I/O during memory allocations
  * while devices are suspended.  To avoid races with the suspend/hibernate code,
- * they should always be called with pm_mutex held (gfp_allowed_mask also should
- * only be modified with pm_mutex held, unless the suspend/hibernate code is
- * guaranteed not to run in parallel with that modification).
+ * they should always be called with system_transition_mutex held
+ * (gfp_allowed_mask also should only be modified with system_transition_mutex
+ * held, unless the suspend/hibernate code is guaranteed not to run in parallel
+ * with that modification).
  */
 
 static gfp_t saved_gfp_mask;
 
 void pm_restore_gfp_mask(void)
 {
  */
 
 static gfp_t saved_gfp_mask;
 
 void pm_restore_gfp_mask(void)
 {
-       WARN_ON(!mutex_is_locked(&pm_mutex));
+       WARN_ON(!mutex_is_locked(&system_transition_mutex));
        if (saved_gfp_mask) {
                gfp_allowed_mask = saved_gfp_mask;
                saved_gfp_mask = 0;
        if (saved_gfp_mask) {
                gfp_allowed_mask = saved_gfp_mask;
                saved_gfp_mask = 0;
@@ -173,7 +174,7 @@ void pm_restore_gfp_mask(void)
 
 void pm_restrict_gfp_mask(void)
 {
 
 void pm_restrict_gfp_mask(void)
 {
-       WARN_ON(!mutex_is_locked(&pm_mutex));
+       WARN_ON(!mutex_is_locked(&system_transition_mutex));
        WARN_ON(saved_gfp_mask);
        saved_gfp_mask = gfp_allowed_mask;
        gfp_allowed_mask &= ~(__GFP_IO | __GFP_FS);
        WARN_ON(saved_gfp_mask);
        saved_gfp_mask = gfp_allowed_mask;
        gfp_allowed_mask &= ~(__GFP_IO | __GFP_FS);