| commit b94887bbc0621e1e8402e7f0ec4bc3adf46c9a6e |
| Author: Rafael J. Wysocki <rjw@sisk.pl> |
| Date: Fri Feb 17 12:42:08 2012 -0500 |
| |
| Freeze all filesystems during system suspend and (kernel-driven) |
| hibernation by calling freeze_supers() for all superblocks and thaw |
| them during the subsequent resume with the help of thaw_supers(). |
| |
| This makes filesystems stay in a consistent state in case something |
| goes wrong between system suspend (or hibernation) and the subsequent |
| resume (e.g. journal replays won't be necessary in those cases). In |
| particular, this should help to solve a long-standing issue that, in |
| some cases, during resume from hibernation the boot loader causes the |
| journal to be replied for the filesystem containing the kernel image |
| and/or initrd causing it to become inconsistent with the information |
| stored in the hibernation image. |
| |
| The user-space-driven hibernation (s2disk) is not covered by this |
| change, because the freezing of filesystems prevents s2disk from |
| accessing device special files it needs to do its job. |
| |
| This change is based on earlier work by Nigel Cunningham. |
| |
| Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> |
| |
| Rebased to 3.3-rc3 by Josh Boyer <jwboyer@redhat.com> |
| |
| diff --git a/fs/super.c b/fs/super.c |
| index 6015c02..c8057fa 100644 |
| |
| |
| @@ -594,6 +594,79 @@ void iterate_supers_type(struct file_system_type *type, |
| EXPORT_SYMBOL(iterate_supers_type); |
| |
| /** |
| + * thaw_supers - call thaw_super() for all superblocks |
| + */ |
| +void thaw_supers(void) |
| +{ |
| + struct super_block *sb, *p = NULL; |
| + |
| + spin_lock(&sb_lock); |
| + list_for_each_entry(sb, &super_blocks, s_list) { |
| + if (hlist_unhashed(&sb->s_instances)) |
| + continue; |
| + sb->s_count++; |
| + spin_unlock(&sb_lock); |
| + |
| + if (sb->s_flags & MS_FROZEN) { |
| + thaw_super(sb); |
| + sb->s_flags &= ~MS_FROZEN; |
| + } |
| + |
| + spin_lock(&sb_lock); |
| + if (p) |
| + __put_super(p); |
| + p = sb; |
| + } |
| + if (p) |
| + __put_super(p); |
| + spin_unlock(&sb_lock); |
| +} |
| + |
| +/** |
| + * freeze_supers - call freeze_super() for all superblocks |
| + */ |
| +int freeze_supers(void) |
| +{ |
| + struct super_block *sb, *p = NULL; |
| + int error = 0; |
| + |
| + spin_lock(&sb_lock); |
| + /* |
| + * Freeze in reverse order so filesystems depending on others are |
| + * frozen in the right order (eg. loopback on ext3). |
| + */ |
| + list_for_each_entry_reverse(sb, &super_blocks, s_list) { |
| + if (hlist_unhashed(&sb->s_instances)) |
| + continue; |
| + sb->s_count++; |
| + spin_unlock(&sb_lock); |
| + |
| + if (sb->s_root && sb->s_frozen != SB_FREEZE_TRANS |
| + && !(sb->s_flags & MS_RDONLY)) { |
| + error = freeze_super(sb); |
| + if (!error) |
| + sb->s_flags |= MS_FROZEN; |
| + } |
| + |
| + spin_lock(&sb_lock); |
| + if (error) |
| + break; |
| + if (p) |
| + __put_super(p); |
| + p = sb; |
| + } |
| + if (p) |
| + __put_super(p); |
| + spin_unlock(&sb_lock); |
| + |
| + if (error) |
| + thaw_supers(); |
| + |
| + return error; |
| +} |
| + |
| + |
| +/** |
| * get_super - get the superblock of a device |
| * @bdev: device to get the superblock for |
| * |
| diff --git a/include/linux/fs.h b/include/linux/fs.h |
| index 386da09..a164f4a 100644 |
| |
| |
| @@ -210,6 +210,7 @@ struct inodes_stat_t { |
| #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ |
| #define MS_I_VERSION (1<<23) /* Update inode I_version field */ |
| #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ |
| +#define MS_FROZEN (1<<25) /* Frozen filesystem */ |
| #define MS_NOSEC (1<<28) |
| #define MS_BORN (1<<29) |
| #define MS_ACTIVE (1<<30) |
| @@ -2501,6 +2502,8 @@ extern void drop_super(struct super_block *sb); |
| extern void iterate_supers(void (*)(struct super_block *, void *), void *); |
| extern void iterate_supers_type(struct file_system_type *, |
| void (*)(struct super_block *, void *), void *); |
| +extern int freeze_supers(void); |
| +extern void thaw_supers(void); |
| |
| extern int dcache_dir_open(struct inode *, struct file *); |
| extern int dcache_dir_close(struct inode *, struct file *); |
| diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c |
| index 6d6d288..492fc62 100644 |
| |
| |
| @@ -626,12 +626,17 @@ int hibernate(void) |
| if (error) |
| goto Finish; |
| |
| - error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); |
| + error = freeze_supers(); |
| if (error) |
| goto Thaw; |
| + |
| + error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); |
| + if (error) |
| + goto Thaw_fs; |
| + |
| if (freezer_test_done) { |
| freezer_test_done = false; |
| - goto Thaw; |
| + goto Thaw_fs; |
| } |
| |
| if (in_suspend) { |
| @@ -655,6 +660,8 @@ int hibernate(void) |
| pr_debug("PM: Image restored successfully.\n"); |
| } |
| |
| + Thaw_fs: |
| + thaw_supers(); |
| Thaw: |
| thaw_processes(); |
| Finish: |
| diff --git a/kernel/power/power.h b/kernel/power/power.h |
| index 21724ee..40d6f64 100644 |
| |
| |
| @@ -1,3 +1,4 @@ |
| +#include <linux/fs.h> |
| #include <linux/suspend.h> |
| #include <linux/suspend_ioctls.h> |
| #include <linux/utsname.h> |
| @@ -227,45 +228,3 @@ enum { |
| #define TEST_MAX (__TEST_AFTER_LAST - 1) |
| |
| extern int pm_test_level; |
| - |
| -#ifdef CONFIG_SUSPEND_FREEZER |
| -static inline int suspend_freeze_processes(void) |
| -{ |
| - int error; |
| - |
| - error = freeze_processes(); |
| - |
| - /* |
| - * freeze_processes() automatically thaws every task if freezing |
| - * fails. So we need not do anything extra upon error. |
| - */ |
| - if (error) |
| - goto Finish; |
| - |
| - error = freeze_kernel_threads(); |
| - |
| - /* |
| - * freeze_kernel_threads() thaws only kernel threads upon freezing |
| - * failure. So we have to thaw the userspace tasks ourselves. |
| - */ |
| - if (error) |
| - thaw_processes(); |
| - |
| - Finish: |
| - return error; |
| -} |
| - |
| -static inline void suspend_thaw_processes(void) |
| -{ |
| - thaw_processes(); |
| -} |
| -#else |
| -static inline int suspend_freeze_processes(void) |
| -{ |
| - return 0; |
| -} |
| - |
| -static inline void suspend_thaw_processes(void) |
| -{ |
| -} |
| -#endif |
| diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c |
| index 4fd51be..5f51fc7 100644 |
| |
| |
| @@ -29,6 +29,62 @@ |
| |
| #include "power.h" |
| |
| +#ifdef CONFIG_SUSPEND_FREEZER |
| + |
| +static inline int suspend_freeze_processes(void) |
| +{ |
| + int error; |
| + |
| + error = freeze_processes(); |
| + |
| + /* |
| + * freeze_processes() automatically thaws every task if freezing |
| + * fails. So we need not do anything extra upon error. |
| + */ |
| + |
| + if (error) |
| + goto Finish; |
| + |
| + error = freeze_supers(); |
| + if (error) { |
| + thaw_processes(); |
| + goto Finish; |
| + } |
| + |
| + error = freeze_kernel_threads(); |
| + |
| + /* |
| + * freeze_kernel_threads() thaws only kernel threads upon freezing |
| + * failure. So we have to thaw the userspace tasks ourselves. |
| + */ |
| + if (error) { |
| + thaw_supers(); |
| + thaw_processes(); |
| + } |
| + |
| +Finish: |
| + return error; |
| +} |
| + |
| +static inline void suspend_thaw_processes(void) |
| +{ |
| + thaw_supers(); |
| + thaw_processes(); |
| +} |
| + |
| +#else /* !CONFIG_SUSPEND_FREEZER */ |
| + |
| +static inline int suspend_freeze_processes(void) |
| +{ |
| + return 0; |
| +} |
| + |
| +static inline void suspend_thaw_processes(void) |
| +{ |
| +} |
| + |
| +#endif /* !CONFIG_SUSPEND_FREEZER */ |
| + |
| const char *const pm_states[PM_SUSPEND_MAX] = { |
| [PM_SUSPEND_STANDBY] = "standby", |
| [PM_SUSPEND_MEM] = "mem", |