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