Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Introduce freeze_super and thaw_super for the fsfreeze ioctl

Currently the way we do freezing is by passing sb>s_bdev to freeze_bdev and then
letting it do all the work. But freezing is more of an fs thing, and doesn't
really have much to do with the bdev at all, all the work gets done with the
super. In btrfs we do not populate s_bdev, since we can have multiple bdev's
for one fs and setting s_bdev makes removing devices from a pool kind of tricky.
This means that freezing a btrfs filesystem fails, which causes us to corrupt
with things like tux-on-ice which use the fsfreeze mechanism. So instead of
populating sb->s_bdev with a random bdev in our pool, I've broken the actual fs
freezing stuff into freeze_super and thaw_super. These just take the
super_block that we're freezing and does the appropriate work. It's basically
just copy and pasted from freeze_bdev. I've then converted freeze_bdev over to
use the new super helpers. I've tested this with ext4 and btrfs and verified
everything continues to work the same as before.

The only new gotcha is multiple calls to the fsfreeze ioctl will return EBUSY if
the fs is already frozen. I thought this was a better solution than adding a
freeze counter to the super_block, but if everybody hates this idea I'm open to
suggestions. Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

authored by

Josef Bacik and committed by
Al Viro
18e9e510 e1e46bf1

+109 -70
+15 -57
fs/block_dev.c
··· 245 245 sb = get_active_super(bdev); 246 246 if (!sb) 247 247 goto out; 248 - down_write(&sb->s_umount); 249 - if (sb->s_flags & MS_RDONLY) { 250 - sb->s_frozen = SB_FREEZE_TRANS; 251 - up_write(&sb->s_umount); 248 + error = freeze_super(sb); 249 + if (error) { 250 + deactivate_super(sb); 251 + bdev->bd_fsfreeze_count--; 252 252 mutex_unlock(&bdev->bd_fsfreeze_mutex); 253 - return sb; 253 + return ERR_PTR(error); 254 254 } 255 - 256 - sb->s_frozen = SB_FREEZE_WRITE; 257 - smp_wmb(); 258 - 259 - sync_filesystem(sb); 260 - 261 - sb->s_frozen = SB_FREEZE_TRANS; 262 - smp_wmb(); 263 - 264 - sync_blockdev(sb->s_bdev); 265 - 266 - if (sb->s_op->freeze_fs) { 267 - error = sb->s_op->freeze_fs(sb); 268 - if (error) { 269 - printk(KERN_ERR 270 - "VFS:Filesystem freeze failed\n"); 271 - sb->s_frozen = SB_UNFROZEN; 272 - deactivate_locked_super(sb); 273 - bdev->bd_fsfreeze_count--; 274 - mutex_unlock(&bdev->bd_fsfreeze_mutex); 275 - return ERR_PTR(error); 276 - } 277 - } 278 - up_write(&sb->s_umount); 279 - 255 + deactivate_super(sb); 280 256 out: 281 257 sync_blockdev(bdev); 282 258 mutex_unlock(&bdev->bd_fsfreeze_mutex); ··· 273 297 274 298 mutex_lock(&bdev->bd_fsfreeze_mutex); 275 299 if (!bdev->bd_fsfreeze_count) 276 - goto out_unlock; 300 + goto out; 277 301 278 302 error = 0; 279 303 if (--bdev->bd_fsfreeze_count > 0) 280 - goto out_unlock; 304 + goto out; 281 305 282 306 if (!sb) 283 - goto out_unlock; 307 + goto out; 284 308 285 - BUG_ON(sb->s_bdev != bdev); 286 - down_write(&sb->s_umount); 287 - if (sb->s_flags & MS_RDONLY) 288 - goto out_unfrozen; 289 - 290 - if (sb->s_op->unfreeze_fs) { 291 - error = sb->s_op->unfreeze_fs(sb); 292 - if (error) { 293 - printk(KERN_ERR 294 - "VFS:Filesystem thaw failed\n"); 295 - sb->s_frozen = SB_FREEZE_TRANS; 296 - bdev->bd_fsfreeze_count++; 297 - mutex_unlock(&bdev->bd_fsfreeze_mutex); 298 - return error; 299 - } 309 + error = thaw_super(sb); 310 + if (error) { 311 + bdev->bd_fsfreeze_count++; 312 + mutex_unlock(&bdev->bd_fsfreeze_mutex); 313 + return error; 300 314 } 301 - 302 - out_unfrozen: 303 - sb->s_frozen = SB_UNFROZEN; 304 - smp_wmb(); 305 - wake_up(&sb->s_wait_unfrozen); 306 - 307 - if (sb) 308 - deactivate_locked_super(sb); 309 - out_unlock: 315 + out: 310 316 mutex_unlock(&bdev->bd_fsfreeze_mutex); 311 317 return 0; 312 318 }
+2 -13
fs/ioctl.c
··· 525 525 if (sb->s_op->freeze_fs == NULL) 526 526 return -EOPNOTSUPP; 527 527 528 - /* If a blockdevice-backed filesystem isn't specified, return. */ 529 - if (sb->s_bdev == NULL) 530 - return -EINVAL; 531 - 532 528 /* Freeze */ 533 - sb = freeze_bdev(sb->s_bdev); 534 - if (IS_ERR(sb)) 535 - return PTR_ERR(sb); 536 - return 0; 529 + return freeze_super(sb); 537 530 } 538 531 539 532 static int ioctl_fsthaw(struct file *filp) ··· 536 543 if (!capable(CAP_SYS_ADMIN)) 537 544 return -EPERM; 538 545 539 - /* If a blockdevice-backed filesystem isn't specified, return EINVAL. */ 540 - if (sb->s_bdev == NULL) 541 - return -EINVAL; 542 - 543 546 /* Thaw */ 544 - return thaw_bdev(sb->s_bdev, sb); 547 + return thaw_super(sb); 545 548 } 546 549 547 550 /*
+90
fs/super.c
··· 944 944 945 945 EXPORT_SYMBOL_GPL(vfs_kern_mount); 946 946 947 + /** 948 + * freeze_super -- lock the filesystem and force it into a consistent state 949 + * @super: the super to lock 950 + * 951 + * Syncs the super to make sure the filesystem is consistent and calls the fs's 952 + * freeze_fs. Subsequent calls to this without first thawing the fs will return 953 + * -EBUSY. 954 + */ 955 + int freeze_super(struct super_block *sb) 956 + { 957 + int ret; 958 + 959 + atomic_inc(&sb->s_active); 960 + down_write(&sb->s_umount); 961 + if (sb->s_frozen) { 962 + deactivate_locked_super(sb); 963 + return -EBUSY; 964 + } 965 + 966 + if (sb->s_flags & MS_RDONLY) { 967 + sb->s_frozen = SB_FREEZE_TRANS; 968 + smp_wmb(); 969 + up_write(&sb->s_umount); 970 + return 0; 971 + } 972 + 973 + sb->s_frozen = SB_FREEZE_WRITE; 974 + smp_wmb(); 975 + 976 + sync_filesystem(sb); 977 + 978 + sb->s_frozen = SB_FREEZE_TRANS; 979 + smp_wmb(); 980 + 981 + sync_blockdev(sb->s_bdev); 982 + if (sb->s_op->freeze_fs) { 983 + ret = sb->s_op->freeze_fs(sb); 984 + if (ret) { 985 + printk(KERN_ERR 986 + "VFS:Filesystem freeze failed\n"); 987 + sb->s_frozen = SB_UNFROZEN; 988 + deactivate_locked_super(sb); 989 + return ret; 990 + } 991 + } 992 + up_write(&sb->s_umount); 993 + return 0; 994 + } 995 + EXPORT_SYMBOL(freeze_super); 996 + 997 + /** 998 + * thaw_super -- unlock filesystem 999 + * @sb: the super to thaw 1000 + * 1001 + * Unlocks the filesystem and marks it writeable again after freeze_super(). 1002 + */ 1003 + int thaw_super(struct super_block *sb) 1004 + { 1005 + int error; 1006 + 1007 + down_write(&sb->s_umount); 1008 + if (sb->s_frozen == SB_UNFROZEN) { 1009 + up_write(&sb->s_umount); 1010 + return -EINVAL; 1011 + } 1012 + 1013 + if (sb->s_flags & MS_RDONLY) 1014 + goto out; 1015 + 1016 + if (sb->s_op->unfreeze_fs) { 1017 + error = sb->s_op->unfreeze_fs(sb); 1018 + if (error) { 1019 + printk(KERN_ERR 1020 + "VFS:Filesystem thaw failed\n"); 1021 + sb->s_frozen = SB_FREEZE_TRANS; 1022 + up_write(&sb->s_umount); 1023 + return error; 1024 + } 1025 + } 1026 + 1027 + out: 1028 + sb->s_frozen = SB_UNFROZEN; 1029 + smp_wmb(); 1030 + wake_up(&sb->s_wait_unfrozen); 1031 + deactivate_locked_super(sb); 1032 + 1033 + return 0; 1034 + } 1035 + EXPORT_SYMBOL(thaw_super); 1036 + 947 1037 static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype) 948 1038 { 949 1039 int err;
+2
include/linux/fs.h
··· 1797 1797 extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *, 1798 1798 struct vfsmount *); 1799 1799 extern int vfs_statfs(struct dentry *, struct kstatfs *); 1800 + extern int freeze_super(struct super_block *super); 1801 + extern int thaw_super(struct super_block *super); 1800 1802 1801 1803 extern int current_umask(void); 1802 1804