fs: use freeze_fs on suspend/hibernate
Subsystems (9)
cover fs: use freeze_fs on suspend/hibernate
This is a followup from the original RFC which proposed to start to kill kthread freezing all together [0]. Instead of going straight out to the jugular for kthread freezing this series only addresses killing freezer calls on filesystems which implement freeze_fs, after we let the kernel freeze these filesystems for us on suspend.
This approach puts on a slow but steady path towards the original goal though. Each subsystem could look for similar solutions. Even with filesystems we're not all done yet, after this we'll still have to decide what to do about filesystems which do not implement freeze_fs().
Motivation and problem:
kthreads have some semantics for freezing, which helps the kernel freeze them when a system is going to suspend or hibernation. These semantics are not well defined though, and it actually turns out pretty hard to get it right.
Without a proper solution suspend and hibernation are fragile on filesystems, it can easily break suspend and fixing such issues are in no way trivial [1] [2].
Proposed solution:
Instead of fixing such semantics and trying to get all filesystems to do it right, we can easily do away with all freezing calls if the filesystem implements a proper freeze_fs() callback. The following 9 filesystems have freeze_fs() implemented as such we can let the kernel issue the callback upon suspend and thaw on resume automatically on our behalf.
o xfs
o reiserfs
o nilfs2
o jfs
o f2fs
o ext4
o ext2
o btrfs
Of these, the following have freezer helpers, which can then be removed after the kernel automaticaly calls freeze_fs for us on suspend:
o xfs
o nilfs2
o jfs
o f2fs
o ext4
I've tested this on a system with ext4 and XFS, and have let 0-day go at without issues. I have branches availabe for linux-next [3] and Linus' latest tree [4].
Further testing, thoughts, reviews, flames are all equally appreciated.
[0] https://lkml.kernel.org/r/[email protected]
[1] https://bugzilla.suse.com/show_bug.cgi?id=1043449
[2] https://lkml.kernel.org/r/[email protected]
[3] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20171129-fs-freeze-cleanup
[4] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux.git/log/?h=20171129-fs-freeze-cleanup
Luis R. Rodriguez (11):
fs: provide unlocked helper for freeze_super()
fs: provide unlocked helper thaw_super()
fs: add frozen sb state helpers
fs: distinguish between user initiated freeze and kernel initiated
freeze
fs: add iterate_supers_excl() and iterate_supers_reverse_excl()
fs: freeze on suspend and thaw on resume
xfs: remove not needed freezing calls
ext4: remove not needed freezing calls
f2fs: remove not needed freezing calls
nilfs2: remove not needed freezing calls
jfs: remove not needed freezing calls
fs/ext4/ext4_jbd2.c | 2 +- fs/ext4/super.c | 2 - fs/f2fs/gc.c | 5 +- fs/f2fs/segment.c | 6 +- fs/jfs/jfs_logmgr.c | 11 +- fs/jfs/jfs_txnmgr.c | 31 ++--- fs/nilfs2/segment.c | 48 ++++---- fs/super.c | 320 ++++++++++++++++++++++++++++++++++++++++--------- fs/xfs/xfs_trans.c | 2 +- fs/xfs/xfs_trans_ail.c | 7 +- include/linux/fs.h | 63 +++++++++- kernel/power/process.c | 15 ++- 12 files changed, 378 insertions(+), 134 deletions(-)
Signature · 1 line
2.15.0
PATCH 1/11 fs: provide unlocked helper for freeze_super()
freeze_super() holds a write lock, however we wish to also enable callers which already hold the write lock. To do this provide a helper and make freeze_super() use it. This way, all that freeze_super() does now is lock handling and active count management.
This change has no functional changes.
--- fs/super.c | 100 +++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 55 insertions(+), 45 deletions(-)
fs/super.c +55 -45
@@ -1387,59 +1387,20 @@ static void sb_freeze_unlock(struct super_block *sb)percpu_up_write (sb->s_writers.rw_sem + level); } -/** -* freeze_super - lock the filesystem and force it into a consistent state -* @sb: the super to lock -* -* Syncs the super to make sure the filesystem is consistent and calls the fs's -* freeze_fs. Subsequent calls to this without first thawing the fs will return -* -EBUSY. -* -* During this function, sb->s_writers.frozen goes through these values: -* -* SB_UNFROZEN: File system is normal, all writes progress as usual. -* -* SB_FREEZE_WRITE: The file system is in the process of being frozen. New -* writes should be blocked, though page faults are still allowed. We wait for -* all writes to complete and then proceed to the next stage. -* -* SB_FREEZE_PAGEFAULT: Freezing continues. Now also page faults are blocked -* but internal fs threads can still modify the filesystem (although they -* should not dirty new pages or inodes), writeback can run etc. After waiting -* for all running page faults we sync the filesystem which will clean all -* dirty pages and inodes (no new dirty pages or inodes can be created when -* sync is running). -* -* SB_FREEZE_FS: The file system is frozen. Now all internal sources of fs -* modification are blocked (e.g. XFS preallocation truncation on inode -* reclaim). This is usually implemented by blocking new transactions for -* filesystems that have them and need this additional guard. After all -* internal writers are finished we call ->freeze_fs() to finish filesystem -* freezing. Then we transition to SB_FREEZE_COMPLETE state. This state is -* mostly auxiliary for filesystems to verify they do not modify frozen fs. -* -* sb->s_writers.frozen is protected by sb->s_umount. -*/ -int freeze_super (struct super_block *sb) +/* Caller takes lock and handles active count */ +static int freeze_locked_super (struct super_block *sb) {int ret; -atomic_inc (&sb->s_active); -down_write (&sb->s_umount); -if (sb->s_writers.frozen !=SB_UNFROZEN ) { -deactivate_locked_super (sb); +if (sb->s_writers.frozen !=SB_UNFROZEN )return -EBUSY ; - } -if (!(sb->s_flags &SB_BORN )) { -up_write (&sb->s_umount); +if (!(sb->s_flags &SB_BORN ))return 0 ;/* sic - it's "nothing to do" */ - }if (sb_rdonly (sb)) {/* Nothing to do really... */ sb->s_writers.frozen =SB_FREEZE_COMPLETE ; -up_write (&sb->s_umount);return 0 ; }
@@ -1468,7 +1429,6 @@ int freeze_super(struct super_block *sb) sb->s_writers.frozen =SB_UNFROZEN ;sb_freeze_unlock (sb);wake_up (&sb->s_writers.wait_unfrozen); -deactivate_locked_super (sb);return ret; } }
@@ -1478,9 +1438,59 @@ int freeze_super(struct super_block *sb)*/ sb->s_writers.frozen =SB_FREEZE_COMPLETE ;lockdep_sb_freeze_release (sb); -up_write (&sb->s_umount);return 0 ; } + +/** +* freeze_super - lock the filesystem and force it into a consistent state +* @sb: the super to lock +* +* Syncs the super to make sure the filesystem is consistent and calls the fs's +* freeze_fs. Subsequent calls to this without first thawing the fs will return +* -EBUSY. +* +* During this function, sb->s_writers.frozen goes through these values: +* +* SB_UNFROZEN: File system is normal, all writes progress as usual. +* +* SB_FREEZE_WRITE: The file system is in the process of being frozen. New +* writes should be blocked, though page faults are still allowed. We wait for +* all writes to complete and then proceed to the next stage. +* +* SB_FREEZE_PAGEFAULT: Freezing continues. Now also page faults are blocked +* but internal fs threads can still modify the filesystem (although they +* should not dirty new pages or inodes), writeback can run etc. After waiting +* for all running page faults we sync the filesystem which will clean all +* dirty pages and inodes (no new dirty pages or inodes can be created when +* sync is running). +* +* SB_FREEZE_FS: The file system is frozen. Now all internal sources of fs +* modification are blocked (e.g. XFS preallocation truncation on inode +* reclaim). This is usually implemented by blocking new transactions for +* filesystems that have them and need this additional guard. After all +* internal writers are finished we call ->freeze_fs() to finish filesystem +* freezing. Then we transition to SB_FREEZE_COMPLETE state. This state is +* mostly auxiliary for filesystems to verify they do not modify frozen fs. +* +* sb->s_writers.frozen is protected by sb->s_umount. +*/ +int freeze_super (struct super_block *sb) +{ +int error; + +atomic_inc (&sb->s_active); + +down_write (&sb->s_umount); + error =freeze_locked_super (sb); +if (error) { +deactivate_locked_super (sb); + goto out; + } +up_write (&sb->s_umount); + +out: +return error; +}EXPORT_SYMBOL (freeze_super);/**
Signature · 1 line
2.15.0
Re: [PATCH 01/11] fs: provide unlocked helper for freeze_super()
Quoted 9 lines · Luis R. Rodriguez
freeze_super()holds a write lock, however we wish to also enable callers which already hold the write lock. To do this provide a helper and makefreeze_super()use it. This way, all thatfreeze_super()does now is lock handling and active count management.This change has no functional changes.
Suggested-by Dave Chinner <[email protected]>Signed-off-by Luis R. Rodriguez <[email protected]>
Looks good to me. You can add:
Honza
Quoted 144 lines
--- fs/super.c | 100 +++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 55 insertions(+), 45 deletions(-)fs/super.c +55
-45@@ -1387,59 +1387,20 @@ static void sb_freeze_unlock(struct super_block *sb)percpu_up_write (sb->s_writers.rw_sem + level); } -/** -* freeze_super - lock the filesystem and force it into a consistent state -* @sb: the super to lock -* -* Syncs the super to make sure the filesystem is consistent and calls the fs's -* freeze_fs. Subsequent calls to this without first thawing the fs will return -* -EBUSY. -* -* During this function, sb->s_writers.frozen goes through these values: -* -* SB_UNFROZEN: File system is normal, all writes progress as usual. -* -* SB_FREEZE_WRITE: The file system is in the process of being frozen. New -* writes should be blocked, though page faults are still allowed. We wait for -* all writes to complete and then proceed to the next stage. -* -* SB_FREEZE_PAGEFAULT: Freezing continues. Now also page faults are blocked -* but internal fs threads can still modify the filesystem (although they -* should not dirty new pages or inodes), writeback can run etc. After waiting -* for all running page faults we sync the filesystem which will clean all -* dirty pages and inodes (no new dirty pages or inodes can be created when -* sync is running). -* -* SB_FREEZE_FS: The file system is frozen. Now all internal sources of fs -* modification are blocked (e.g. XFS preallocation truncation on inode -* reclaim). This is usually implemented by blocking new transactions for -* filesystems that have them and need this additional guard. After all -* internal writers are finished we call ->freeze_fs() to finish filesystem -* freezing. Then we transition to SB_FREEZE_COMPLETE state. This state is -* mostly auxiliary for filesystems to verify they do not modify frozen fs. -* -* sb->s_writers.frozen is protected by sb->s_umount. -*/ -int freeze_super (struct super_block *sb) +/* Caller takes lock and handles active count */ +static int freeze_locked_super (struct super_block *sb) {int ret; -atomic_inc (&sb->s_active); -down_write (&sb->s_umount); -if (sb->s_writers.frozen !=SB_UNFROZEN ) { -deactivate_locked_super (sb); +if (sb->s_writers.frozen !=SB_UNFROZEN )return -EBUSY ; - } -if (!(sb->s_flags &SB_BORN )) { -up_write (&sb->s_umount); +if (!(sb->s_flags &SB_BORN ))return 0 ;/* sic - it's "nothing to do" */ - }if (sb_rdonly (sb)) {/* Nothing to do really... */ sb->s_writers.frozen =SB_FREEZE_COMPLETE ; -up_write (&sb->s_umount);return 0 ; }@@ -1468,7 +1429,6 @@ int freeze_super(struct super_block *sb) sb->s_writers.frozen =SB_UNFROZEN ;sb_freeze_unlock (sb);wake_up (&sb->s_writers.wait_unfrozen); -deactivate_locked_super (sb);return ret; } }@@ -1478,9 +1438,59 @@ int freeze_super(struct super_block *sb)*/ sb->s_writers.frozen =SB_FREEZE_COMPLETE ;lockdep_sb_freeze_release (sb); -up_write (&sb->s_umount);return 0 ; } + +/** +* freeze_super - lock the filesystem and force it into a consistent state +* @sb: the super to lock +* +* Syncs the super to make sure the filesystem is consistent and calls the fs's +* freeze_fs. Subsequent calls to this without first thawing the fs will return +* -EBUSY. +* +* During this function, sb->s_writers.frozen goes through these values: +* +* SB_UNFROZEN: File system is normal, all writes progress as usual. +* +* SB_FREEZE_WRITE: The file system is in the process of being frozen. New +* writes should be blocked, though page faults are still allowed. We wait for +* all writes to complete and then proceed to the next stage. +* +* SB_FREEZE_PAGEFAULT: Freezing continues. Now also page faults are blocked +* but internal fs threads can still modify the filesystem (although they +* should not dirty new pages or inodes), writeback can run etc. After waiting +* for all running page faults we sync the filesystem which will clean all +* dirty pages and inodes (no new dirty pages or inodes can be created when +* sync is running). +* +* SB_FREEZE_FS: The file system is frozen. Now all internal sources of fs +* modification are blocked (e.g. XFS preallocation truncation on inode +* reclaim). This is usually implemented by blocking new transactions for +* filesystems that have them and need this additional guard. After all +* internal writers are finished we call ->freeze_fs() to finish filesystem +* freezing. Then we transition to SB_FREEZE_COMPLETE state. This state is +* mostly auxiliary for filesystems to verify they do not modify frozen fs. +* +* sb->s_writers.frozen is protected by sb->s_umount. +*/ +int freeze_super (struct super_block *sb) +{ +int error; + +atomic_inc (&sb->s_active); + +down_write (&sb->s_umount); + error =freeze_locked_super (sb); +if (error) { +deactivate_locked_super (sb); + goto out; + } +up_write (&sb->s_umount); + +out: +return error; +}EXPORT_SYMBOL (freeze_super);/** Signature · 1 line
2.15.0
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH 2/11 fs: provide unlocked helper thaw_super()
In reply to Luis R. Rodriguezthaw_super() hold a write lock, however we wish to also enable callers which already hold the write lock. To do this provide a helper and make thaw_super() use it. This way, all that thaw_super() does now is lock handling and active count management.
This change has no functional changes.
--- fs/super.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-)
fs/super.c +26 -13
@@ -1493,21 +1493,13 @@ int freeze_super(struct super_block *sb) }EXPORT_SYMBOL (freeze_super); -/** -* thaw_super -- unlock filesystem -* @sb: the super to thaw -* -* Unlocks the filesystem and marks it writeable again after freeze_super(). -*/ -int thaw_super (struct super_block *sb) +/* Caller takes lock and handles active count */ +static int thaw_locked_super (struct super_block *sb) {int error; -down_write (&sb->s_umount); -if (sb->s_writers.frozen !=SB_FREEZE_COMPLETE ) { -up_write (&sb->s_umount); +if (sb->s_writers.frozen !=SB_FREEZE_COMPLETE )return -EINVAL ; - }if (sb_rdonly (sb)) { sb->s_writers.frozen =SB_UNFROZEN ;
@@ -1522,7 +1514,6 @@ int thaw_super(struct super_block *sb)printk (KERN_ERR "VFS:Filesystem thaw failed\n" );lockdep_sb_freeze_release (sb); -up_write (&sb->s_umount);return error; } }
@@ -1531,7 +1522,29 @@ int thaw_super(struct super_block *sb)sb_freeze_unlock (sb); out:wake_up (&sb->s_writers.wait_unfrozen); -deactivate_locked_super (sb);return 0 ; } + +/** +* thaw_super -- unlock filesystem +* @sb: the super to thaw +* +* Unlocks the filesystem and marks it writeable again after freeze_super(). +*/ +int thaw_super (struct super_block *sb) +{ +int error; + +down_write (&sb->s_umount); + error =thaw_locked_super (sb); +if (error) { +up_write (&sb->s_umount); + goto out; + } + +deactivate_locked_super (sb); + +out: +return error; +}EXPORT_SYMBOL (thaw_super);
Signature · 1 line
2.15.0
Re: [PATCH 02/11] fs: provide unlocked helper thaw_super()
Quoted 9 lines · Luis R. Rodriguez
thaw_super()hold a write lock, however we wish to also enable callers which already hold the write lock. To do this provide a helper and makethaw_super()use it. This way, all thatthaw_super()does now is lock handling and active count management.This change has no functional changes.
Suggested-by Dave Chinner <[email protected]>Signed-off-by Luis R. Rodriguez <[email protected]>
Looks good to me. You can add:
Honza
Quoted 75 lines
--- fs/super.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-)fs/super.c +26
-13@@ -1493,21 +1493,13 @@ int freeze_super(struct super_block *sb) }EXPORT_SYMBOL (freeze_super); -/** -* thaw_super -- unlock filesystem -* @sb: the super to thaw -* -* Unlocks the filesystem and marks it writeable again after freeze_super(). -*/ -int thaw_super (struct super_block *sb) +/* Caller takes lock and handles active count */ +static int thaw_locked_super (struct super_block *sb) {int error; -down_write (&sb->s_umount); -if (sb->s_writers.frozen !=SB_FREEZE_COMPLETE ) { -up_write (&sb->s_umount); +if (sb->s_writers.frozen !=SB_FREEZE_COMPLETE )return -EINVAL ; - }if (sb_rdonly (sb)) { sb->s_writers.frozen =SB_UNFROZEN ;@@ -1522,7 +1514,6 @@ int thaw_super(struct super_block *sb)printk (KERN_ERR "VFS:Filesystem thaw failed\n" );lockdep_sb_freeze_release (sb); -up_write (&sb->s_umount);return error; } }@@ -1531,7 +1522,29 @@ int thaw_super(struct super_block *sb)sb_freeze_unlock (sb); out:wake_up (&sb->s_writers.wait_unfrozen); -deactivate_locked_super (sb);return 0 ; } + +/** +* thaw_super -- unlock filesystem +* @sb: the super to thaw +* +* Unlocks the filesystem and marks it writeable again after freeze_super(). +*/ +int thaw_super (struct super_block *sb) +{ +int error; + +down_write (&sb->s_umount); + error =thaw_locked_super (sb); +if (error) { +up_write (&sb->s_umount); + goto out; + } + +deactivate_locked_super (sb); + +out: +return error; +}EXPORT_SYMBOL (thaw_super);Signature · 1 line
2.15.0
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH 3/11 fs: add frozen sb state helpers
In reply to Luis R. RodriguezThe question of whether or not a superblock is frozen needs to be augmented in the future to account for differences between a user initiated freeze and a kernel initiated freeze done automatically on behalf of the kernel.
Provide helpers so that these can be used instead so that we don't have to expand checks later in these same call sites as we expand the definition of a frozen superblock.
--- fs/ext4/ext4_jbd2.c | 2 +- fs/super.c | 4 ++-- fs/xfs/xfs_trans.c | 2 +- include/linux/fs.h | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-)
fs/ext4/ext4_jbd2.c +1 -1
@@ -50,7 +50,7 @@ static int ext4_journal_check_start(struct super_block *sb)if (sb_rdonly (sb))return -EROFS ; -WARN_ON (sb->s_writers.frozen ==SB_FREEZE_COMPLETE ); +WARN_ON (sb_is_frozen (sb)); journal =EXT4_SB (sb)->s_journal;/* * Special case here: if the journal has aborted behind our
fs/super.c +2 -2
@@ -1392,7 +1392,7 @@ static int freeze_locked_super(struct super_block *sb) {int ret; -if (sb->s_writers.frozen !=SB_UNFROZEN ) +if (!sb_is_unfrozen (sb))return -EBUSY ;if (!(sb->s_flags &SB_BORN ))
@@ -1498,7 +1498,7 @@ static int thaw_locked_super(struct super_block *sb) {int error; -if (sb->s_writers.frozen !=SB_FREEZE_COMPLETE ) +if (!sb_is_frozen (sb))return -EINVAL ;if (sb_rdonly (sb)) {
fs/xfs/xfs_trans.c +1 -1
@@ -241,7 +241,7 @@ xfs_trans_alloc(if (!(flags &XFS_TRANS_NO_WRITECOUNT ))sb_start_intwrite (mp->m_super); -WARN_ON (mp->m_super->s_writers.frozen ==SB_FREEZE_COMPLETE ); +WARN_ON (sb_is_frozen (mp->m_super));atomic_inc (&mp->m_active_trans); tp = kmem_zone_zalloc(xfs_trans_zone,
include/linux/fs.h +33 -0
@@ -1589,6 +1589,39 @@ static inline void sb_start_intwrite(struct super_block *sb)__sb_start_write (sb,SB_FREEZE_FS , true); } +/** +* sb_is_frozen_by_user - is superblock frozen by a user call +* @sb: the super to check +* +* Returns true if the super freeze was initiated by userspace, for instance, +* an ioctl call. +*/ +static inline bool sb_is_frozen_by_user (struct super_block *sb) +{ +return sb->s_writers.frozen ==SB_FREEZE_COMPLETE ; +} + +/** +* sb_is_frozen - is superblock frozen +* @sb: the super to check +* +* Returns true if the super is frozen. +*/ +static inline bool sb_is_frozen (struct super_block *sb) +{ +return sb_is_frozen_by_user (sb); +} + +/** +* sb_is_unfrozen - is superblock unfrozen +* @sb: the super to check +* +* Returns true if the super is unfrozen. +*/ +static inline bool sb_is_unfrozen (struct super_block *sb) +{ +return sb->s_writers.frozen ==SB_UNFROZEN ; +}extern bool inode_owner_or_capable (const struct inode *inode);
Signature · 1 line
2.15.0
Jan Kara
reviewerRe: [PATCH 03/11] fs: add frozen sb state helpers
Quoted 10 lines · Luis R. Rodriguez
The question of whether or not a superblock is frozen needs to be augmented in the future to account for differences between a user initiated freeze and a kernel initiated freeze done automatically on behalf of the kernel.
Provide helpers so that these can be used instead so that we don't have to expand checks later in these same call sites as we expand the definition of a frozen superblock.
Signed-off-by Luis R. Rodriguez <[email protected]>
So helpers are fine but...
include/linux/fs.h static inline void sb_start_intwrite(struct super_block *sb) · line 1602
Quoted 11 lines
include/linux/fs.h +11
-0+/** +* sb_is_frozen_by_user - is superblock frozen by a user call +* @sb: the super to check +* +* Returns true if the super freeze was initiated by userspace, for instance, +* an ioctl call. +*/ +static inline bool sb_is_frozen_by_user (struct super_block *sb) +{ +return sb->s_writers.frozen ==SB_FREEZE_COMPLETE ; +}
... I dislike the _by_user() suffix as there may be different places that call freeze_super() (e.g. device mapper does this during some operations). Clearly we need to distinguish "by system suspend" and "the other" cases. So please make this clear in the naming.
In fact, what might be a cleaner solution is to introduce a 'freeze_count' for superblock freezing (we already do have this for block devices). Then you don't need to differentiate these two cases - but you'd still need to properly handle cleanup if freezing of all superblocks fails in the middle. So I'm not 100% this works out nicely in the end. But it's certainly worth a consideration.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 03/11] fs: add frozen sb state helpers
Quoted 4 lines · Jan Kara
... I dislike the
_by_user()suffix as there may be different places that callfreeze_super()(e.g. device mapper does this during some operations). Clearly we need to distinguish "by system suspend" and "the other" cases. So please make this clear in the naming.
Ah. How about sb_frozen_by_cb() ?
Quoted 6 lines
In fact, what might be a cleaner solution is to introduce a '
freeze_count' for superblock freezing (we already do have this for block devices). Then you don't need to differentiate these two cases - but you'd still need to properly handle cleanup if freezing of all superblocks fails in the middle. So I'm not 100% this works out nicely in the end. But it's certainly worth a consideration.
Ah, there are three important reasons for doing it the way I did it which are easy to miss, unless you read the commit log message very carefully.
0) The ioctl interface causes a failure to be sent back to userspace if you issue two consecutive freezes, or two thaws. Ie, once a filesystem is frozen, a secondary call will result in an error. Likewise for thaw.
1) The new iterate supers stuff I added bail on the first error and return that error. If we kept the ioctl() interface error scheme we'd be erroring out if on suspend if userspace had already frozen a filesystem. Clearly that'd be silly so we need to distinguish between the automatic kernel freezing and the old userspace ioctl initiated interface, so that we can keep the old behaviour but allow in-kernel auto freeze on suspend to work properly.
2) If we fail to suspend we need to then thaw up all filesystems. The order in which we try to freeze is in reverse order on the super_block list. If we fail though we iterate in proper order on the super_block list and thaw. If you had two filesystems this means that if a failure happened on freezing the first filesystem, we'd first thaw the other filesystem -- and because of
0) if we don't distinguish between the ioctl interface or auto freezing, we'd also fail on thaw'ing given the other superblock wouldn't have been frozen.
So we need to keep two separate approaches. The count stuff would not suffice to distinguish origin of source for freeze call.
Come to think of it, I think I forgot to avoid thaw if the freeze was ioctl initiated..
thaw_unlocked(bool cb_call)
{
if (sb_frozen_by_cb(sb) && !cb_call)
return 0; /* skip as the user wanted to keep this fs frozen */
...
}
Even though the kernel auto call is new I think we need to keep ioctl initiated frozen filesystems frozen to not break old userspace assumptions.
So, keeping all this in mind, does a count method still suffice?
Luis
Jan Kara
reviewerRe: [PATCH 03/11] fs: add frozen sb state helpers
Quoted 7 lines · Luis R. Rodriguez
Quoted 4 lines · Jan Kara
... I dislike the
_by_user()suffix as there may be different places that callfreeze_super()(e.g. device mapper does this during some operations). Clearly we need to distinguish "by system suspend" and "the other" cases. So please make this clear in the naming.Ah. How about
sb_frozen_by_cb()?
And what does 'cb' stand for? :)
Quoted 7 lines
Quoted 6 lines
In fact, what might be a cleaner solution is to introduce a '
freeze_count' for superblock freezing (we already do have this for block devices). Then you don't need to differentiate these two cases - but you'd still need to properly handle cleanup if freezing of all superblocks fails in the middle. So I'm not 100% this works out nicely in the end. But it's certainly worth a consideration.
Quoted 6 lines
Ah, there are three important reasons for doing it the way I did it which are easy to miss, unless you read the commit log message very carefully.
0) The ioctl interface causes a failure to be sent back to userspace if you issue two consecutive freezes, or two thaws. Ie, once a filesystem is frozen, a secondary call will result in an error. Likewise for thaw.
Yep. But also note that there's *another* interface to filesystem freezing which behaves differently - freeze_bdev() (used internally by dm). That interface uses the counter and freezing of already frozen device succeeds. IOW it is a mess. We cannot change the behavior of the ioctl but we could still provide an in-kernel interface to freeze_super() with the same semantics as freeze_bdev() which might be easier to use by suspend - maybe we could call it 'exclusive' (for the current freeze_super() semantics) and 'non-exclusive' (for the freeze_bdev() semantics) since this is very much like O_EXCL open of block devices...
Quoted 6 lines
1) The new iterate supers stuff I added bail on the first error and return that error. If we kept the
ioctl()interface error scheme we'd be erroring out if on suspend if userspace had already frozen a filesystem. Clearly that'd be silly so we need to distinguish between the automatic kernel freezing and the old userspace ioctl initiated interface, so that we can keep the old behaviour but allow in-kernel auto freeze on suspend to work properly.
This would work fine with the non-exclusive semantics I believe.
Quoted 21 lines
2) If we fail to suspend we need to then thaw up all filesystems. The order in which we try to freeze is in reverse order on the
super_blocklist. If we fail though we iterate in proper order on thesuper_blocklist and thaw. If you had two filesystems this means that if a failure happened on freezing the first filesystem, we'd first thaw the other filesystem -- and because of
0) if we don't distinguish between the ioctl interface or auto freezing, we'd also fail on thaw'ing given the other superblock wouldn't have been frozen.So we need to keep two separate approaches. The count stuff would not suffice to distinguish origin of source for freeze call.
Come to think of it, I think I forgot to avoid thaw if the freeze was ioctl initiated..
thaw_unlocked(bool cb_call)
{
if (sb_frozen_by_cb(sb)&& !cb_call)
return 0; /* skip as the user wanted to keep this fs frozen */
...
}
Quoted 4 lines
Even though the kernel auto call is new I think we need to keep ioctl initiated frozen filesystems frozen to not break old userspace assumptions.
So, keeping all this in mind, does a count method still suffice?
The count method would need a different error recovery method - i.e. if you fail freezing filesystems somewhere in the middle of iteration through superblock list, you'd need to iterate from that point on to the superblock where you've started. This is somewhat more complicated than your approach but it seems cleaner to me:
1) Function freezing all superblocks either succeeds and all superblocks are frozen or fails and no superblocks are (additionally) frozen.
2) It is not that normal users + one special user (who owns the "flag" in the superblock in form of a special freeze state) setup. We'd simply have exclusive and non-exclusive users of superblock freezing and there can be arbitrary numbers of them.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 03/11] fs: add frozen sb state helpers
Quoted 10 lines · Jan Kara
Quoted 7 lines · Luis R. Rodriguez
Quoted 4 lines · Jan Kara
... I dislike the
_by_user()suffix as there may be different places that callfreeze_super()(e.g. device mapper does this during some operations). Clearly we need to distinguish "by system suspend" and "the other" cases. So please make this clear in the naming.Ah. How about
sb_frozen_by_cb()?And what does 'cb' stand for? :)
Callback. But let me think about bdev usage a bit and we can worry about the bikeshedding later.
Quoted 10 lines
Quoted 10 lines
Quoted 6 lines
In fact, what might be a cleaner solution is to introduce a '
freeze_count' for superblock freezing (we already do have this for block devices). Then you don't need to differentiate these two cases - but you'd still need to properly handle cleanup if freezing of all superblocks fails in the middle. So I'm not 100% this works out nicely in the end. But it's certainly worth a consideration.Ah, there are three important reasons for doing it the way I did it which are easy to miss, unless you read the commit log message very carefully.
Quoted 7 lines
Quoted 3 lines
0) The ioctl interface causes a failure to be sent back to userspace if you issue two consecutive freezes, or two thaws. Ie, once a filesystem is frozen, a secondary call will result in an error. Likewise for thaw.
Yep. But also note that there's *another* interface to filesystem freezing which behaves differently -
freeze_bdev()(used internally by dm). That interface uses the counter and freezing of already frozen device succeeds.
Ah... so freeze_bdev() semantics matches the same semantics I was looking for.
Quoted 1 line
IOW it is a mess.
To say the least.
Quoted 6 lines
We cannot change the behavior of the ioctl but we could
still provide an in-kernel interface tofreeze_super()with the same semantics asfreeze_bdev()which might be easier to use by suspend - maybe we could call it 'exclusive' (for the currentfreeze_super()semantics) and 'non-exclusive' (for thefreeze_bdev()semantics) since this is very much likeO_EXCLopen of block devices...
Sure, now typically I see we make exclusive calls with the postfix _excl() so I take it you'd be OK in renaming freeze_super() freeze_super_excl() eventually then?
I totally missed freeze_bdev() otherwise I think I would have picked up on the shared semantics stuff and I would have just made a helper out of what freeze_bdev() uses, and then have both in-kernel and freeze_bdev() use it.
I'll note that its still not perfectly clear if really the semantics behind freeze_bdev() match what I described above fully. That still needs to be vetted for. For instance, does thaw_bdev() keep a superblock frozen if we an ioctl initiated freeze had occurred before? If so then great. Otherwise I think we'll need to distinguish the ioctl interface. Worst possible case is that bdev semantics and in-kernel semantics differ somehow, then that will really create a holy fucking mess.
Quoted 8 lines
Quoted 6 lines
1) The new iterate supers stuff I added bail on the first error and return that error. If we kept the
ioctl()interface error scheme we'd be erroring out if on suspend if userspace had already frozen a filesystem. Clearly that'd be silly so we need to distinguish between the automatic kernel freezing and the old userspace ioctl initiated interface, so that we can keep the old behaviour but allow in-kernel auto freeze on suspend to work properly.This would work fine with the non-exclusive semantics I believe.
Groovy.
Quoted 26 lines
Quoted 21 lines
2) If we fail to suspend we need to then thaw up all filesystems. The order in which we try to freeze is in reverse order on the
super_blocklist. If we fail though we iterate in proper order on thesuper_blocklist and thaw. If you had two filesystems this means that if a failure happened on freezing the first filesystem, we'd first thaw the other filesystem -- and because of
0) if we don't distinguish between the ioctl interface or auto freezing, we'd also fail on thaw'ing given the other superblock wouldn't have been frozen.So we need to keep two separate approaches. The count stuff would not suffice to distinguish origin of source for freeze call.
Come to think of it, I think I forgot to avoid thaw if the freeze was ioctl initiated..
thaw_unlocked(bool cb_call)
{
if (sb_frozen_by_cb(sb)&& !cb_call)
return 0; /* skip as the user wanted to keep this fs frozen */
...
}Quoted 4 lines
Even though the kernel auto call is new I think we need to keep ioctl initiated frozen filesystems frozen to not break old userspace assumptions.
So, keeping all this in mind, does a count method still suffice?
Quoted 8 lines
The count method would need a different error recovery method - i.e. if you fail freezing filesystems somewhere in the middle of iteration through superblock list, you'd need to iterate from that point on to the superblock where you've started. This is somewhat more complicated than your approach but it seems cleaner to me:
1) Function freezing all superblocks either succeeds and all superblocks are frozen or fails and no superblocks are (additionally) frozen.
To be clear, for now this would just be, all superblocks that support freeze_fs() are frozen :)
Quoted 4 lines
2) It is not that normal users + one special user (who owns the "flag" in the superblock in form of a special freeze state) setup. We'd simply have exclusive and non-exclusive users of superblock freezing and there can be arbitrary numbers of them.
Sorry I did not understand this point. Can you rephrase perhaps a bit?
Anyway, I just tried implementing this and it seemed rather easy to use a pivot, however note that then freeze_processes() which calls fs_suspend_freeze() would somehow need to pass the failed sb... do we want to have let fs_suspend_freeze() pass a parameter to be set to the failed sb of it failed? Locking-wise this seems racy.
So I mean, adding support to thaw using a pivot, the failed sb is rather easy:
fs/super.c +9 -1
@@ -614,13 +614,21 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg)* locked superblock and given argument. Returns 0 unless an error * occurred on calling the function on any superblock. */ -int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg) +int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg, +struct super_block *pivot) {struct super_block *sb, *p =NULL ;int error =0 ;spin_lock (&sb_lock);list_for_each_entry (sb, &super_blocks, s_list) { +/* If we have a pivot, start work on the next item */ +if (pivot) { +if (sb != pivot) +continue ; + pivot =NULL ; +continue ; + }if (hlist_unhashed (&sb->s_instances))continue ; sb->s_count++;
But we'd still need to to give enough context to let thaw use the failed sb as a pivot.
Luis
Jan Kara
reviewerRe: [PATCH 03/11] fs: add frozen sb state helpers
Hello,
I think I owe you a reply here... Sorry that it took so long.
Quoted 34 lines · Luis R. Rodriguez
Quoted 11 lines · Jan Kara
Quoted 10 lines · Luis R. Rodriguez
Quoted 6 lines
In fact, what might be a cleaner solution is to introduce a '
freeze_count' for superblock freezing (we already do have this for block devices). Then you don't need to differentiate these two cases - but you'd still need to properly handle cleanup if freezing of all superblocks fails in the middle. So I'm not 100% this works out nicely in the end. But it's certainly worth a consideration.Ah, there are three important reasons for doing it the way I did it which are easy to miss, unless you read the commit log message very carefully.
Quoted 7 lines
Quoted 3 lines
0) The ioctl interface causes a failure to be sent back to userspace if you issue two consecutive freezes, or two thaws. Ie, once a filesystem is frozen, a secondary call will result in an error. Likewise for thaw.
Yep. But also note that there's *another* interface to filesystem freezing which behaves differently -
freeze_bdev()(used internally by dm). That interface uses the counter and freezing of already frozen device succeeds.Ah... so
freeze_bdev()semantics matches the same semantics I was looking for.Quoted 1 line
IOW it is a mess.
To say the least.
Quoted 6 lines
We cannot change the behavior of the ioctl but we could
still provide an in-kernel interface tofreeze_super()with the same semantics asfreeze_bdev()which might be easier to use by suspend - maybe we could call it 'exclusive' (for the currentfreeze_super()semantics) and 'non-exclusive' (for thefreeze_bdev()semantics) since this is very much likeO_EXCLopen of block devices...
Quoted 3 lines
Sure, now typically I see we make exclusive calls with the postfix
_excl()so I take it you'd be OK in renamingfreeze_super()freeze_super_excl()eventually then?
In principle yes but let's leave the naming disputes to a later time when it is clear what API do we actually want to provide.
Quoted 11 lines
I totally missed
freeze_bdev()otherwise I think I would have picked up on the shared semantics stuff and I would have just made a helper out of whatfreeze_bdev()uses, and then have both in-kernel andfreeze_bdev()use it.I'll note that its still not perfectly clear if really the semantics behind
freeze_bdev()match what I described above fully. That still needs to be vetted for. For instance, doesthaw_bdev()keep a superblock frozen if we an ioctl initiated freeze had occurred before? If so then great. Otherwise I think we'll need to distinguish the ioctl interface. Worst possible case is that bdev semantics and in-kernel semantics differ somehow, then that will really create a holy fucking mess.
I believe nobody really thought about mixing those two interfaces to fs freezing and so the behavior is basically defined by the implementation. That is:
freeze_bdev() on sb frozen by ioctl_fsfreeze() -> EBUSY freeze_bdev() on sb frozen by freeze_bdev() -> success ioctl_fsfreeze() on sb frozen by freeze_bdev() -> EBUSY ioctl_fsfreeze() on sb frozen by ioctl_fsfreeze() -> EBUSY
thaw_bdev() on sb frozen by ioctl_fsfreeze() -> EINVAL ioctl_fsthaw() on sb frozen by freeze_bdev() -> success
What I propose is the following API:
freeze_super_excl()
- freezes superblock, returns EBUSY if the superblock is already frozen (either by another freeze_super_excl() or by freeze_super())freeze_super()
- this function will make sure superblock is frozen when the function returns with success. It can be nested with other freeze_super() or freeze_super_excl() calls (this second part is different from how freeze_bdev() behaves currently but AFAICT this behavior is actually what all current users of freeze_bdev() really want - just make sure fs cannot be written to)thaw_super()
- counterpart to freeze_super(), would fail with EINVAL if we were to drop the last "freeze reference" but sb was actually frozen with freeze_super_excl()thaw_super_excl()
- counterpart to freeze_super_excl(). Fails with EINVAL if sb was not frozen with freeze_super_excl() (this is different to current behavior but I don't believe anyone relies on this and doing otherwise is asking for data corruption).
I'd implement it by a freeze counter in the superblock (similar to what we currently have in bdev) where every call to freeze_super() or freeze_super_excl() would add one. Additionally we'd have a flag in the superblock whether the first freeze (it could not be any other since those would fail with EBUSY) came from freeze_super_excl().
Then we could make ioctl interface use the _excl() variant of the freezing API, freeze_bdev() would use the non-exclusive variant (we could drop the freeze counter in bdev completely), your freezing on suspend could then use the non-exclusive variant as well.
Also when doing this, you'd need to move code like:
if (sb->s_op->freeze_super)
error = sb->s_op->freeze_super(sb);
else
error = freeze_super(sb);into the freeze_super() / freeze_super_excl() handler behind the freeze counting code which might be a bit tricky WRT locking. GFS2 is the only fs having ->freeze_super() and that callback was implemented specifically so that it can do its own (cluster wide) locking before generic code grabbing s_umount semaphore. Then internally GFS2 ends up calling freeze_super() from freeze_go_sync() when cluster lock is acquired.
Quoted 5 lines
Quoted 4 lines
2) It is not that normal users + one special user (who owns the "flag" in the superblock in form of a special freeze state) setup. We'd simply have exclusive and non-exclusive users of superblock freezing and there can be arbitrary numbers of them.
Quoted 7 lines
Sorry I did not understand this point. Can you rephrase perhaps a bit?
Anyway, I just tried implementing this and it seemed rather easy to use a pivot, however note that then
freeze_processes()which callsfs_suspend_freeze()would somehow need to pass the failed sb... do we want to have letfs_suspend_freeze()pass a parameter to be set to the failed sb of it failed? Locking-wise this seems racy.
So with your iterate_supers_excl() doing this is somewhat difficult but you could have something like:
int freeze_all_supers(void)
{
struct super_block *sb, *p = NULL;
int error = 0;
spin_lock(&sb_lock);
list_for_each_entry_reverse(sb, &super_blocks, s_list) {
if (hlist_unhashed(&sb->s_instances))
continue;
sb->s_count++;
spin_unlock(&sb_lock);
down_write(&sb->s_umount);
if (sb->s_root && (sb->s_flags & SB_BORN)) {
error = freeze_super(sb, arg);
if (error) {
up_write(&sb->s_umount);
spin_lock(&sb_lock);
if (p)
__put_super(p);
p = sb;
list_for_each_entry_continue(sb, &super_blocks,
s_list) {
if (hlist_unhashed(&sb->s_instances))
continue;
sb->s_count++;
spin_unlock(&sb_lock);
down_write(&sb->s_umount);
if (sb->s_root && (sb->s_flags & SB_BORN))
thaw_super(sb, arg);
up_write(&sb->s_umount);
spin_lock(&sb_lock);
if (p)
__put_super(p);
p = sb;
}
break;
}
}
up_write(&sb->s_umount);
spin_lock(&sb_lock);
if (p)
__put_super(p);
p = sb;
}
if (p)
__put_super(p);
spin_unlock(&sb_lock);
return error;
}And you could possibly factor that out into two helper functions for iterating the superblocks, just they'd need more parameters and you'd need to pass reference (sb->count) when passing in the 'pivot' as you call it.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 03/11] fs: add frozen sb state helpers
Quoted 3 lines · Jan Kara
Hello,
I think I owe you a reply here... Sorry that it took so long.
Took me just as long :)
Quoted 10 lines · Luis R. Rodriguez
Quoted 8 lines
I'll note that its still not perfectly clear if really the semantics behind
freeze_bdev()match what I described above fully. That still needs to be vetted for. For instance, doesthaw_bdev()keep a superblock frozen if we an ioctl initiated freeze had occurred before? If so then great. Otherwise I think we'll need to distinguish the ioctl interface. Worst possible case is that bdev semantics and in-kernel semantics differ somehow, then that will really create a holy fucking mess.
Quoted 5 lines
I believe nobody really thought about mixing those two interfaces to fs freezing and so the behavior is basically defined by the implementation. That is:
freeze_bdev()on sb frozen byioctl_fsfreeze()-> EBUSY
Note below as well on your *future* freeze_super() implementation.
Quoted 5 lines
freeze_bdev()on sb frozen byfreeze_bdev()-> successioctl_fsfreeze()on sb frozen byfreeze_bdev()-> EBUSYioctl_fsfreeze()on sb frozen byioctl_fsfreeze()-> EBUSY
thaw_bdev()on sb frozen byioctl_fsfreeze()-> EINVAL
Phew, so this is what we want for the in-kernel freezing so we're good and *can* combine these then.
Quoted 10 lines
ioctl_fsthaw()on sb frozen byfreeze_bdev()-> successWhat I propose is the following API:
freeze_super_excl()
- freezes superblock, returns EBUSY if the superblock is already frozen (either by anotherfreeze_super_excl()or byfreeze_super())freeze_super()
- this function will make sure superblock is frozen when the function returns with success.
That's straight forward.
Quoted 2 lines
It can be nested with other
freeze_super()or
freeze_super_excl()calls
This is where it can get hairy. More below.
Quoted 4 lines
(this second part is different from how
freeze_bdev()behaves currently but AFAICT this behavior is actually what all current users offreeze_bdev()really want - just make sure fs cannot be written to)
If we can agree to this, then sure. However there are two types of possible nested calls to consider, one where the sb was already frozen by an IOCTL, and the other where it was initiated by either another freeze_super_excl() or another freeze_super() call which is currently being processed. For the first type, its easy to say the device is already frozen as such return success. If the freezing is ongoing, we may want to wait or not wait, and this will depend on our current use cases for freeze_bdev().
As you noted above, freeze_bdev() currently returns EBUSY if we had the sb already frozen by ioctl_fsfreeze(). It may be a welcomed enhancement to correct the semantics first to address the first case, but keep the EBUSY for the other case. A secondary patch could then add a completion mechanism and let callers decide to either wait or not. *Iff* the caller did not opt-in to wait we keep the EBUSY return.
Seem reasonable?
I'll address the rest of the mail later.
Luis
Jan Kara
reviewerRe: [PATCH 03/11] fs: add frozen sb state helpers
Quoted 51 lines · Luis R. Rodriguez
Quoted 10 lines · Jan Kara
Quoted 8 lines · Luis R. Rodriguez
I'll note that its still not perfectly clear if really the semantics behind
freeze_bdev()match what I described above fully. That still needs to be vetted for. For instance, doesthaw_bdev()keep a superblock frozen if we an ioctl initiated freeze had occurred before? If so then great. Otherwise I think we'll need to distinguish the ioctl interface. Worst possible case is that bdev semantics and in-kernel semantics differ somehow, then that will really create a holy fucking mess.Quoted 5 lines
I believe nobody really thought about mixing those two interfaces to fs freezing and so the behavior is basically defined by the implementation. That is:
freeze_bdev()on sb frozen byioctl_fsfreeze()-> EBUSYNote below as well on your *future*
freeze_super()implementation.Quoted 5 lines
freeze_bdev()on sb frozen byfreeze_bdev()-> successioctl_fsfreeze()on sb frozen byfreeze_bdev()-> EBUSYioctl_fsfreeze()on sb frozen byioctl_fsfreeze()-> EBUSY
thaw_bdev()on sb frozen byioctl_fsfreeze()-> EINVALPhew, so this is what we want for the in-kernel freezing so we're good and *can* combine these then.
Quoted 10 lines
ioctl_fsthaw()on sb frozen byfreeze_bdev()-> successWhat I propose is the following API:
freeze_super_excl()
- freezes superblock, returns EBUSY if the superblock is already frozen (either by anotherfreeze_super_excl()or byfreeze_super())freeze_super()
- this function will make sure superblock is frozen when the function returns with success.That's straight forward.
Quoted 2 lines
It can be nested with other
freeze_super()or
freeze_super_excl()callsThis is where it can get hairy. More below.
Quoted 4 lines
(this second part is different from how
freeze_bdev()behaves currently but AFAICT this behavior is actually what all current users offreeze_bdev()really want - just make sure fs cannot be written to)
Quoted 8 lines
If we can agree to this, then sure. However there are two types of possible nested calls to consider, one where the sb was already frozen by an IOCTL, and the other where it was initiated by either another
freeze_super_excl()or anotherfreeze_super()call which is currently being processed. For the first type, its easy to say the device is already frozen as such return success. If the freezing is ongoing, we may want to wait or not wait, and this will depend on our current use cases forfreeze_bdev().
A side note since I'm not sure I wrote this down in my previous email:
I want ioctl_fsfreeze() directly use freeze_super_excl().
Now to your freeze in progress question: freeze_super_excl() can immediately return EBUSY when there's freezing in progress. OTOH freeze_super() always has to wait for the current freeze / thaw to finish and then do what's necessary. I don't see a use case where you'd like to have freeze_super() not wait.
Quoted 6 lines
As you noted above,
freeze_bdev()currently returns EBUSY if we had the sb already frozen byioctl_fsfreeze(). It may be a welcomed enhancement to correct the semantics first to address the first case, but keep the EBUSY for the other case. A secondary patch could then add a completion mechanism and let callers decide to either wait or not. *Iff* the caller did not opt-in to wait we keep the EBUSY return.
You're now speaking about steps to transition to the new API, right? I'd structure the transition as follows:
1) Move bdev->bd_fsfreeze_count to a superblock.
2) Make freeze_super() grab the counter as well, thaw_super() drops it and unfreezes the filesystem only if the counter dropped to zero.
3) Rename freeze_super() to freeze_super_excl().
4) Only now I'd go for messing with freeze_bdev() as it now combines sanely with freeze_super_excl(). Probably I'd just implement new freeze_super() with the desired semantics (including waiting for ongoing operation to finish).
5) And then switch all users (there are 4 in the kernel) from freeze_bdev() to freeze_super() with the justification in each case why the new semantics is actually desirable.
6) Drop old freeze_bdev() - note that only one freeze_bdev() user (in drivers/md/dm.c) is actually interested in passing bdev, all the others are better off just passing in superblock to new freeze_super(). Anyway for that user in dm we might still provide a convenience wrapper to grab the superblock and call new freeze_super() on it.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 03/11] fs: add frozen sb state helpers
In reply to Luis R. RodriguezQuoted 26 lines · Luis R. Rodriguez
Quoted 3 lines · Jan Kara
Hello,
I think I owe you a reply here... Sorry that it took so long.
Took me just as long :)
Quoted 19 lines · Luis R. Rodriguez
Quoted 8 lines
I'll note that its still not perfectly clear if really the semantics behind
freeze_bdev()match what I described above fully. That still needs to be vetted for. For instance, doesthaw_bdev()keep a superblock frozen if we an ioctl initiated freeze had occurred before? If so then great. Otherwise I think we'll need to distinguish the ioctl interface. Worst possible case is that bdev semantics and in-kernel semantics differ somehow, then that will really create a holy fucking mess.I believe nobody really thought about mixing those two interfaces to fs freezing and so the behavior is basically defined by the implementation. That is:
freeze_bdev()on sb frozen byioctl_fsfreeze()-> EBUSYfreeze_bdev()on sb frozen byfreeze_bdev()-> successioctl_fsfreeze()on sb frozen byfreeze_bdev()-> EBUSYioctl_fsfreeze()on sb frozen byioctl_fsfreeze()-> EBUSY
Quoted 4 lines
Quoted 1 line
thaw_bdev()on sb frozen byioctl_fsfreeze()-> EINVALPhew, so this is what we want for the in-kernel freezing so we're good and *can* combine these then.
I double checked, and I don't see where you get EINVAL for this case. We *do* keep the sb frozen though, which is good, and the worst fear I had was that we did not. However we return 0 if there was already a prior freeze_bdev() or ioctl_fsfreeze() other than the context that started the prior freeze (--bdev->bd_fsfreeze_count > 0).
The -EINVAL is only returned currently if there were no freezers.
int thaw_bdev(struct block_device *bdev, struct super_block *sb)
{
int error = -EINVAL;
mutex_lock(&bdev->bd_fsfreeze_mutex);
if (!bdev->bd_fsfreeze_count)
goto out;
error = 0;
if (--bdev->bd_fsfreeze_count > 0)
goto out;
...
out:
mutex_unlock(&bdev->bd_fsfreeze_mutex);
return error;
}Luis
Jan Kara
reviewerRe: [PATCH 03/11] fs: add frozen sb state helpers
Quoted 40 lines · Luis R. Rodriguez
Quoted 26 lines · Luis R. Rodriguez
Quoted 3 lines · Jan Kara
Hello,
I think I owe you a reply here... Sorry that it took so long.
Took me just as long :)
Quoted 19 lines · Luis R. Rodriguez
Quoted 8 lines
I'll note that its still not perfectly clear if really the semantics behind
freeze_bdev()match what I described above fully. That still needs to be vetted for. For instance, doesthaw_bdev()keep a superblock frozen if we an ioctl initiated freeze had occurred before? If so then great. Otherwise I think we'll need to distinguish the ioctl interface. Worst possible case is that bdev semantics and in-kernel semantics differ somehow, then that will really create a holy fucking mess.I believe nobody really thought about mixing those two interfaces to fs freezing and so the behavior is basically defined by the implementation. That is:
freeze_bdev()on sb frozen byioctl_fsfreeze()-> EBUSYfreeze_bdev()on sb frozen byfreeze_bdev()-> successioctl_fsfreeze()on sb frozen byfreeze_bdev()-> EBUSYioctl_fsfreeze()on sb frozen byioctl_fsfreeze()-> EBUSYQuoted 4 lines
Quoted 1 line
thaw_bdev()on sb frozen byioctl_fsfreeze()-> EINVALPhew, so this is what we want for the in-kernel freezing so we're good and *can* combine these then.
I double checked, and I don't see where you get EINVAL for this case. We *do* keep the sb frozen though, which is good, and the worst fear I had was that we did not. However we return 0 if there was already a prior
freeze_bdev()orioctl_fsfreeze()other than the context that started the prior freeze (--bdev->bd_fsfreeze_count > 0).The
-EINVALis only returned currently if there were no freezers.
Quoted 7 lines
int
thaw_bdev(struct block_device *bdev, struct super_block *sb){ int error = -EINVAL; mutex_lock(&bdev->bd_fsfreeze_mutex); if (!bdev->bd_fsfreeze_count) goto out;
But this is precisely where we'd bail if we freeze sb by ioctl_fsfreeze() but try to thaw by thaw_bdev(). ioctl_fsfreeze() does not touch bd_fsfreeze_count...
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 03/11] fs: add frozen sb state helpers
Quoted 45 lines · Jan Kara
Quoted 44 lines · Luis R. Rodriguez
Quoted 26 lines · Luis R. Rodriguez
Quoted 3 lines · Jan Kara
Hello,
I think I owe you a reply here... Sorry that it took so long.
Took me just as long :)
Quoted 19 lines · Luis R. Rodriguez
Quoted 8 lines
I'll note that its still not perfectly clear if really the semantics behind
freeze_bdev()match what I described above fully. That still needs to be vetted for. For instance, doesthaw_bdev()keep a superblock frozen if we an ioctl initiated freeze had occurred before? If so then great. Otherwise I think we'll need to distinguish the ioctl interface. Worst possible case is that bdev semantics and in-kernel semantics differ somehow, then that will really create a holy fucking mess.I believe nobody really thought about mixing those two interfaces to fs freezing and so the behavior is basically defined by the implementation. That is:
freeze_bdev()on sb frozen byioctl_fsfreeze()-> EBUSYfreeze_bdev()on sb frozen byfreeze_bdev()-> successioctl_fsfreeze()on sb frozen byfreeze_bdev()-> EBUSYioctl_fsfreeze()on sb frozen byioctl_fsfreeze()-> EBUSYQuoted 4 lines
Quoted 1 line
thaw_bdev()on sb frozen byioctl_fsfreeze()-> EINVALPhew, so this is what we want for the in-kernel freezing so we're good and *can* combine these then.
I double checked, and I don't see where you get EINVAL for this case. We *do* keep the sb frozen though, which is good, and the worst fear I had was that we did not. However we return 0 if there was already a prior
freeze_bdev()orioctl_fsfreeze()other than the context that started the prior freeze (--bdev->bd_fsfreeze_count > 0).The
-EINVALis only returned currently if there were no freezers.int
thaw_bdev(struct block_device *bdev, struct super_block *sb){ int error = -EINVAL;
Quoted 7 lines
Quoted 3 lines
mutex_lock(&bdev->bd_fsfreeze_mutex); if (!bdev->bd_fsfreeze_count) goto out;But this is precisely where we'd bail if we freeze sb by
ioctl_fsfreeze()but try to thaw bythaw_bdev().ioctl_fsfreeze()does not touchbd_fsfreeze_count...
Ah, yes, I see that now, thanks!
Luis
Re: [PATCH 03/11] fs: add frozen sb state helpers
In reply to Jan KaraQuoted 31 lines · Jan Kara
Quoted 10 lines · Luis R. Rodriguez
The question of whether or not a superblock is frozen needs to be augmented in the future to account for differences between a user initiated freeze and a kernel initiated freeze done automatically on behalf of the kernel.
Provide helpers so that these can be used instead so that we don't have to expand checks later in these same call sites as we expand the definition of a frozen superblock.
Signed-off-by Luis R. Rodriguez <[email protected]>So helpers are fine but...
include/linux/fs.h static inline void sb_start_intwrite(struct super_block *sb) · line 1602
Quoted 11 lines
include/linux/fs.h +11
-0+/** +* sb_is_frozen_by_user - is superblock frozen by a user call +* @sb: the super to check +* +* Returns true if the super freeze was initiated by userspace, for instance, +* an ioctl call. +*/ +static inline bool sb_is_frozen_by_user (struct super_block *sb) +{ +return sb->s_writers.frozen ==SB_FREEZE_COMPLETE ; +}... I dislike the
_by_user()suffix as there may be different places that callfreeze_super()(e.g. device mapper does this during some operations). Clearly we need to distinguish "by system suspend" and "the other" cases. So please make this clear in the naming.
Quoted 6 lines
In fact, what might be a cleaner solution is to introduce a '
freeze_count' for superblock freezing (we already do have this for block devices). Then you don't need to differentiate these two cases - but you'd still need to properly handle cleanup if freezing of all superblocks fails in the middle. So I'm not 100% this works out nicely in the end. But it's certainly worth a consideration.
Seems reasonable.
Luis
PATCH 4/11 fs: distinguish between user initiated freeze and kernel initiated freeze
In reply to Luis R. RodriguezUserspace can initiate a freeze call using ioctls. If the kernel decides to freeze a filesystem later it must be able to distinguish if userspace had initiated the freeze, so that it does not unfreeze it later automatically on resume.
Likewise if the kernel is initiating a freeze on its own it should *not* fail to freeze a filesystem if a user had already frozen it on our behalf. This same concept applies to thawing, even if its not possible for userspace to beat the kernel in thawing a filesystem. This logic however has never applied to userspace freezing and thawing, two consecutive userspace freeze calls will results in only the first one succeeding, so we must retain the same behaviour in userspace.
This doesn't implement yet kernel initiated filesystem freeze calls, this will be done in subsequent calls. This change should introduce no functional changes, it just extends the definitions a frozen filesystem to account for future kernel initiated filesystem freeze.
--- fs/super.c | 19 ++++++++++++++----- include/linux/fs.h | 17 +++++++++++++++-- 2 files changed, 29 insertions(+), 7 deletions(-)
fs/super.c +14 -5
@@ -1388,10 +1388,13 @@ static void sb_freeze_unlock(struct super_block *sb) }/* Caller takes lock and handles active count */ -static int freeze_locked_super (struct super_block *sb) +static int freeze_locked_super (struct super_block *sb,bool usercall) {int ret; +if (!usercall &&sb_is_frozen (sb)) +return 0 ; +if (!sb_is_unfrozen (sb))return -EBUSY ;
@@ -1436,7 +1439,10 @@ static int freeze_locked_super(struct super_block *sb)* For debugging purposes so that fs can warn if it sees write activity * when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super(). */ - sb->s_writers.frozen =SB_FREEZE_COMPLETE ; +if (usercall) + sb->s_writers.frozen =SB_FREEZE_COMPLETE ; +else + sb->s_writers.frozen =SB_FREEZE_COMPLETE_AUTO ;lockdep_sb_freeze_release (sb);return 0 ; }
@@ -1481,7 +1487,7 @@ int freeze_super(struct super_block *sb)atomic_inc (&sb->s_active);down_write (&sb->s_umount); - error =freeze_locked_super (sb); + error =freeze_locked_super (sb, true);if (error) {deactivate_locked_super (sb); goto out;
@@ -1494,10 +1500,13 @@ int freeze_super(struct super_block *sb)EXPORT_SYMBOL (freeze_super);/* Caller takes lock and handles active count */ -static int thaw_locked_super (struct super_block *sb) +static int thaw_locked_super (struct super_block *sb,bool usercall) {int error; +if (!usercall &&sb_is_unfrozen (sb)) +return 0 ; +if (!sb_is_frozen (sb))return -EINVAL ;
@@ -1536,7 +1545,7 @@ int thaw_super(struct super_block *sb)int error;down_write (&sb->s_umount); - error =thaw_locked_super (sb); + error =thaw_locked_super (sb, true);if (error) {up_write (&sb->s_umount); goto out;
include/linux/fs.h +15 -2
@@ -1324,9 +1324,10 @@ enum {SB_FREEZE_FS =3 ,/* For internal FS use (e.g. to stop * internal threads if needed) */ SB_FREEZE_COMPLETE =4 ,/* ->freeze_fs finished successfully */ + SB_FREEZE_COMPLETE_AUTO = 5, /* same but initiated automatically */ }; -#define SB_FREEZE_LEVELS (SB_FREEZE_COMPLETE - 1) +#define SB_FREEZE_LEVELS (SB_FREEZE_COMPLETE_AUTO - 1) struct sb_writers { int frozen; /* Is sb frozen? */
@@ -1601,6 +1602,18 @@ static inline bool sb_is_frozen_by_user(struct super_block *sb)return sb->s_writers.frozen ==SB_FREEZE_COMPLETE ; } +/** +* sb_is_frozen_by_kernel - is superblock frozen by the kernel automatically +* @sb: the super to check +* +* Returns true if the super freeze was initiated by the kernel, automatically, +* for instance during system sleep or hibernation. +*/ +static inline bool sb_is_frozen_by_kernel (struct super_block *sb) +{ +return sb->s_writers.frozen ==SB_FREEZE_COMPLETE_AUTO ; +} +/** * sb_is_frozen - is superblock frozen * @sb: the super to check
@@ -1609,7 +1622,7 @@ static inline bool sb_is_frozen_by_user(struct super_block *sb)*/ static inline bool sb_is_frozen (struct super_block *sb) { -return sb_is_frozen_by_user (sb); +return sb_is_frozen_by_user (sb) ||sb_is_frozen_by_kernel (sb); }/**
Signature · 1 line
2.15.0
PATCH 5/11 fs: add iterate_supers_excl() and iterate_supers_reverse_excl()
In reply to Luis R. RodriguezThere are use cases where we wish to traverse the superblock list but also capture errors, and in which case we want to avoid having our callers issue a lock themselves since we can do the locking for the callers. Provide a iterate_supers_excl() which calls a function with the write lock held. If an error occurs we capture it and propagate it.
Likewise there are use cases where we wish to traverse the superblock list but in reverse order. The new iterate_supers_reverse_excl() helpers does this but also also captures any errors encountered.
--- fs/super.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 2 ++ 2 files changed, 93 insertions(+)
fs/super.c +91 -0
@@ -605,6 +605,97 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg)spin_unlock (&sb_lock); } +/** +* iterate_supers_excl - exclusively call func for all active superblocks +* @f: function to call +* @arg: argument to pass to it +* +* Scans the superblock list and calls given function, passing it +* locked superblock and given argument. Returns 0 unless an error +* occurred on calling the function on any superblock. +*/ +int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg) +{ +struct super_block *sb, *p =NULL ; +int error =0 ; + +spin_lock (&sb_lock); +list_for_each_entry (sb, &super_blocks, s_list) { +if (hlist_unhashed (&sb->s_instances)) +continue ; + sb->s_count++; +spin_unlock (&sb_lock); + +down_write (&sb->s_umount); +if (sb->s_root && (sb->s_flags &SB_BORN )) { + error =f (sb, arg); +if (error) { +up_write (&sb->s_umount); +spin_lock (&sb_lock); +__put_super (sb); +break ; + } + } +up_write (&sb->s_umount); + +spin_lock (&sb_lock); +if (p) +__put_super (p); + p = sb; + } +if (p) +__put_super (p); +spin_unlock (&sb_lock); + +return error; +} + +/** +* iterate_supers_reverse_excl - exclusively calls func in reverse order +* @f: function to call +* @arg: argument to pass to it +* +* Scans the superblock list and calls given function, passing it +* locked superblock and given argument, in reverse order, and holding +* the s_umount write lock. Returns if an error occurred. +*/ +int iterate_supers_reverse_excl (int (*f)(struct super_block *,void *), +void *arg) +{ +struct super_block *sb, *p =NULL ; +int error =0 ; + +spin_lock (&sb_lock); +list_for_each_entry_reverse (sb, &super_blocks, s_list) { +if (hlist_unhashed (&sb->s_instances)) +continue ; + sb->s_count++; +spin_unlock (&sb_lock); + +down_write (&sb->s_umount); +if (sb->s_root && (sb->s_flags &SB_BORN )) { + error =f (sb, arg); +if (error) { +up_write (&sb->s_umount); +spin_lock (&sb_lock); +__put_super (sb); +break ; + } + } +up_write (&sb->s_umount); + +spin_lock (&sb_lock); +if (p) +__put_super (p); + p = sb; + } +if (p) +__put_super (p); +spin_unlock (&sb_lock); + +return error; +} +/** * iterate_supers_type - call function for superblocks of given type * @type: fs type
include/linux/fs.h +2 -0
@@ -3164,6 +3164,8 @@ extern struct super_block *get_active_super(struct block_device *bdev);extern void drop_super (struct super_block *sb);extern void drop_super_exclusive (struct super_block *sb);extern void iterate_supers (void (*)(struct super_block *,void *),void *); +extern int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg); +extern int iterate_supers_reverse_excl (int (*)(struct super_block *,void *),void *);extern void iterate_supers_type (struct file_system_type *,void (*)(struct super_block *,void *),void *);
Signature · 1 line
2.15.0
Rafael J. Wysocki
maintainerRe: [PATCH 05/11] fs: add iterate_supers_excl() and iterate_supers_reverse_excl()
Quoted 35 lines · Luis R. Rodriguez
There are use cases where we wish to traverse the superblock list but also capture errors, and in which case we want to avoid having our callers issue a lock themselves since we can do the locking for the callers. Provide a
iterate_supers_excl()which calls a function with the write lock held. If an error occurs we capture it and propagate it.Likewise there are use cases where we wish to traverse the superblock list but in reverse order. The new
iterate_supers_reverse_excl()helpers does this but also also captures any errors encountered.Signed-off-by Luis R. Rodriguez <[email protected]>--- fs/super.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 2 ++ 2 files changed, 93 insertions(+)fs/super.c +10
-0@@ -605,6 +605,97 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg)spin_unlock (&sb_lock); } +/** +* iterate_supers_excl - exclusively call func for all active superblocks +* @f: function to call +* @arg: argument to pass to it +* +* Scans the superblock list and calls given function, passing it +* locked superblock and given argument. Returns 0 unless an error +* occurred on calling the function on any superblock. +*/ +int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg)
fs/super.c void iterate_supers(void (*f)(struct super_block *, void *), void *arg) · line 627
Quoted 10 lines
fs/super.c +10
-0+{ +struct super_block *sb, *p =NULL ; +int error =0 ; + +spin_lock (&sb_lock); +list_for_each_entry (sb, &super_blocks, s_list) { +if (hlist_unhashed (&sb->s_instances)) +continue ; + sb->s_count++; +spin_unlock (&sb_lock);
Can anything bad happen if the list is modified at this point by a concurrent thread?
Quoted 25 lines
fs/super.c +25
-0+ +down_write (&sb->s_umount); +if (sb->s_root && (sb->s_flags &SB_BORN )) { + error =f (sb, arg); +if (error) { +up_write (&sb->s_umount); +spin_lock (&sb_lock); +__put_super (sb); +break ; + } + } +up_write (&sb->s_umount); + +spin_lock (&sb_lock); +if (p) +__put_super (p); + p = sb; + } +if (p) +__put_super (p); +spin_unlock (&sb_lock); + +return error; +} +
Re: [PATCH 05/11] fs: add iterate_supers_excl() and iterate_supers_reverse_excl()
Quoted 15 lines · Rafael J. Wysocki
fs/super.c void iterate_supers(void (*f)(struct super_block *, void *), void *arg) · line 627
Quoted 11 lines · Luis R. Rodriguez
fs/super.c +11
-0+int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg) +{ +struct super_block *sb, *p =NULL ; +int error =0 ; + +spin_lock (&sb_lock); +list_for_each_entry (sb, &super_blocks, s_list) { +if (hlist_unhashed (&sb->s_instances)) +continue ; + sb->s_count++; +spin_unlock (&sb_lock);Can anything bad happen if the list is modified at this point by a concurrent thread?
The race is theoretical and applies to all users of iterate_supers() as well.
Its certainly worth considering, however given other code is implicated its not a *new* issue or race. Its the best we can do with the current design.
That said, as I looked at all this code I considered that perhaps super_blocks deserves its own RCU lock to enable us to do full swap operations on the list, without having to do these nasty releases in between.
If that's possible / desirable I'd consider it a welcomed future optimization, and I could give it a shot, however its unclear if this is a requirement for this feature at this time.
Luis
Re: [PATCH 05/11] fs: add iterate_supers_excl() and iterate_supers_reverse_excl()
In reply to Rafael J. WysockiQuoted 41 lines · Rafael J. Wysocki
Quoted 40 lines · Luis R. Rodriguez
There are use cases where we wish to traverse the superblock list but also capture errors, and in which case we want to avoid having our callers issue a lock themselves since we can do the locking for the callers. Provide a
iterate_supers_excl()which calls a function with the write lock held. If an error occurs we capture it and propagate it.Likewise there are use cases where we wish to traverse the superblock list but in reverse order. The new
iterate_supers_reverse_excl()helpers does this but also also captures any errors encountered.Signed-off-by Luis R. Rodriguez <[email protected]>--- fs/super.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 2 ++ 2 files changed, 93 insertions(+)fs/super.c +15
-0@@ -605,6 +605,97 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg)spin_unlock (&sb_lock); } +/** +* iterate_supers_excl - exclusively call func for all active superblocks +* @f: function to call +* @arg: argument to pass to it +* +* Scans the superblock list and calls given function, passing it +* locked superblock and given argument. Returns 0 unless an error +* occurred on calling the function on any superblock. +*/ +int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg) +{ +struct super_block *sb, *p =NULL ; +int error =0 ; + +spin_lock (&sb_lock);
Quoted 8 lines
fs/super.c void iterate_supers(void (*f)(struct super_block *, void *), void *arg) · line 627
Quoted 5 lines
fs/super.c +5
-0+list_for_each_entry (sb, &super_blocks, s_list) { +if (hlist_unhashed (&sb->s_instances)) +continue ; + sb->s_count++; +spin_unlock (&sb_lock);Can anything bad happen if the list is modified at this point by a concurrent thread?
No. We have a valid reference to sb->s_count and that keeps it on the list while we have the lock dropped. The sb reference isn't dropped until we've iterated to the next sb on the list and taken a reference to that, hence it's safe to drop and regain the list lock without needing to restart the iteration.
Quoted 9 lines
Quoted 9 lines
fs/super.c +9
-0+ +down_write (&sb->s_umount); +if (sb->s_root && (sb->s_flags &SB_BORN )) { + error =f (sb, arg); +if (error) { +up_write (&sb->s_umount); +spin_lock (&sb_lock); +__put_super (sb); +break ;
Quoted 8 lines
fs/super.c void iterate_supers(void (*f)(struct super_block *, void *), void *arg) · line 644
Quoted 8 lines
fs/super.c +8
-0+ } + } +up_write (&sb->s_umount); + +spin_lock (&sb_lock); +if (p) +__put_super (p); + p = sb;
This code here is what drops the reference to the previous sb we've iterated past.
FWIW, this "hold until next is held" iteration pattern is used frequently for inodes, dentries, and other reference counted VFS objects so we can iterate the list without needing to hold the list lock for the entire iteration....
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Rafael J. Wysocki
maintainerRe: [PATCH 05/11] fs: add iterate_supers_excl() and iterate_supers_reverse_excl()
Quoted 78 lines · Dave Chinner
Quoted 41 lines · Rafael J. Wysocki
Quoted 40 lines · Luis R. Rodriguez
There are use cases where we wish to traverse the superblock list but also capture errors, and in which case we want to avoid having our callers issue a lock themselves since we can do the locking for the callers. Provide a
iterate_supers_excl()which calls a function with the write lock held. If an error occurs we capture it and propagate it.Likewise there are use cases where we wish to traverse the superblock list but in reverse order. The new
iterate_supers_reverse_excl()helpers does this but also also captures any errors encountered.Signed-off-by Luis R. Rodriguez <[email protected]>--- fs/super.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 2 ++ 2 files changed, 93 insertions(+)fs/super.c +15
-0@@ -605,6 +605,97 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg)spin_unlock (&sb_lock); } +/** +* iterate_supers_excl - exclusively call func for all active superblocks +* @f: function to call +* @arg: argument to pass to it +* +* Scans the superblock list and calls given function, passing it +* locked superblock and given argument. Returns 0 unless an error +* occurred on calling the function on any superblock. +*/ +int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg) +{ +struct super_block *sb, *p =NULL ; +int error =0 ; + +spin_lock (&sb_lock);Quoted 8 lines
fs/super.c void iterate_supers(void (*f)(struct super_block *, void *), void *arg) · line 627
Quoted 5 lines
fs/super.c +5
-0+list_for_each_entry (sb, &super_blocks, s_list) { +if (hlist_unhashed (&sb->s_instances)) +continue ; + sb->s_count++; +spin_unlock (&sb_lock);Can anything bad happen if the list is modified at this point by a concurrent thread?
No. We have a valid reference to
sb->s_countand that keeps it on the list while we have the lock dropped. The sb reference isn't dropped until we've iterated to the next sb on the list and taken a reference to that, hence it's safe to drop and regain the list lock without needing to restart the iteration.Quoted 9 lines
Quoted 9 lines
fs/super.c +9
-0+ +down_write (&sb->s_umount); +if (sb->s_root && (sb->s_flags &SB_BORN )) { + error =f (sb, arg); +if (error) { +up_write (&sb->s_umount); +spin_lock (&sb_lock); +__put_super (sb); +break ;Quoted 8 lines
fs/super.c void iterate_supers(void (*f)(struct super_block *, void *), void *arg) · line 644
Quoted 8 lines
fs/super.c +8
-0+ } + } +up_write (&sb->s_umount); + +spin_lock (&sb_lock); +if (p) +__put_super (p); + p = sb;This code here is what drops the reference to the previous sb we've iterated past.
Quoted 4 lines
FWIW, this "hold until next is held" iteration pattern is used frequently for inodes, dentries, and other reference counted VFS objects so we can iterate the list without needing to hold the list lock for the entire iteration....
OK, thanks!
Re: [PATCH 05/11] fs: add iterate_supers_excl() and iterate_supers_reverse_excl()
In reply to Luis R. RodriguezQuoted 7 lines · Luis R. Rodriguez
There are use cases where we wish to traverse the superblock list but also capture errors, and in which case we want to avoid having our callers issue a lock themselves since we can do the locking for the callers. Provide a
iterate_supers_excl()which calls a function with the write lock held. If an error occurs we capture it and propagate it.
Quoted 5 lines
Likewise there are use cases where we wish to traverse the superblock list but in reverse order. The new
iterate_supers_reverse_excl()helpers does this but also also captures any errors encountered.Signed-off-by Luis R. Rodriguez <[email protected]>
The patch looks good to me. You can add:
Honza
Quoted 123 lines
--- fs/super.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 2 ++ 2 files changed, 93 insertions(+)fs/super.c +91
-0@@ -605,6 +605,97 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg)spin_unlock (&sb_lock); } +/** +* iterate_supers_excl - exclusively call func for all active superblocks +* @f: function to call +* @arg: argument to pass to it +* +* Scans the superblock list and calls given function, passing it +* locked superblock and given argument. Returns 0 unless an error +* occurred on calling the function on any superblock. +*/ +int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg) +{ +struct super_block *sb, *p =NULL ; +int error =0 ; + +spin_lock (&sb_lock); +list_for_each_entry (sb, &super_blocks, s_list) { +if (hlist_unhashed (&sb->s_instances)) +continue ; + sb->s_count++; +spin_unlock (&sb_lock); + +down_write (&sb->s_umount); +if (sb->s_root && (sb->s_flags &SB_BORN )) { + error =f (sb, arg); +if (error) { +up_write (&sb->s_umount); +spin_lock (&sb_lock); +__put_super (sb); +break ; + } + } +up_write (&sb->s_umount); + +spin_lock (&sb_lock); +if (p) +__put_super (p); + p = sb; + } +if (p) +__put_super (p); +spin_unlock (&sb_lock); + +return error; +} + +/** +* iterate_supers_reverse_excl - exclusively calls func in reverse order +* @f: function to call +* @arg: argument to pass to it +* +* Scans the superblock list and calls given function, passing it +* locked superblock and given argument, in reverse order, and holding +* the s_umount write lock. Returns if an error occurred. +*/ +int iterate_supers_reverse_excl (int (*f)(struct super_block *,void *), +void *arg) +{ +struct super_block *sb, *p =NULL ; +int error =0 ; + +spin_lock (&sb_lock); +list_for_each_entry_reverse (sb, &super_blocks, s_list) { +if (hlist_unhashed (&sb->s_instances)) +continue ; + sb->s_count++; +spin_unlock (&sb_lock); + +down_write (&sb->s_umount); +if (sb->s_root && (sb->s_flags &SB_BORN )) { + error =f (sb, arg); +if (error) { +up_write (&sb->s_umount); +spin_lock (&sb_lock); +__put_super (sb); +break ; + } + } +up_write (&sb->s_umount); + +spin_lock (&sb_lock); +if (p) +__put_super (p); + p = sb; + } +if (p) +__put_super (p); +spin_unlock (&sb_lock); + +return error; +} +/** * iterate_supers_type - call function for superblocks of given type * @type: fs type include/linux/fs.h +2
-0@@ -3164,6 +3164,8 @@ extern struct super_block *get_active_super(struct block_device *bdev);extern void drop_super (struct super_block *sb);extern void drop_super_exclusive (struct super_block *sb);extern void iterate_supers (void (*)(struct super_block *,void *),void *); +extern int iterate_supers_excl (int (*f)(struct super_block *,void *),void *arg); +extern int iterate_supers_reverse_excl (int (*)(struct super_block *,void *),void *);extern void iterate_supers_type (struct file_system_type *,void (*)(struct super_block *,void *),void *);Signature · 1 line
2.15.0
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH 6/11 fs: freeze on suspend and thaw on resume
In reply to Luis R. RodriguezThis uses the existing filesystem freeze and thaw callbacks to freeze each filesystem on suspend/hibernation and thaw upon resume.
This is needed so that we properly really stop IO in flight without races after userspace has been frozen. Without this we rely on kthread freezing and its semantics are loose and error prone. For instance, even though a kthread may use try_to_freeze() and end up being frozen we have no way of being sure that everything that has been spawned asynchronously from it (such as timers) have also been stopped as well.
A long term advantage of also adding filesystem freeze / thawing supporting durign suspend / hibernation is that long term we may be able to eventually drop the kernel's thread freezing completely as it was originally added to stop disk IO in flight as we hibernate or suspend.
This also implies that many kthread users exist which have been adding freezer semantics onto its kthreads without need. These also will need to be reviewed later.
This is based on prior work originally by Rafael Wysocki and later by Jiri Kosina.
--- fs/super.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 13 ++++++++ kernel/power/process.c | 15 ++++++++- 3 files changed, 112 insertions(+), 1 deletion(-)
fs/super.c +85 -0
@@ -1648,3 +1648,88 @@ int thaw_super(struct super_block *sb)return error; }EXPORT_SYMBOL (thaw_super); + +#ifdef CONFIG_PM_SLEEP +static bool super_should_freeze (struct super_block *sb) +{ +if (!sb->s_root) +return false; +if (!(sb->s_flags &MS_BORN )) +return false; +/* +* We don't freeze virtual filesystems, we skip those filesystems with +* no backing device. +*/ +if (sb->s_bdi == &noop_backing_dev_info) +return false; +/* No need to freeze read-only filesystems */ +if (sb->s_flags &MS_RDONLY ) +return false; + +return true; +} + +static int fs_suspend_freeze_sb (struct super_block *sb,void *priv) +{ +int error =0 ; + +spin_lock (&sb_lock); +if (!super_should_freeze (sb)) + goto out; + +pr_info ("%s (%s): freezing\n" , sb->s_type->name, sb->s_id); + +spin_unlock (&sb_lock); + +atomic_inc (&sb->s_active); + error =freeze_locked_super (sb, false); +if (error) +atomic_dec (&sb->s_active); + +spin_lock (&sb_lock); +if (error && error != -EBUSY ) +pr_notice ("%s (%s): Unable to freeze, error=%d" , + sb->s_type->name, sb->s_id, error); + +out: +spin_unlock (&sb_lock); +return error; +} + +int fs_suspend_freeze (void ) +{ +return iterate_supers_reverse_excl (fs_suspend_freeze_sb,NULL ); +} + +static int fs_suspend_thaw_sb (struct super_block *sb,void *priv) +{ +int error =0 ; + +spin_lock (&sb_lock); +if (!super_should_freeze (sb)) + goto out; + +pr_info ("%s (%s): thawing\n" , sb->s_type->name, sb->s_id); + +spin_unlock (&sb_lock); + + error =thaw_locked_super (sb, false); +if (!error) +atomic_dec (&sb->s_active); + +spin_lock (&sb_lock); +if (error && error != -EBUSY ) +pr_notice ("%s (%s): Unable to unfreeze, error=%d" , + sb->s_type->name, sb->s_id, error); + +out: +spin_unlock (&sb_lock); +return error; +} + +int fs_resume_unfreeze (void ) +{ +return iterate_supers_excl (fs_suspend_thaw_sb,NULL ); +} + +#endif
include/linux/fs.h +13 -0
@@ -2237,6 +2237,19 @@ extern int user_statfs(const char __user *, struct kstatfs *);extern int fd_statfs (int ,struct kstatfs *);extern int freeze_super (struct super_block *super);extern int thaw_super (struct super_block *super); +#ifdef CONFIG_PM_SLEEP +int fs_suspend_freeze (void ); +int fs_resume_unfreeze (void ); +#else +static inline int fs_suspend_freeze (void ) +{ +return 0 ; +} +static inline int fs_resume_unfreeze (void ) +{ +return 0 ; +} +#endif extern bool our_mnt (struct vfsmount *mnt); extern __printf(2, 3) int super_setup_bdi_name(struct super_block *sb, char *fmt, ...);
kernel/power/process.c +14 -1
@@ -145,6 +145,16 @@ int freeze_processes(void)pr_cont ("\n" );BUG_ON (in_atomic ()); +pr_info ("Freezing filesystems ... " ); + error =fs_suspend_freeze (); +if (error) { +pr_cont ("failed\n" ); +fs_resume_unfreeze (); +thaw_processes (); +return error; + } +pr_cont ("done.\n" ); +/* * Now that the whole userspace is frozen we need to disbale * the OOM killer to disallow any further interference with
@@ -154,8 +164,10 @@ int freeze_processes(void)if (!error && !oom_killer_disable (msecs_to_jiffies (freeze_timeout_msecs))) error = -EBUSY ; -if (error) +if (error) { +fs_resume_unfreeze ();thaw_processes (); + }return error; }
@@ -198,6 +210,7 @@ void thaw_processes(void) pm_nosig_freezing = false;oom_killer_enable (); +fs_resume_unfreeze ();pr_info ("Restarting tasks ... " );
Signature · 1 line
2.15.0
PATCH 7/11 xfs: remove not needed freezing calls
In reply to Luis R. RodriguezThis removes superflous freezer calls as they are no longer needed as the VFS now performs filesystem freezing/thaw if the filesystem has support for it.
The following Coccinelle rule was used as follows:
spatch --sp-file fs-freeze-cleanup.cocci --in-place fs/$FS/
@ has_freeze_fs @
identifier super_ops;
expression freeze_op;
@@
struct super_operations super_ops = {
.freeze_fs = freeze_op,
};
@ remove_set_freezable depends on has_freeze_fs @
expression time;
statement S, S2, S3;
expression task;
@@
(
- set_freezable();
|
- if (try_to_freeze())
- continue;
|
- try_to_freeze();
|
- freezable_schedule();
+ schedule();
|
- freezable_schedule_timeout(time);
+ schedule_timeout(time);
|
- if (freezing(task)) { S }
|
- if (freezing(task)) { S }
- else
{ S2 }|
- freezing(current)
)
--- fs/xfs/xfs_trans_ail.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
fs/xfs/xfs_trans_ail.c +2 -5
@@ -513,7 +513,6 @@ xfsaild(long tout =0 ;/* milliseconds */ current->flags |=PF_MEMALLOC ; -set_freezable ();while (1 ) {if (tout && tout <=20 )
@@ -551,19 +550,17 @@ xfsaild(if (!xfs_ail_min (ailp) && ailp->xa_target == ailp->xa_target_prev) {spin_unlock (&ailp->xa_lock); -freezable_schedule (); +schedule (); tout =0 ;continue ; }spin_unlock (&ailp->xa_lock);if (tout) -freezable_schedule_timeout (msecs_to_jiffies (tout)); +schedule_timeout (msecs_to_jiffies (tout));__set_current_state (TASK_RUNNING ); -try_to_freeze (); - tout =xfsaild_push (ailp); }
Signature · 1 line
2.15.0
Jan Kara
reviewerRe: [PATCH 07/11] xfs: remove not needed freezing calls
Quoted 7 lines · Luis R. Rodriguez
This removes superflous freezer calls as they are no longer needed as the VFS now performs filesystem freezing/thaw if the filesystem has support for it.
The following Coccinelle rule was used as follows:
spatch --sp-file
fs-freeze-cleanup.cocci--in-placefs/$FS/
I think your rule misses WQ_FREEZABLE flag for workqueues? That would be also good to get rid of...
Honza
Quoted 82 lines
@
has_freeze_fs@
identifiersuper_ops;
expressionfreeze_op;
@@struct
super_operationssuper_ops= {
.freeze_fs =freeze_op,
};@
remove_set_freezabledepends onhas_freeze_fs@
expression time;
statement S, S2, S3;
expression task;
@@(
fs/xfs/xfs_trans_ail.c +0
-1-set_freezable ();|
(quoted diff) +0
-2- if (try_to_freeze()) - continue;|
fs/xfs/xfs_trans_ail.c +0
-1-try_to_freeze ();|
fs/xfs/xfs_trans_ail.c +1
-1-freezable_schedule (); +schedule ();|
(quoted diff) +1
-1- freezable_schedule_timeout(time); + schedule_timeout(time);|
(quoted diff) +0
-1- if (freezing(task)) { S }|
(quoted diff) +0
-2- if (freezing(task)) { S } - else{ S2 }|
(quoted diff) +0
-1- freezing(current))
Generated-by Coccinelle SmPLSigned-off-by Luis R. Rodriguez <[email protected]>--- fs/xfs/xfs_trans_ail.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)fs/xfs/xfs_trans_ail.c +2
-5@@ -513,7 +513,6 @@ xfsaild(long tout =0 ;/* milliseconds */ current->flags |=PF_MEMALLOC ; -set_freezable ();while (1 ) {if (tout && tout <=20 )@@ -551,19 +550,17 @@ xfsaild(if (!xfs_ail_min (ailp) && ailp->xa_target == ailp->xa_target_prev) {spin_unlock (&ailp->xa_lock); -freezable_schedule (); +schedule (); tout =0 ;continue ; }spin_unlock (&ailp->xa_lock);if (tout) -freezable_schedule_timeout (msecs_to_jiffies (tout)); +schedule_timeout (msecs_to_jiffies (tout));__set_current_state (TASK_RUNNING ); -try_to_freeze (); - tout =xfsaild_push (ailp); }Signature · 1 line
2.15.0
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Rafael J. Wysocki
maintainerRe: [PATCH 07/11] xfs: remove not needed freezing calls
Quoted 11 lines · Jan Kara
Quoted 7 lines · Luis R. Rodriguez
This removes superflous freezer calls as they are no longer needed as the VFS now performs filesystem freezing/thaw if the filesystem has support for it.
The following Coccinelle rule was used as follows:
spatch --sp-file
fs-freeze-cleanup.cocci--in-placefs/$FS/I think your rule misses
WQ_FREEZABLEflag for workqueues? That would be also good to get rid of...
We need that one (or equivalent) for the runtime PM workqueue at least.
Thanks,
Rafael
Re: [PATCH 07/11] xfs: remove not needed freezing calls
Quoted 6 lines · Rafael J. Wysocki
Quoted 5 lines · Jan Kara
Quoted 4 lines · Luis R. Rodriguez
This removes superflous freezer calls as they are no longer needed as the VFS now performs filesystem freezing/thaw if the filesystem has support for it.
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
The following Coccinelle rule was used as follows:
spatch --sp-file
fs-freeze-cleanup.cocci--in-placefs/$FS/I think your rule misses
WQ_FREEZABLEflag for workqueues? That would be also good to get rid of...We need that one (or equivalent) for the runtime PM workqueue at least.
I think Jan was talking about all the WQ_FREEZABLE flags in filesystem workqueues (such as all the XFS wqs) that are no longer necessary once filesystems are frozen appropriately.
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 07/11] xfs: remove not needed freezing calls
Quoted 11 lines · CET Dave Chinner
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines · Jan Kara
Quoted 7 lines · Luis R. Rodriguez
This removes superflous freezer calls as they are no longer needed as the VFS now performs filesystem freezing/thaw if the filesystem has support for it.
The following Coccinelle rule was used as follows:
spatch --sp-file
fs-freeze-cleanup.cocci--in-placefs/$FS/
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
I think your rule misses
WQ_FREEZABLEflag for workqueues? That would be also good to get rid of...We need that one (or equivalent) for the runtime PM workqueue at least.
I think Jan was talking about all the
WQ_FREEZABLEflags in filesystem workqueues (such as all the XFS wqs) that are no longer necessary once filesystems are frozen appropriately.
I see, thanks. That was unclear to me, sorry for the confusion.
Thanks,
Rafael
PATCH 8/11 ext4: remove not needed freezing calls
In reply to Luis R. RodriguezThis removes superflous freezer calls as they are no longer needed as the VFS now performs filesystem freezing/thaw if the filesystem has support for it.
The following Coccinelle rule was used as follows:
spatch --sp-file fs-freeze-cleanup.cocci --in-place fs/$FS/
@ has_freeze_fs @
identifier super_ops;
expression freeze_op;
@@
struct super_operations super_ops = {
.freeze_fs = freeze_op,
};
@ remove_set_freezable depends on has_freeze_fs @
expression time;
statement S, S2, S3;
expression task;
@@
(
- set_freezable();
|
- if (try_to_freeze())
- continue;
|
- try_to_freeze();
|
- freezable_schedule();
+ schedule();
|
- freezable_schedule_timeout(time);
+ schedule_timeout(time);
|
- if (freezing(task)) { S }
|
- if (freezing(task)) { S }
- else
{ S2 }|
- freezing(current)
)
--- fs/ext4/super.c | 2 -- 1 file changed, 2 deletions(-)
fs/ext4/super.c +0 -2
@@ -2980,8 +2980,6 @@ static int ext4_lazyinit_thread(void *arg) }mutex_unlock (&eli->li_list_mtx); -try_to_freeze (); - cur = jiffies;if ((time_after_eq (cur, next_wakeup)) || (MAX_JIFFY_OFFSET == next_wakeup)) {
Signature · 1 line
2.15.0
PATCH 9/11 f2fs: remove not needed freezing calls
In reply to Luis R. RodriguezThis removes superflous freezer calls as they are no longer needed as the VFS now performs filesystem freezing/thaw if the filesystem has support for it.
The following Coccinelle rule was used as follows:
spatch --sp-file fs-freeze-cleanup.cocci --in-place fs/$FS/
@ has_freeze_fs @
identifier super_ops;
expression freeze_op;
@@
struct super_operations super_ops = {
.freeze_fs = freeze_op,
};
@ remove_set_freezable depends on has_freeze_fs @
expression time;
statement S, S2, S3;
expression task;
@@
(
- set_freezable();
|
- if (try_to_freeze())
- continue;
|
- try_to_freeze();
|
- freezable_schedule();
+ schedule();
|
- freezable_schedule_timeout(time);
+ schedule_timeout(time);
|
- if (freezing(task)) { S }
|
- if (freezing(task)) { S }
- else
{ S2 }|
- freezing(current)
)
--- fs/f2fs/gc.c | 5 +---- fs/f2fs/segment.c | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-)
fs/f2fs/gc.c +1 -4
@@ -32,10 +32,9 @@ static int gc_thread_func(void *data) wait_ms = gc_th->min_sleep_time; -set_freezable (); do { wait_event_interruptible_timeout(*wq, - kthread_should_stop() || freezing(current) || + kthread_should_stop() || gc_th->gc_wake, msecs_to_jiffies(wait_ms));
@@ -43,8 +42,6 @@ static int gc_thread_func(void *data)if (gc_th->gc_wake) gc_th->gc_wake =0 ; -if (try_to_freeze ()) -continue ;if (kthread_should_stop ())break ;
fs/f2fs/segment.c +1 -5
@@ -1382,18 +1382,14 @@ static int issue_discard_thread(void *data)unsigned int wait_ms =DEF_MIN_DISCARD_ISSUE_TIME ;int issued; -set_freezable (); - do { init_discard_policy(&dpolicy, DPOLICY_BG, dcc->discard_granularity); wait_event_interruptible_timeout(*q, - kthread_should_stop() || freezing(current) || + kthread_should_stop() || dcc->discard_wake, msecs_to_jiffies(wait_ms)); - if (try_to_freeze()) - continue; if (kthread_should_stop()) return 0;
Signature · 1 line
2.15.0
PATCH 10/11 nilfs2: remove not needed freezing calls
In reply to Luis R. RodriguezThis removes superflous freezer calls as they are no longer needed as the VFS now performs filesystem freezing/thaw if the filesystem has support for it.
The following Coccinelle rule was used as follows:
spatch --sp-file fs-freeze-cleanup.cocci --in-place fs/$FS/
@ has_freeze_fs @
identifier super_ops;
expression freeze_op;
@@
struct super_operations super_ops = {
.freeze_fs = freeze_op,
};
@ remove_set_freezable depends on has_freeze_fs @
expression time;
statement S, S2, S3;
expression task;
@@
(
- set_freezable();
|
- if (try_to_freeze())
- continue;
|
- try_to_freeze();
|
- freezable_schedule();
+ schedule();
|
- freezable_schedule_timeout(time);
+ schedule_timeout(time);
|
- if (freezing(task)) { S }
|
- if (freezing(task)) { S }
- else
{ S2 }|
- freezing(current)
)
--- fs/nilfs2/segment.c | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-)
fs/nilfs2/segment.c +20 -28
@@ -2543,6 +2543,8 @@ static int nilfs_segctor_thread(void *arg)struct nilfs_sc_info *sci = (struct nilfs_sc_info *)arg;struct the_nilfs *nilfs = sci->sc_super->s_fs_info;int timeout =0 ; +DEFINE_WAIT (wait); +int should_sleep =1 ; sci->sc_timer_task = current;
@@ -2574,38 +2576,28 @@ static int nilfs_segctor_thread(void *arg) timeout =0 ; } +prepare_to_wait (&sci->sc_wait_daemon, &wait, +TASK_INTERRUPTIBLE ); -if (freezing (current)) { +if (sci->sc_seq_request != sci->sc_seq_done) + should_sleep =0 ; +else if (sci->sc_flush_request) + should_sleep =0 ; +else if (sci->sc_state &NILFS_SEGCTOR_COMMIT ) + should_sleep =time_before (jiffies, + sci->sc_timer.expires); + +if (should_sleep) {spin_unlock (&sci->sc_state_lock); -try_to_freeze (); +schedule ();spin_lock (&sci->sc_state_lock); - }else { -DEFINE_WAIT (wait); -int should_sleep =1 ; - -prepare_to_wait (&sci->sc_wait_daemon, &wait, -TASK_INTERRUPTIBLE ); - -if (sci->sc_seq_request != sci->sc_seq_done) - should_sleep =0 ; -else if (sci->sc_flush_request) - should_sleep =0 ; -else if (sci->sc_state &NILFS_SEGCTOR_COMMIT ) - should_sleep =time_before (jiffies, - sci->sc_timer.expires); - -if (should_sleep) { -spin_unlock (&sci->sc_state_lock); -schedule (); -spin_lock (&sci->sc_state_lock); - } -finish_wait (&sci->sc_wait_daemon, &wait); - timeout = ((sci->sc_state &NILFS_SEGCTOR_COMMIT ) && -time_after_eq (jiffies, sci->sc_timer.expires)); - -if (nilfs_sb_dirty (nilfs) &&nilfs_sb_need_update (nilfs)) -set_nilfs_discontinued (nilfs); } +finish_wait (&sci->sc_wait_daemon, &wait); + timeout = ((sci->sc_state &NILFS_SEGCTOR_COMMIT ) && +time_after_eq (jiffies, sci->sc_timer.expires)); + +if (nilfs_sb_dirty (nilfs) &&nilfs_sb_need_update (nilfs)) +set_nilfs_discontinued (nilfs); goto loop; end_thread:
Signature · 1 line
2.15.0
PATCH 11/11 jfs: remove not needed freezing calls
In reply to Luis R. RodriguezThis removes superflous freezer calls as they are no longer needed as the VFS now performs filesystem freezing/thaw if the filesystem has support for it.
The following Coccinelle rule was used as follows:
spatch --sp-file fs-freeze-cleanup.cocci --in-place fs/$FS/
@ has_freeze_fs @
identifier super_ops;
expression freeze_op;
@@
struct super_operations super_ops = {
.freeze_fs = freeze_op,
};
@ remove_set_freezable depends on has_freeze_fs @
expression time;
statement S, S2, S3;
expression task;
@@
(
- set_freezable();
|
- if (try_to_freeze())
- continue;
|
- try_to_freeze();
|
- freezable_schedule();
+ schedule();
|
- freezable_schedule_timeout(time);
+ schedule_timeout(time);
|
- if (freezing(task)) { S }
|
- if (freezing(task)) { S }
- else
{ S2 }|
- freezing(current)
)
--- fs/jfs/jfs_logmgr.c | 11 +++-------- fs/jfs/jfs_txnmgr.c | 31 +++++++++---------------------- 2 files changed, 12 insertions(+), 30 deletions(-)
fs/jfs/jfs_logmgr.c +3 -8
@@ -2344,14 +2344,9 @@ int jfsIOWait(void *arg)spin_lock_irq (&log_redrive_lock); } -if (freezing (current)) { -spin_unlock_irq (&log_redrive_lock); -try_to_freeze (); - }else { -set_current_state (TASK_INTERRUPTIBLE ); -spin_unlock_irq (&log_redrive_lock); -schedule (); - } +set_current_state (TASK_INTERRUPTIBLE ); +spin_unlock_irq (&log_redrive_lock); +schedule (); }while (!kthread_should_stop ());jfs_info ("jfsIOWait being killed!" );
fs/jfs/jfs_txnmgr.c +9 -22
@@ -2747,6 +2747,7 @@ int jfs_lazycommit(void *arg)struct tblock *tblk;unsigned long flags;struct jfs_sb_info *sbi; +DECLARE_WAITQUEUE (wq, current); do { LAZY_LOCK(flags);
@@ -2793,19 +2794,11 @@ int jfs_lazycommit(void *arg) }/* In case a wakeup came while all threads were active */ jfs_commit_thread_waking =0 ; - -if (freezing (current)) { -LAZY_UNLOCK (flags); -try_to_freeze (); - }else { -DECLARE_WAITQUEUE (wq, current); - -add_wait_queue (&jfs_commit_thread_wait, &wq); -set_current_state (TASK_INTERRUPTIBLE ); -LAZY_UNLOCK (flags); -schedule (); -remove_wait_queue (&jfs_commit_thread_wait, &wq); - } +add_wait_queue (&jfs_commit_thread_wait, &wq); +set_current_state (TASK_INTERRUPTIBLE ); +LAZY_UNLOCK (flags); +schedule (); +remove_wait_queue (&jfs_commit_thread_wait, &wq); }while (!kthread_should_stop ());if (!list_empty (&TxAnchor.unlock_queue))
@@ -2982,15 +2975,9 @@ int jfs_sync(void *arg) }/* Add anon_list2 back to anon_list */ list_splice_init (&TxAnchor.anon_list2, &TxAnchor.anon_list); - -if (freezing (current)) { -TXN_UNLOCK (); -try_to_freeze (); - }else { -set_current_state (TASK_INTERRUPTIBLE ); -TXN_UNLOCK (); -schedule (); - } +set_current_state (TASK_INTERRUPTIBLE ); +TXN_UNLOCK (); +schedule (); }while (!kthread_should_stop ());jfs_info ("jfs_sync being killed" );
Signature · 1 line
2.15.0
Quoted 7 lines · Luis R. Rodriguez
This is a followup from the original RFC which proposed to start to kill kthread freezing all together [0]. Instead of going straight out to the jugular for kthread freezing this series only addresses killing freezer calls on filesystems which implement
freeze_fs, after we let the kernel freeze these filesystems for us on suspend.
Quoted 9 lines
o xfs
o reiserfs
o nilfs2
o jfs
o f2fs
o ext4
o ext2
o btrfs
Thanks for your work on this! I'll test xfs part first. BTW, is nfs able to be included in this set? I also encountered a freeze() failure
due to nfs access during that stage recently.
Signature · 2 lines
Thanks, Yu
Quoted 2 lines · Yu Chen
BTW, is nfs able to be included in this set? I also encountered a
freeze()failure due to nfs access during that stage recently.
The freezer usage in NFS is magnitudes more complicated, so it makes sense to first go after the lower hanging fruit to figure out the viability of the whole aproach in practice.
Signature · 2 lines
Jiri Kosina SUSE Labs
Quoted 8 lines · Jiri Kosina
Quoted 2 lines · Yu Chen
BTW, is nfs able to be included in this set? I also encountered a
freeze()failure due to nfs access during that stage recently.The freezer usage in NFS is magnitudes more complicated, so it makes sense to first go after the lower hanging fruit to figure out the viability of the whole aproach in practice.
Ok, thanks!
Quoted 9 lines · Jiri Kosina
Quoted 2 lines · Yu Chen
BTW, is nfs able to be included in this set? I also encountered a
freeze()failure due to nfs access during that stage recently.The freezer usage in NFS is magnitudes more complicated, so it makes sense to first go after the lower hanging fruit to figure out the viability of the whole aproach in practice.
Agreed that we should do this in stages. It doesn't help that freezer handling in the client is a bit of a mess at this point...
At a high level for NFS, I think we need to have freeze_fs make the RPC engine "park" newly issued RPCs for that fs' client onto a rpc_wait_queue. Any RPC that has already been sent however, we need to wait for a reply.
Once everything is quiesced we can return and call it frozen. unfreeze_fs can then just have the engine stop parking RPCs and wake up the waitq.
That should be enough to make suspend and resume work more reliably. If, however, you're interested in making the cgroup freezer also work, then we may need to do a bit more work to ensure that we don't end up with frozen tasks squatting on VFS locks.
Signature · 1 line
Jeff Layton <[email protected]>
Quoted 14 lines · Jeff Layton
Quoted 9 lines · Jiri Kosina
Quoted 2 lines · Yu Chen
BTW, is nfs able to be included in this set? I also encountered a
freeze()failure due to nfs access during that stage recently.The freezer usage in NFS is magnitudes more complicated, so it makes sense to first go after the lower hanging fruit to figure out the viability of the whole aproach in practice.
Agreed that we should do this in stages. It doesn't help that freezer handling in the client is a bit of a mess at this point...
Quoted 8 lines
At a high level for NFS, I think we need to have
freeze_fsmake the RPC engine "park" newly issued RPCs for that fs' client onto arpc_wait_queue. Any RPC that has already been sent however, we need to wait for a reply.Once everything is quiesced we can return and call it frozen.
unfreeze_fscan then just have the engine stop parking RPCs and wake up the waitq.
That seems pretty reasonable. freezing is expected to take a bit of time to run - local filesystems can do a fair bit of IO draining queues, inflight operations and bringing the journal into a consistent state on disk before declaring the filesystem is frozen.
Quoted 4 lines
That should be enough to make suspend and resume work more reliably. If, however, you're interested in making the cgroup freezer also work, then we may need to do a bit more work to ensure that we don't end up with frozen tasks squatting on VFS locks.
None of the existing freezing code gives those guarantees. In fact, freezing a filesystem pretty much guarantees the opposite - that tasks *will freeze when holding VFS locks* - and so the cgroup freezer is broken by design if it requires tasks to be frozen without holding any VFS/filesystem lock context. So I wouldn't really worry about the cgroup freezer....
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Quoted 48 lines · Luis R. Rodriguez
This is a followup from the original RFC which proposed to start to kill kthread freezing all together [0]. Instead of going straight out to the jugular for kthread freezing this series only addresses killing freezer calls on filesystems which implement
freeze_fs, after we let the kernel freeze these filesystems for us on suspend.This approach puts on a slow but steady path towards the original goal though. Each subsystem could look for similar solutions. Even with filesystems we're not all done yet, after this we'll still have to decide what to do about filesystems which do not implement
freeze_fs().Motivation and problem:
kthreads have some semantics for freezing, which helps the kernel freeze them when a system is going to suspend or hibernation. These semantics are not well defined though, and it actually turns out pretty hard to get it right.
Without a proper solution suspend and hibernation are fragile on filesystems, it can easily break suspend and fixing such issues are in no way trivial [1] [2].
Proposed solution:
Instead of fixing such semantics and trying to get all filesystems to do it right, we can easily do away with all freezing calls if the filesystem implements a proper
freeze_fs()callback. The following 9 filesystems havefreeze_fs()implemented as such we can let the kernel issue the callback upon suspend and thaw on resume automatically on our behalf.o xfs
o reiserfs
o nilfs2
o jfs
o f2fs
o ext4
o ext2
o btrfsOf these, the following have freezer helpers, which can then be removed after the kernel automaticaly calls
freeze_fsfor us on suspend:o xfs
o nilfs2
o jfs
o f2fs
o ext4
Quoted 5 lines
I've tested this on a system with ext4 and XFS, and have let 0-day go at without issues. I have branches availabe for linux-next [3] and Linus' latest tree [4].
Further testing, thoughts, reviews, flames are all equally appreciated.
Hello Luis,
It's great to see that you are making progress with this work :-) However, what's not clear to me is what your (long-term) plan is for freezing filesystems that e.g. exist on top of a md RAID1 block device? The md resync thread must be stopped before a system is frozen. Today the md driver uses the kthread freezing mechanism for that purpose. Do you have a plan for handling the more complicated scenarios, e.g. a filesystem that exists on top of an md device where the md device uses one or more files as backing store and with the loop driver between the md device and the files?
How about filesystems implemented in user space using the FUSE driver? Patch 6/11 of this series freezes user space processes before freezing filesystems. Will that work for FUSE filesystems?
Thanks,
Bart.
Quoted 3 lines · Bart Van Assche
It's great to see that you are making progress with this work :-) However, what's not clear to me is what your (long-term) plan is for freezing filesystems that e.g. exist on top of a md RAID1 block device?
The original approach of doing a full swing kill on kthread freezing helpers in one shot was too much. We must therefore approach this turtle style and carefully. This patch series currently only addresses filesystems which have a freeze_fs() callback implemented, that's it. The rest of the kernel needs more work.
So this work is far as I've gotten so far.
Note, it does *not* remove the kthread freezing API calls on any other drivers, as such that should still work.
Quoted 6 lines
The md resync
thread must be stopped before a system is frozen. Today the md driver uses the kthread freezing mechanism for that purpose. Do you have a plan for handling the more complicated scenarios, e.g. a filesystem that exists on top of an md device where the md device uses one or more files as backing store and with the loop driver between the md device and the files?
Nope not yet. It seems you have given this some thought though so you're help here is greatly appreciated. In fact the way we should see the long term 'kill kthread freezing' effort should be a collaborative one. I've never touched md, so folks more familiar with md should give this some thought.
Can for instance md register_pm_notifier() and register_syscore_ops() and do handy work to pause some work to replace kthread freezing? Note that I believe a pm notifier is needed in case syscore_suspend() is not called, say on a suspend fail.
Quoted 1 line
How about filesystems implemented in user space using the FUSE driver?
Not sure, someone more familiar with FUSE drivers should chime in. Right now all this does is leverage filesystems which *do* implement freeze_fs() only, and removes the kthread freezing calls after that. That's all. It should not affect any other filesystems
Likewise consider filesystems that implement freeze_super() instead (like gfs2) which don't hold sb->s_umount -- see commit 48b6bca6b7b8309 ("fs: add freeze_super/thaw_super fs hooks"). If its desirable to address those as well, it would seem a pair of non-locking iters somehow are needed. More work to be done.
This all needs to be considered for the future as well.
Quoted 2 lines
Patch 6/11 of this series freezes user space processes before freezing filesystems. Will that work for FUSE filesystems?
It doesn't add new code which should negatively affect FUSE drivers. Long term if we wanted to address FUSE, it may require other work.
In the future then fs_suspend_freeze() may need to work for different types of filesystems. Right now only filesystems which implement freeze_fs() are addressed.
Luis
T24gVGh1LCAyMDE3LTExLTMwIGF0IDIwOjQyICswMTAwLCBMdWlzIFIuIFJvZHJpZ3VleiB3cm90 ZToNCj4gT24gVGh1LCBOb3YgMzAsIDIwMTcgYXQgMDU6MDE6MTNQTSArMDAwMCwgQmFydCBWYW4g QXNzY2hlIHdyb3RlOg0KPiA+IFRoZSBtZCByZXN5bmMNCj4gPiB0aHJlYWQgbXVzdCBiZSBzdG9w cGVkIGJlZm9yZSBhIHN5c3RlbSBpcyBmcm96ZW4uIFRvZGF5IHRoZSBtZCBkcml2ZXIgdXNlcw0K PiA+IHRoZSBrdGhyZWFkIGZyZWV6aW5nIG1lY2hhbmlzbSBmb3IgdGhhdCBwdXJwb3NlLiBEbyB5 b3UgaGF2ZSBhIHBsYW4gZm9yDQo+ID4gaGFuZGxpbmcgdGhlIG1vcmUgY29tcGxpY2F0ZWQgc2Nl bmFyaW9zLCBlLmcuIGEgZmlsZXN5c3RlbSB0aGF0IGV4aXN0cyBvbiB0b3ANCj4gPiBvZiBhbiBt ZCBkZXZpY2Ugd2hlcmUgdGhlIG1kIGRldmljZSB1c2VzIG9uZSBvciBtb3JlIGZpbGVzIGFzIGJh Y2tpbmcgc3RvcmUNCj4gPiBhbmQgd2l0aCB0aGUgbG9vcCBkcml2ZXIgYmV0d2VlbiB0aGUgbWQg ZGV2aWNlIGFuZCB0aGUgZmlsZXM/DQo+IA0KPiBOb3BlIG5vdCB5ZXQuIEl0IHNlZW1zIHlvdSBo YXZlIGdpdmVuIHRoaXMgc29tZSB0aG91Z2h0IHRob3VnaCBzbyB5b3UncmUgDQo+IGhlbHAgaGVy ZSBpcyBncmVhdGx5IGFwcHJlY2lhdGVkLiBJbiBmYWN0IHRoZSB3YXkgd2Ugc2hvdWxkIHNlZSB0 aGUgbG9uZw0KPiB0ZXJtICdraWxsIGt0aHJlYWQgZnJlZXppbmcnIGVmZm9ydCBzaG91bGQgYmUg YSBjb2xsYWJvcmF0aXZlIG9uZS4gSSd2ZQ0KPiBuZXZlciB0b3VjaGVkIG1kLCBzbyBmb2xrcyBt b3JlIGZhbWlsaWFyIHdpdGggbWQgc2hvdWxkIGdpdmUgdGhpcyBzb21lDQo+IHRob3VnaHQuDQo+ IA0KPiBDYW4gZm9yIGluc3RhbmNlIG1kIHJlZ2lzdGVyX3BtX25vdGlmaWVyKCkgYW5kIHJlZ2lz dGVyX3N5c2NvcmVfb3BzKCkNCj4gYW5kIGRvIGhhbmR5IHdvcmsgdG8gcGF1c2Ugc29tZSB3b3Jr IHRvIHJlcGxhY2Uga3RocmVhZCBmcmVlemluZz8NCj4gTm90ZSB0aGF0IEkgYmVsaWV2ZSBhIHBt IG5vdGlmaWVyIGlzIG5lZWRlZCBpbiBjYXNlIHN5c2NvcmVfc3VzcGVuZCgpDQo+IGlzIG5vdCBj YWxsZWQsIHNheSBvbiBhIHN1c3BlbmQgZmFpbC4NCg0KU29ycnkgYnV0IEkgZG9uJ3QgdGhpbmsg dGhhdCBhIHNvbHV0aW9uIGNhbiBiZSBiYXNlZCBvbiBhIG5vdGlmaWVyIG1lY2hhbmlzbS4NCkZy ZWV6aW5nIGhhcyB0byBoYXBwZW4gaW4gdGhlIG9yZGVyIGluIHdoaWNoIGRyaXZlcnMgYW5kIGZp bGVzeXN0ZW1zIGhhdmUNCmJlZW4gc3RhY2tlZCAoZmlsZXN5c3RlbSA+IG1kIGRldmljZSA+IGZp bGVzeXN0ZW0gZm9yIHRoZSBhYm92ZSBleGFtcGxlKS4NClNpbmNlIHRoZSBvcmRlciBpbiB3aGlj aCBub3RpZmllcnMgYXJlIGNhbGxlZCBpcyByZWxhdGVkIHRvIHRoZSBvcmRlciBpbg0Kd2hpY2gg bm90aWZpZXJzIGhhdmUgYmVlbiByZWdpc3RlcmVkIEkgZG9uJ3QgdGhpbmsgdGhhdCBhIHNvbHV0 aW9uIGZvciB0aGUNCmV4YW1wbGUgSSBkZXNjcmliZWQgY2FuIGJlIGJhc2VkIG9uIG5vdGlmaWVy cy4gV2hhdCBJIHRoaW5rIHdlIG5lZWQgaXMgYQ0KbWVjaGFuaXNtIGZvciB0cmF2ZXJzaW5nIHRo ZSBzdG9yYWdlIHN0YWNrIHRoYXQgaW5jbHVkZXMgYmxvY2sgZHJpdmVycyBhbmQNCmFuIGVxdWl2 YWxlbnQgb2YgZnJlZXplX2ZzKCkgZm9yIGJsb2NrIGRyaXZlcnMuIEZyZWV6aW5nIHNob3VsZCBv Y2N1ciBieQ0KY2FsbGluZyB0aGUgZnJlZXplX2ZzKCkgbWV0aG9kcyBmb3IgZWFjaCBzdG9yYWdl IGxheWVyIHN0YXJ0aW5nIGF0IHRoZSB0b3Agb2YNCnRoZSBzdG9yYWdlIHN0YWNrIGFuZCBwcm9j ZWVkaW5nIHRvd2FyZHMgdGhlIGJvdHRvbS4NCg0KQmFydC4=
Quoted 15 lines · Bart Van Assche
Quoted 14 lines · Luis R. Rodriguez
Quoted 6 lines · Bart Van Assche
The md resync
thread must be stopped before a system is frozen. Today the md driver uses the kthread freezing mechanism for that purpose. Do you have a plan for handling the more complicated scenarios, e.g. a filesystem that exists on top of an md device where the md device uses one or more files as backing store and with the loop driver between the md device and the files?Nope not yet. It seems you have given this some thought though so you're help here is greatly appreciated. In fact the way we should see the long term 'kill kthread freezing' effort should be a collaborative one. I've never touched md, so folks more familiar with md should give this some thought.
Quoted 8 lines
Quoted 4 lines
Can for instance md
register_pm_notifier()andregister_syscore_ops()and do handy work to pause some work to replace kthread freezing? Note that I believe a pm notifier is needed in casesyscore_suspend()is not called, say on a suspend fail.Sorry but I don't think that a solution can be based on a notifier mechanism. Freezing has to happen in the order in which drivers and filesystems have been stacked (filesystem > md device > filesystem for the above example).
Yup, we need top down stack freezing. We get that at the filesystem layer by the reverse order superblock iteration - that freezes newer mounts before older mounts, so things like filesystems on loopback freeze before the lower filesystem that hosts the loopback image.
I think what we need is the opposite of the "freeze_bdev()" interface. That allows a block device to freeze the superblock on the block device. i.e. when the block device needs to quiesce (e.g. for a snapshot to be taken) it locks the filesystem and waits for it to quiesce, then does what it needs to and unlocks the filesystem.
ISTM that you're asking for the opposite of this - a call that allows the superblock to quiesce the underlying block device into a known, unchanging state. This would allow MD/dm to suspend whatever on-going maintenance operations it has in progress until the filesystem says "we need you to start again" when it gets a thaw call....
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Hi!
Quoted 26 lines
Proposed solution:
Instead of fixing such semantics and trying to get all filesystems to do it right, we can easily do away with all freezing calls if the filesystem implements a proper
freeze_fs()callback. The following 9 filesystems havefreeze_fs()implemented as such we can let the kernel issue the callback upon suspend and thaw on resume automatically on our behalf.o xfs
o reiserfs
o nilfs2
o jfs
o f2fs
o ext4
o ext2
o btrfsOf these, the following have freezer helpers, which can then be removed after the kernel automaticaly calls
freeze_fsfor us on suspend:o xfs
o nilfs2
o jfs
o f2fs
o ext4
Quoted 3 lines
I've tested this on a system with ext4 and XFS, and have let 0-day go at without issues. I have branches availabe for linux-next [3] and Linus' latest tree [4].
Was hibernation tested? uswsusp?
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Quoted 28 lines · Pavel Machek
Hi!
Quoted 26 lines
Proposed solution:
Instead of fixing such semantics and trying to get all filesystems to do it right, we can easily do away with all freezing calls if the filesystem implements a proper
freeze_fs()callback. The following 9 filesystems havefreeze_fs()implemented as such we can let the kernel issue the callback upon suspend and thaw on resume automatically on our behalf.o xfs
o reiserfs
o nilfs2
o jfs
o f2fs
o ext4
o ext2
o btrfsOf these, the following have freezer helpers, which can then be removed after the kernel automaticaly calls
freeze_fsfor us on suspend:o xfs
o nilfs2
o jfs
o f2fs
o ext4
Quoted 5 lines
Quoted 3 lines
I've tested this on a system with ext4 and XFS, and have let 0-day go at without issues. I have branches availabe for linux-next [3] and Linus' latest tree [4].
Was hibernation tested? uswsusp?
I had not done a test before but I just did and it worked, in fact I was able to keep my ssh connection to my qemu guest after resume from hibernation with this.
root@piggy:~# echo shutdown > /sys/power/disk; echo disk > /sys/power/state
[ 87.930446] PM: hibernation entry [ 87.936294] firmware_class: device_cache_fw_images [ 87.936363] PM: Syncing filesystems ... [ 87.979960] PM: done. [ 87.980594] Freezing user space processes ... (elapsed 0.001 seconds) done. [ 87.983839] Freezing filesystems ... [ 87.983844] xfs (sdb1): freezing [ 88.013313] ext4 (sda1): freezing [ 88.057635] done. [ 88.058242] OOM killer disabled. ... [ 88.145364] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done. [ 88.221583] Disabling non-boot CPUs ... [ 88.241231] Unregister pv shared memory for cpu 1 [ 88.244139] smpboot: CPU 1 is now offline [ 88.273290] Unregister pv shared memory for cpu 2 [ 88.276170] smpboot: CPU 2 is now offline [ 88.297296] Unregister pv shared memory for cpu 3 [ 88.299991] smpboot: CPU 3 is now offline [ 88.302665] PM: Creating hibernation image: [ 88.305633] PM: Need to copy 107866 pages [ 88.305633] PM: Normal pages needed: 107866 + 1024, available pages: 940488
...
At this point my qemu session ends. I start it up again.
88.305633] Enabling non-boot CPUs ...
[ 88.305633] x86: Booting SMP configuration: [ 88.305633] smpboot: Booting Node 0 Processor 1 APIC 0x1 [ 88.244097] kvm-clock: cpu 1, msr 1:3ffef041, secondary cpu clock [ 88.334899] KVM setup async PF for cpu 1 [ 88.335174] kvm-stealtime: cpu 1, msr 13fc8d9c0 ... [ 88.424852] sd 0:0:0:0: [sda] Starting disk [ 88.424893] sd 0:0:1:0: [sdb] Starting disk [ 88.424928] sd 1:0:0:0: [sdc] Starting disk [ 88.473888] PM: Basic memory bitmaps freed [ 88.473890] OOM killer enabled. [ 88.474876] ext4 (sda1): thawing [ 88.585233] ata2.01: NODEV after polling detection [ 88.587805] ata2.00: configured for MWDMA2 [ 88.589436] ata1.00: configured for MWDMA2 [ 88.592421] ata1.01: configured for MWDMA2 [ 88.601141] xfs (sdb1): thawing [ 88.602430] Restarting tasks ... done. [ 88.611055] PM: hibernation exit [ 90.492926] e1000: ens3 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX [ 98.780159] firmware_class: device_uncache_fw_images
And ssh is working after this, without requiring to initiate another connection.
Let me know if you'd like me to test something else.
Luis
Quoted 74 lines · CET Luis R. Rodriguez
This is a followup from the original RFC which proposed to start to kill kthread freezing all together [0]. Instead of going straight out to the jugular for kthread freezing this series only addresses killing freezer calls on filesystems which implement
freeze_fs, after we let the kernel freeze these filesystems for us on suspend.This approach puts on a slow but steady path towards the original goal though. Each subsystem could look for similar solutions. Even with filesystems we're not all done yet, after this we'll still have to decide what to do about filesystems which do not implement
freeze_fs().Motivation and problem:
kthreads have some semantics for freezing, which helps the kernel freeze them when a system is going to suspend or hibernation. These semantics are not well defined though, and it actually turns out pretty hard to get it right.
Without a proper solution suspend and hibernation are fragile on filesystems, it can easily break suspend and fixing such issues are in no way trivial [1] [2].
Proposed solution:
Instead of fixing such semantics and trying to get all filesystems to do it right, we can easily do away with all freezing calls if the filesystem implements a proper
freeze_fs()callback. The following 9 filesystems havefreeze_fs()implemented as such we can let the kernel issue the callback upon suspend and thaw on resume automatically on our behalf.o xfs
o reiserfs
o nilfs2
o jfs
o f2fs
o ext4
o ext2
o btrfsOf these, the following have freezer helpers, which can then be removed after the kernel automaticaly calls
freeze_fsfor us on suspend:o xfs
o nilfs2
o jfs
o f2fs
o ext4I've tested this on a system with ext4 and XFS, and have let 0-day go at without issues. I have branches availabe for linux-next [3] and Linus' latest tree [4].
Further testing, thoughts, reviews, flames are all equally appreciated.
[0] https://lkml.kernel.org/r/[email protected]
[1] https://bugzilla.suse.com/show_bug.cgi?id=1043449
[2] https://lkml.kernel.org/r/[email protected]
[3] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20171129-fs-freeze-cleanup
[4] https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux.git/log/?h=20171129-fs-freeze-cleanupLuis R. Rodriguez (11):
fs: provide unlocked helper forfreeze_super()
fs: provide unlocked helperthaw_super()
fs: add frozen sb state helpers
fs: distinguish between user initiated freeze and kernel initiated
freeze
fs: additerate_supers_excl()anditerate_supers_reverse_excl()
fs: freeze on suspend and thaw on resume
xfs: remove not needed freezing calls
ext4: remove not needed freezing calls
f2fs: remove not needed freezing calls
nilfs2: remove not needed freezing calls
jfs: remove not needed freezing calls
Quoted 14 lines
fs/ext4/ext4_jbd2.c | 2 +- fs/ext4/super.c | 2 - fs/f2fs/gc.c | 5 +- fs/f2fs/segment.c | 6 +- fs/jfs/jfs_logmgr.c | 11 +- fs/jfs/jfs_txnmgr.c | 31 ++--- fs/nilfs2/segment.c | 48 ++++---- fs/super.c | 320 ++++++++++++++++++++++++++++++++++++++++--------- fs/xfs/xfs_trans.c | 2 +- fs/xfs/xfs_trans_ail.c | 7 +- include/linux/fs.h | 63 +++++++++- kernel/power/process.c | 15 ++- 12 files changed, 378 insertions(+), 134 deletions(-)
I'm assuming an update of this to be posted due to the comments from Jan on patch [3/11] and patch [7/11] probably.
Is there anything else that needs to be addressed?
Thanks,
Rafael
Quoted 4 lines · Rafael J. Wysocki
I'm assuming an update of this to be posted due to the comments from Jan on patch [3/11] and patch [7/11] probably.
Is there anything else that needs to be addressed?
I was waiting on Jan Kara's feedback on how he'd like to proceed with the unthawing on error given his point on that the device mapper API seems to match the in kernel automatic freezing just that I didn't use that same interface.
0-day did come back with one RCU issue which I also have to address:
[ 422.919958] kernel BUG at kernel/rcu/sync.c:228! [ 422.920115] invalid opcode: 0000 [#1] SMP [ 422.920212] Modules linked in: rpcsec_gss_krb5 auth_rpcgss nfsv4 dns_resolver netconsole sr_mod cdrom sd_mod sg snd_hda_codec_idt snd_hda_codec_generic intel_rapl x86_pkg_temp_thermal +intel_powerclamp coretemp kvm irqbypass crct10dif_pclmul snd_hda_intel crc32_pclmul crc32c_intel snd_hda_codec snd_hda_core snd_hwdep i915 ghash_clmulni_intel cryptd snd_pcm pcspkr +drm_kms_helper snd_timer ahci libahci syscopyarea sysfillrect sysimgblt fb_sys_fops snd libata shpchp soundcore drm video ip_tables [ 422.921168] CPU: 2 PID: 237 Comm: kworker/2:3 Not tainted 4.15.0-rc1-00030-gf95c16a #1 [ 422.921347] Hardware name: Hewlett-Packard p6-1451cx/2ADA, BIOS 8.15 02/05/2013 [ 422.921515] Workqueue: events destroy_super_work [ 422.921628] task: ffff8801bfcd0000 task.stack: ffffc90001128000 [ 422.921768] RIP: 0010:rcu_sync_dtor+0x65/0x70 [ 422.921874] RSP: 0000:ffffc9000112be60 EFLAGS: 00010286 [ 422.921985] RAX: 0000000000080000 RBX: ffff8801c00793d8 RCX: 000000000001fece [ 422.922133] RDX: 00000000fffffff6 RSI: 0000000000000282 RDI: ffff8801c00793d8 [ 422.922283] RBP: ffff880212f1b6c0 R08: 0000000000000000 R09: 000000000000009c [ 422.922432] R10: 0000000000000000 R11: 0000000000000000 R12: ffff880212f1f800 [ 422.922579] R13: 0000000000000000 R14: ffff8801c036db40 R15: ffff8801c00795b0 [ 422.922728] FS: 0000000000000000(0000) GS:ffff880212f00000(0000) knlGS:0000000000000000 [ 422.922931] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 422.923054] CR2: 00007ffe2f820ff8 CR3: 0000000001e09002 CR4: 00000000001606e0 [ 422.923203] Call Trace: [ 422.923266] percpu_free_rwsem+0x15/0x30 [ 422.923357] destroy_super_work+0x3d/0x50 [ 422.923449] process_one_work+0x18f/0x3e0 [ 422.923540] worker_thread+0x3a/0x3b0 [ 422.923623] ? process_one_work+0x3e0/0x3e0 [ 422.923716] kthread+0x11c/0x140 [ 422.923792] ? kthread_create_worker_on_cpu+0x50/0x50 [ 422.923905] ret_from_fork+0x1f/0x30 [ 422.923986] Code: 00 fb 66 0f 1f 44 00 00 65 ff 0d 57 6c f2 7e 85 d2 74 15 8b 43 38 48 8d 04 40 ff 14 c5 f0 f5 a1 81 8b 43 20 85 c0 75 05 5b 5d c3 <0f> 0b 0f 0b 90 90 90 90 90 90 90 0f 1f +44 00 00 8b 87 c8 c3 00 [ 422.924405] RIP: rcu_sync_dtor+0x65/0x70 RSP: ffffc9000112be60
Luis