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