Dave Jones 90fada3
From: Rafael J. Wysocki <rjw@sisk.pl>
Dave Jones 90fada3
Dave Jones 90fada3
Freeze all filesystems during system suspend and (kernel-driven)
Dave Jones 90fada3
hibernation by calling freeze_supers() for all superblocks and thaw
Dave Jones 90fada3
them during the subsequent resume with the help of thaw_supers().
Dave Jones 90fada3
Dave Jones 90fada3
This makes filesystems stay in a consistent state in case something
Dave Jones 90fada3
goes wrong between system suspend (or hibernation) and the subsequent
Dave Jones 90fada3
resume (e.g. journal replays won't be necessary in those cases).  In
Dave Jones 90fada3
particular, this should help to solve a long-standing issue that, in
Dave Jones 90fada3
some cases, during resume from hibernation the boot loader causes the
Dave Jones 90fada3
journal to be replied for the filesystem containing the kernel image
Dave Jones 90fada3
and/or initrd causing it to become inconsistent with the information
Dave Jones 90fada3
stored in the hibernation image.
Dave Jones 90fada3
Dave Jones 90fada3
The user-space-driven hibernation (s2disk) is not covered by this
Dave Jones 90fada3
change, because the freezing of filesystems prevents s2disk from
Dave Jones 90fada3
accessing device special files it needs to do its job.
Dave Jones 90fada3
Dave Jones 90fada3
This change is based on earlier work by Nigel Cunningham.
Dave Jones 90fada3
Dave Jones 90fada3
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Dave Jones 90fada3
---
Dave Jones 90fada3
 fs/super.c               |   73 +++++++++++++++++++++++++++++++++++++++++++++++
Dave Jones 90fada3
 include/linux/fs.h       |    3 +
Dave Jones 90fada3
 kernel/power/hibernate.c |   11 +++++--
Dave Jones 90fada3
 kernel/power/power.h     |   23 --------------
Dave Jones 90fada3
 kernel/power/suspend.c   |   42 +++++++++++++++++++++++++++
Dave Jones 90fada3
 5 files changed, 128 insertions(+), 24 deletions(-)
Dave Jones 90fada3
Dave Jones 90fada3
Index: linux/include/linux/fs.h
Dave Jones 90fada3
===================================================================
Dave Jones 90fada3
--- linux.orig/include/linux/fs.h
Dave Jones 90fada3
+++ linux/include/linux/fs.h
Dave Jones 90fada3
@@ -210,6 +210,7 @@ struct inodes_stat_t {
Dave Jones 90fada3
 #define MS_KERNMOUNT	(1<<22) /* this is a kern_mount call */
Dave Jones 90fada3
 #define MS_I_VERSION	(1<<23) /* Update inode I_version field */
Dave Jones 90fada3
 #define MS_STRICTATIME	(1<<24) /* Always perform atime updates */
Dave Jones 90fada3
+#define MS_FROZEN	(1<<25) /* Frozen filesystem */
Dave Jones 90fada3
 #define MS_NOSEC	(1<<28)
Dave Jones 90fada3
 #define MS_BORN		(1<<29)
Dave Jones 90fada3
 #define MS_ACTIVE	(1<<30)
Dave Jones 90fada3
@@ -2501,6 +2502,8 @@ extern void drop_super(struct super_bloc
Dave Jones 90fada3
 extern void iterate_supers(void (*)(struct super_block *, void *), void *);
Dave Jones 90fada3
 extern void iterate_supers_type(struct file_system_type *,
Dave Jones 90fada3
 			        void (*)(struct super_block *, void *), void *);
Dave Jones 90fada3
+extern int freeze_supers(void);
Dave Jones 90fada3
+extern void thaw_supers(void);
Dave Jones 90fada3
 
Dave Jones 90fada3
 extern int dcache_dir_open(struct inode *, struct file *);
Dave Jones 90fada3
 extern int dcache_dir_close(struct inode *, struct file *);
Dave Jones 90fada3
Index: linux/fs/super.c
Dave Jones 90fada3
===================================================================
Dave Jones 90fada3
--- linux.orig/fs/super.c
Dave Jones 90fada3
+++ linux/fs/super.c
Dave Jones 90fada3
@@ -594,6 +594,79 @@ void iterate_supers_type(struct file_sys
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) {
Dave Jones 90fada3
+		if (list_empty(&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) {
Dave Jones 90fada3
+		if (list_empty(&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
  *	
Dave Jones 90fada3
Index: linux/kernel/power/power.h
Dave Jones 90fada3
===================================================================
Dave Jones 90fada3
--- linux.orig/kernel/power/power.h
Dave Jones 90fada3
+++ linux/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>
Dave Jones 90fada3
@@ -227,25 +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
-{
Dave Jones 90fada3
-	int error = freeze_processes();
Dave Jones 90fada3
-	return error ? : freeze_kernel_threads();
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
Dave Jones 90fada3
Index: linux/kernel/power/suspend.c
Dave Jones 90fada3
===================================================================
Dave Jones 90fada3
--- linux.orig/kernel/power/suspend.c
Dave Jones 90fada3
+++ linux/kernel/power/suspend.c
Dave Jones 90fada3
@@ -29,6 +29,48 @@
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();
Dave Jones 90fada3
+	if (error)
Dave Jones 90fada3
+		return error;
Dave Jones 90fada3
+
Dave Jones 90fada3
+	error = freeze_supers();
Dave Jones 90fada3
+	if (error) {
Dave Jones 90fada3
+		thaw_processes();
Dave Jones 90fada3
+		return error;
Dave Jones 90fada3
+	}
Dave Jones 90fada3
+
Dave Jones 90fada3
+	error = freeze_kernel_threads();
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
+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",
Dave Jones 90fada3
Index: linux/kernel/power/hibernate.c
Dave Jones 90fada3
===================================================================
Dave Jones 90fada3
--- linux.orig/kernel/power/hibernate.c
Dave Jones 90fada3
+++ linux/kernel/power/hibernate.c
Dave Jones 90fada3
@@ -626,12 +626,17 @@ int hibernate(void)
Dave Jones 90fada3
 	if (error)
Dave Jones 90fada3
 		goto Finish;
Dave Jones 90fada3
 
Dave Jones 90fada3
-	error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
Dave Jones 90fada3
+	error = freeze_supers();
Dave Jones 90fada3
 	if (error)
Dave Jones 90fada3
 		goto Thaw;
Dave Jones 90fada3
+
Dave Jones 90fada3
+	error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
Dave Jones 90fada3
+	if (error)
Dave Jones 90fada3
+		goto Thaw_fs;
Dave Jones 90fada3
+
Dave Jones 90fada3
 	if (freezer_test_done) {
Dave Jones 90fada3
 		freezer_test_done = false;
Dave Jones 90fada3
-		goto Thaw;
Dave Jones 90fada3
+		goto Thaw_fs;
Dave Jones 90fada3
 	}
Dave Jones 90fada3
 
Dave Jones 90fada3
 	if (in_suspend) {
Dave Jones 90fada3
@@ -655,6 +660,8 @@ int hibernate(void)
Dave Jones 90fada3
 		pr_debug("PM: Image restored successfully.\n");
Dave Jones 90fada3
 	}
Dave Jones 90fada3
 
Dave Jones 90fada3
+ Thaw_fs:
Dave Jones 90fada3
+	thaw_supers();
Dave Jones 90fada3
  Thaw:
Dave Jones 90fada3
 	thaw_processes();
Dave Jones 90fada3
  Finish:
Dave Jones 90fada3