safe-suspend-04.09.02.patch: This patch provides a power management enhancement that remounts all filesystems as read-only before suspending the system, and as read-write on resume. This feature adds protection in the case of accidentally exchanging batteries with a notebook or powering down a desktop PC while the system is in standby state. Signed-off-by: Geoff Levand for CELF --- fs/file_table.c | 32 +++++++++++ fs/jbd/journal.c | 4 + fs/super.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++ kernel/power/Kconfig | 7 ++ kernel/power/disk.c | 10 +++ kernel/power/main.c | 13 ++++ 6 files changed, 204 insertions(+) diff -ruN -X ./dontdiff tag_LINUX_2_6_8_1/fs/file_table.c branch_SAFE_SUSPEND/fs/file_table.c --- tag_LINUX_2_6_8_1/fs/file_table.c 2004-09-02 15:27:05.000000000 -0700 +++ branch_SAFE_SUSPEND/fs/file_table.c 2004-09-02 16:35:34.000000000 -0700 @@ -289,6 +289,38 @@ return 0; } +#ifdef CONFIG_SAFE_SUSPEND +/** + * suspend_fs_may_remount_ro - ask file system can be remount as RO + * on suspend. + * @sb: superblock in question + * + * returns 0 the file system CANNOT be remount as RO. + * returns 1 otherwise. + */ + +int suspend_fs_may_remount_ro(struct super_block *sb) +{ + struct list_head *p; + + /* Check that no files are currently pending for deleting. */ + file_list_lock(); + list_for_each(p, &sb->s_files) { + struct file *file = list_entry(p, struct file, f_list); + struct inode *inode = file->f_dentry->d_inode; + + /* File with pending delete? */ + if (inode->i_nlink == 0) + goto too_bad; + } + file_list_unlock(); + return 1; /* Tis' cool bro. */ +too_bad: + file_list_unlock(); + return 0; +} +#endif /* CONFIG_SAFE_SUSPEND */ + void __init files_init(unsigned long mempages) { int n; diff -ruN -X ./dontdiff tag_LINUX_2_6_8_1/fs/jbd/journal.c branch_SAFE_SUSPEND/fs/jbd/journal.c --- tag_LINUX_2_6_8_1/fs/jbd/journal.c 2004-09-02 15:27:08.000000000 -0700 +++ branch_SAFE_SUSPEND/fs/jbd/journal.c 2004-09-02 16:35:35.000000000 -0700 @@ -132,6 +132,10 @@ daemonize("kjournald"); +#ifdef CONFIG_SAFE_SUSPEND + /* We need forced remouting after user process frozen */ + current->flags |= PF_NOFREEZE; +#endif /* Set up an interval timer which can be used to trigger a commit wakeup after the commit interval expires */ init_timer(&timer); diff -ruN -X ./dontdiff tag_LINUX_2_6_8_1/fs/super.c branch_SAFE_SUSPEND/fs/super.c --- tag_LINUX_2_6_8_1/fs/super.c 2004-09-02 15:27:05.000000000 -0700 +++ branch_SAFE_SUSPEND/fs/super.c 2004-09-02 16:35:34.000000000 -0700 @@ -558,6 +558,144 @@ pdflush_operation(do_emergency_remount, 0); } +#ifdef CONFIG_SAFE_SUSPEND +#define DEBUG 1 + +extern int suspend_fs_may_remount_ro(struct super_block *sb); + +static +int suspend_do_remount_sb(struct super_block *sb) +{ + int retval=0; + int flags; + + if (sb->s_flags & MS_RDONLY) + return 0; + + shrink_dcache_sb(sb); + fsync_super(sb); + + /* make sure there are no files with pending delete */ + if (!suspend_fs_may_remount_ro(sb)) + return -EBUSY; + + if (sb->s_op->remount_fs) { + flags = MS_RDONLY; + lock_super(sb); + retval = sb->s_op->remount_fs(sb, &flags, NULL); + unlock_super(sb); + } + return retval; +} + +/** + * suspend_remount - make filesystems states clean on suspend. + */ + +struct suspend_rw_fs { + struct list_head list; + struct super_block *sb; +}; + +static LIST_HEAD(suspend_rw_fs_list); + +static spinlock_t suspend_fs_list_lock = SPIN_LOCK_UNLOCKED; + + +int suspend_remount(void) +{ + struct super_block *sb; + int retval; + int errcount=0; + struct suspend_rw_fs *p; +#ifdef DEBUG + char buf[BDEVNAME_SIZE]; +#endif + + spin_lock(&sb_lock); + list_for_each_entry(sb, &super_blocks, s_list) { + sb->s_count++; + spin_unlock(&sb_lock); + down_read(&sb->s_umount); + if (sb->s_root && sb->s_bdev && !(sb->s_flags & MS_RDONLY)) { + lock_kernel(); + retval = suspend_do_remount_sb(sb); + unlock_kernel(); + if (retval==0) { + p = (struct suspend_rw_fs*) + kmalloc(sizeof(struct suspend_rw_fs), GFP_KERNEL); + + spin_lock(&suspend_fs_list_lock); + p->sb = sb; + list_add(&p->list, &suspend_rw_fs_list); + spin_unlock(&suspend_fs_list_lock); +#ifdef DEBUG + printk("suspend_remount:%s \n", + bdevname(sb->s_bdev, buf)); +#endif + } + else { + errcount ++; +#ifdef DEBUG + printk("suspend_remount:%s (%d)\n", + bdevname(sb->s_bdev, buf), retval); +#endif + } + } + drop_super(sb); + spin_lock(&sb_lock); + } + spin_unlock(&sb_lock); + return errcount; +} + +/** + * resume_remount - restore filesystems states on resume. + */ + +int resume_remount(void) +{ + struct super_block *sb; + struct list_head *p, *n; + struct suspend_rw_fs *f; + int retval; + int flags; + int errcount=0; + char buf[BDEVNAME_SIZE]; + + spin_lock(&suspend_fs_list_lock); + list_for_each_safe(p, n, &suspend_rw_fs_list) { + f = list_entry(p, struct suspend_rw_fs, list); + sb = f-> sb; + list_del(p); + spin_unlock(&suspend_fs_list_lock); + kfree(f); + + if (sb->s_op->remount_fs) { + flags=0; + lock_super(sb); + retval = sb->s_op->remount_fs(sb, &flags, NULL); + unlock_super(sb); + if (retval) { + printk("resume_remount: %s (%d)\n", + bdevname(sb->s_bdev, buf), retval); + errcount++; + } +#ifdef DEBUG + else { + printk("resume_remount: %s \n", + bdevname(sb->s_bdev, buf)); + } +#endif + } + spin_lock(&suspend_fs_list_lock); + } + spin_unlock(&suspend_fs_list_lock); + return errcount; +} + +#endif /* CONFIG_SAFE_SUSPEND */ + /* * Unnamed block devices are dummy devices used by virtual * filesystems which don't use real block-devices. -- jrs diff -ruN -X ./dontdiff tag_LINUX_2_6_8_1/kernel/power/disk.c branch_SAFE_SUSPEND/kernel/power/disk.c --- tag_LINUX_2_6_8_1/kernel/power/disk.c 2004-09-02 15:27:20.000000000 -0700 +++ branch_SAFE_SUSPEND/kernel/power/disk.c 2004-09-02 16:35:48.000000000 -0700 @@ -28,6 +28,10 @@ extern int pmdisk_read(void); extern int pmdisk_restore(void); extern int pmdisk_free(void); +#ifdef CONFIG_SAFE_SUSPEND +extern int suspend_remount(void); +extern int resume_remount(void); +#endif /** @@ -93,6 +97,9 @@ if (pm_ops && pm_ops->finish) pm_ops->finish(PM_SUSPEND_DISK); } +#ifdef CONFIG_SAFE_SUSPEND + (void) resume_remount(); +#endif } static void finish(void) @@ -116,6 +123,9 @@ goto Thaw; } +#ifdef CONFIG_SAFE_SUSPEND + (void) suspend_remount(); +#endif if (pm_disk_mode == PM_DISK_PLATFORM) { if (pm_ops && pm_ops->prepare) { if ((error = pm_ops->prepare(PM_SUSPEND_DISK))) diff -ruN -X ./dontdiff tag_LINUX_2_6_8_1/kernel/power/Kconfig branch_SAFE_SUSPEND/kernel/power/Kconfig --- tag_LINUX_2_6_8_1/kernel/power/Kconfig 2004-09-02 15:27:20.000000000 -0700 +++ branch_SAFE_SUSPEND/kernel/power/Kconfig 2004-09-02 16:35:48.000000000 -0700 @@ -18,6 +18,13 @@ will issue the hlt instruction if nothing is to be done, thereby sending the processor to sleep and saving power. +config SAFE_SUSPEND + bool "Mark FS clean on Suspend (EXPERIMENTAL)" + depends on EXPERIMENTAL && PM + ---help--- + Mark Filesytem states clean on Suspend. + + config SOFTWARE_SUSPEND bool "Software Suspend (EXPERIMENTAL)" depends on EXPERIMENTAL && PM && SWAP diff -ruN -X ./dontdiff tag_LINUX_2_6_8_1/kernel/power/main.c branch_SAFE_SUSPEND/kernel/power/main.c --- tag_LINUX_2_6_8_1/kernel/power/main.c 2004-09-02 15:27:20.000000000 -0700 +++ branch_SAFE_SUSPEND/kernel/power/main.c 2004-09-02 16:35:48.000000000 -0700 @@ -18,6 +18,10 @@ #include #include +#ifdef CONFIG_SAFE_SUSPEND +extern int suspend_remount(void); +extern int resume_remount(void); +#endif #include "power.h" @@ -64,6 +68,9 @@ goto Thaw; } +#ifdef CONFIG_SAFE_SUSPEND + (void) suspend_remount(); +#endif if (pm_ops->prepare) { if ((error = pm_ops->prepare(state))) goto Thaw; @@ -76,6 +83,9 @@ if (pm_ops->finish) pm_ops->finish(state); Thaw: +#ifdef CONFIG_SAFE_SUSPEND + (void) resume_remount(); +#endif thaw_processes(); pm_restore_console(); return error; @@ -111,6 +121,9 @@ device_resume(); if (pm_ops && pm_ops->finish) pm_ops->finish(state); +#ifdef CONFIG_SAFE_SUSPEND + (void) resume_remount(); +#endif thaw_processes(); pm_restore_console(); }