mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 05:04:09 +08:00
fs: wait for partially frozen filesystems
Jan Kara suggested that when one thread is in the middle of freezing a filesystem, another thread trying to freeze the same fs but with a different freeze_holder should wait until the freezer reaches either end state (UNFROZEN or COMPLETE) instead of returning EBUSY immediately. Neither caller can do anything sensible with this race other than retry but they cannot really distinguish EBUSY as in "some other holder of the same type has the sb already frozen" from "freezing raced with holder of a different type". Plumb in the extra code needed to wait for the fs freezer to reach an end state and try the freeze again. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
880b957785
commit
59ba4fdd2d
34
fs/super.c
34
fs/super.c
@ -1644,6 +1644,24 @@ static void sb_freeze_unlock(struct super_block *sb, int level)
|
||||
percpu_up_write(sb->s_writers.rw_sem + level);
|
||||
}
|
||||
|
||||
static int wait_for_partially_frozen(struct super_block *sb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
do {
|
||||
unsigned short old = sb->s_writers.frozen;
|
||||
|
||||
up_write(&sb->s_umount);
|
||||
ret = wait_var_event_killable(&sb->s_writers.frozen,
|
||||
sb->s_writers.frozen != old);
|
||||
down_write(&sb->s_umount);
|
||||
} while (ret == 0 &&
|
||||
sb->s_writers.frozen != SB_UNFROZEN &&
|
||||
sb->s_writers.frozen != SB_FREEZE_COMPLETE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* freeze_super - lock the filesystem and force it into a consistent state
|
||||
* @sb: the super to lock
|
||||
@ -1695,6 +1713,7 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)
|
||||
atomic_inc(&sb->s_active);
|
||||
down_write(&sb->s_umount);
|
||||
|
||||
retry:
|
||||
if (sb->s_writers.frozen == SB_FREEZE_COMPLETE) {
|
||||
if (sb->s_writers.freeze_holders & who) {
|
||||
deactivate_locked_super(sb);
|
||||
@ -1713,8 +1732,13 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)
|
||||
}
|
||||
|
||||
if (sb->s_writers.frozen != SB_UNFROZEN) {
|
||||
deactivate_locked_super(sb);
|
||||
return -EBUSY;
|
||||
ret = wait_for_partially_frozen(sb);
|
||||
if (ret) {
|
||||
deactivate_locked_super(sb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (!(sb->s_flags & SB_BORN)) {
|
||||
@ -1726,6 +1750,7 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)
|
||||
/* Nothing to do really... */
|
||||
sb->s_writers.freeze_holders |= who;
|
||||
sb->s_writers.frozen = SB_FREEZE_COMPLETE;
|
||||
wake_up_var(&sb->s_writers.frozen);
|
||||
up_write(&sb->s_umount);
|
||||
return 0;
|
||||
}
|
||||
@ -1745,6 +1770,7 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)
|
||||
if (ret) {
|
||||
sb->s_writers.frozen = SB_UNFROZEN;
|
||||
sb_freeze_unlock(sb, SB_FREEZE_PAGEFAULT);
|
||||
wake_up_var(&sb->s_writers.frozen);
|
||||
deactivate_locked_super(sb);
|
||||
return ret;
|
||||
}
|
||||
@ -1760,6 +1786,7 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)
|
||||
"VFS:Filesystem freeze failed\n");
|
||||
sb->s_writers.frozen = SB_UNFROZEN;
|
||||
sb_freeze_unlock(sb, SB_FREEZE_FS);
|
||||
wake_up_var(&sb->s_writers.frozen);
|
||||
deactivate_locked_super(sb);
|
||||
return ret;
|
||||
}
|
||||
@ -1770,6 +1797,7 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)
|
||||
*/
|
||||
sb->s_writers.freeze_holders |= who;
|
||||
sb->s_writers.frozen = SB_FREEZE_COMPLETE;
|
||||
wake_up_var(&sb->s_writers.frozen);
|
||||
lockdep_sb_freeze_release(sb);
|
||||
up_write(&sb->s_umount);
|
||||
return 0;
|
||||
@ -1810,6 +1838,7 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who)
|
||||
if (sb_rdonly(sb)) {
|
||||
sb->s_writers.freeze_holders &= ~who;
|
||||
sb->s_writers.frozen = SB_UNFROZEN;
|
||||
wake_up_var(&sb->s_writers.frozen);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1828,6 +1857,7 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who)
|
||||
|
||||
sb->s_writers.freeze_holders &= ~who;
|
||||
sb->s_writers.frozen = SB_UNFROZEN;
|
||||
wake_up_var(&sb->s_writers.frozen);
|
||||
sb_freeze_unlock(sb, SB_FREEZE_FS);
|
||||
out:
|
||||
deactivate_locked_super(sb);
|
||||
|
Loading…
Reference in New Issue
Block a user