============================================= safe-suspend-04.12.10.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 | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++ kernel/power/Kconfig | 6 ++ kernel/power/disk.c | 10 +++ kernel/power/main.c | 13 ++++ 6 files changed, 216 insertions(+) diff -pruN -X ../cvs-admin/dontdiff branch_PM_ON_EBONY/fs/file_table.c branch_SAFE_SUSPEND/fs/file_table.c --- branch_PM_ON_EBONY/fs/file_table.c 2004-10-28 20:46:09.000000000 -0700 +++ branch_SAFE_SUSPEND/fs/file_table.c 2004-10-29 00:45:03.000000000 -0700 @@ -244,6 +244,38 @@ too_bad: 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 -pruN -X ../cvs-admin/dontdiff branch_PM_ON_EBONY/fs/jbd/journal.c branch_SAFE_SUSPEND/fs/jbd/journal.c --- branch_PM_ON_EBONY/fs/jbd/journal.c 2004-10-28 20:46:28.000000000 -0700 +++ branch_SAFE_SUSPEND/fs/jbd/journal.c 2004-10-29 00:45:17.000000000 -0700 @@ -133,6 +133,10 @@ int kjournald(void *arg) 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 -pruN -X ../cvs-admin/dontdiff branch_PM_ON_EBONY/fs/super.c branch_SAFE_SUSPEND/fs/super.c --- branch_PM_ON_EBONY/fs/super.c 2004-10-28 20:46:12.000000000 -0700 +++ branch_SAFE_SUSPEND/fs/super.c 2004-10-29 00:45:06.000000000 -0700 @@ -580,6 +580,157 @@ void emergency_remount(void) 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) { + return -ENOSYS; + } + flags = MS_RDONLY; + lock_super(sb); + retval = sb->s_op->remount_fs(sb, &flags, NULL); + if (!retval) { + sb->s_flags |= MS_RDONLY; + } + 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=0; + 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)) { + p = (struct suspend_rw_fs*) + kmalloc(sizeof(struct suspend_rw_fs), GFP_ATOMIC); + if (p != NULL) { + lock_kernel(); + retval = suspend_do_remount_sb(sb); + unlock_kernel(); + if (retval!=0) { + kfree(p); + goto err; + } + 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 { + err: + errcount ++; +#ifdef DEBUG + printk("suspend_remount:%s (err: %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++; + } + else { +#ifdef DEBUG + printk("resume_remount: %s \n", + bdevname(sb->s_bdev, buf)); +#endif + /* We can unconditionally remove + * MS_RDONLY flags here, because all + * members of suspend_rw_fs_list were + * originally writable */ + sb->s_flags &= ~MS_RDONLY; + } + } + 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 -pruN -X ../cvs-admin/dontdiff branch_PM_ON_EBONY/kernel/power/disk.c branch_SAFE_SUSPEND/kernel/power/disk.c --- branch_PM_ON_EBONY/kernel/power/disk.c 2004-10-28 20:48:26.000000000 -0700 +++ branch_SAFE_SUSPEND/kernel/power/disk.c 2004-10-29 00:47:04.000000000 -0700 @@ -26,6 +26,10 @@ extern int swsusp_write(void); extern int swsusp_read(void); extern int swsusp_resume(void); extern int swsusp_free(void); +#ifdef CONFIG_SAFE_SUSPEND +extern int suspend_remount(void); +extern int resume_remount(void); +#endif static int noresume = 0; @@ -98,6 +102,9 @@ static inline void platform_finish(void) if (pm_ops && pm_ops->finish) pm_ops->finish(PM_SUSPEND_DISK); } +#ifdef CONFIG_SAFE_SUSPEND + (void) resume_remount(); +#endif } static void finish(void) @@ -122,6 +129,9 @@ static int prepare(void) 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 -pruN -X ../cvs-admin/dontdiff branch_PM_ON_EBONY/kernel/power/Kconfig branch_SAFE_SUSPEND/kernel/power/Kconfig --- branch_PM_ON_EBONY/kernel/power/Kconfig 2004-10-28 20:48:26.000000000 -0700 +++ branch_SAFE_SUSPEND/kernel/power/Kconfig 2004-10-29 00:47:04.000000000 -0700 @@ -25,6 +25,12 @@ config PM_DEBUG code. This is helpful when debugging and reporting various PM bugs, like suspend support. +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 -pruN -X ../cvs-admin/dontdiff branch_PM_ON_EBONY/kernel/power/main.c branch_SAFE_SUSPEND/kernel/power/main.c --- branch_PM_ON_EBONY/kernel/power/main.c 2004-10-28 20:48:26.000000000 -0700 +++ branch_SAFE_SUSPEND/kernel/power/main.c 2004-10-29 00:47:04.000000000 -0700 @@ -16,6 +16,10 @@ #include #include +#ifdef CONFIG_SAFE_SUSPEND +extern int suspend_remount(void); +extern int resume_remount(void); +#endif #include "power.h" @@ -60,6 +64,9 @@ static int suspend_prepare(u32 state) goto Thaw; } +#ifdef CONFIG_SAFE_SUSPEND + (void) suspend_remount(); +#endif if (pm_ops->prepare) { if ((error = pm_ops->prepare(state))) goto Thaw; @@ -72,6 +79,9 @@ static int suspend_prepare(u32 state) if (pm_ops->finish) pm_ops->finish(state); Thaw: +#ifdef CONFIG_SAFE_SUSPEND + (void) resume_remount(); +#endif thaw_processes(); pm_restore_console(); return error; @@ -107,6 +117,9 @@ static void suspend_finish(u32 state) 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(); }