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