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