diff options
author | Josef Bacik <josef@redhat.com> | 2010-03-23 10:34:56 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-05-21 18:31:18 -0400 |
commit | 18e9e5104fcd9a973ffe3eed3816c87f2a1b6cd2 (patch) | |
tree | 635369c866fcb384223618c878e0b4c317790634 /fs | |
parent | e1e46bf1866317d4f86f66bf18d3f07317d9f9ee (diff) |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/block_dev.c | 74 | ||||
-rw-r--r-- | fs/ioctl.c | 15 | ||||
-rw-r--r-- | fs/super.c | 90 |
3 files changed, 108 insertions, 71 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index 39cb6591d37d..1aba036dcabf 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -245,38 +245,14 @@ struct super_block *freeze_bdev(struct block_device *bdev) | |||
245 | sb = get_active_super(bdev); | 245 | sb = get_active_super(bdev); |
246 | if (!sb) | 246 | if (!sb) |
247 | goto out; | 247 | goto out; |
248 | down_write(&sb->s_umount); | 248 | error = freeze_super(sb); |
249 | if (sb->s_flags & MS_RDONLY) { | 249 | if (error) { |
250 | sb->s_frozen = SB_FREEZE_TRANS; | 250 | deactivate_super(sb); |
251 | up_write(&sb->s_umount); | 251 | bdev->bd_fsfreeze_count--; |
252 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 252 | mutex_unlock(&bdev->bd_fsfreeze_mutex); |
253 | return sb; | 253 | return ERR_PTR(error); |
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 | } | 254 | } |
278 | up_write(&sb->s_umount); | 255 | deactivate_super(sb); |
279 | |||
280 | out: | 256 | out: |
281 | sync_blockdev(bdev); | 257 | sync_blockdev(bdev); |
282 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 258 | mutex_unlock(&bdev->bd_fsfreeze_mutex); |
@@ -297,40 +273,22 @@ int thaw_bdev(struct block_device *bdev, struct super_block *sb) | |||
297 | 273 | ||
298 | mutex_lock(&bdev->bd_fsfreeze_mutex); | 274 | mutex_lock(&bdev->bd_fsfreeze_mutex); |
299 | if (!bdev->bd_fsfreeze_count) | 275 | if (!bdev->bd_fsfreeze_count) |
300 | goto out_unlock; | 276 | goto out; |
301 | 277 | ||
302 | error = 0; | 278 | error = 0; |
303 | if (--bdev->bd_fsfreeze_count > 0) | 279 | if (--bdev->bd_fsfreeze_count > 0) |
304 | goto out_unlock; | 280 | goto out; |
305 | 281 | ||
306 | if (!sb) | 282 | if (!sb) |
307 | goto out_unlock; | 283 | goto out; |
308 | |||
309 | BUG_ON(sb->s_bdev != bdev); | ||
310 | down_write(&sb->s_umount); | ||
311 | if (sb->s_flags & MS_RDONLY) | ||
312 | goto out_unfrozen; | ||
313 | |||
314 | if (sb->s_op->unfreeze_fs) { | ||
315 | error = sb->s_op->unfreeze_fs(sb); | ||
316 | if (error) { | ||
317 | printk(KERN_ERR | ||
318 | "VFS:Filesystem thaw failed\n"); | ||
319 | sb->s_frozen = SB_FREEZE_TRANS; | ||
320 | bdev->bd_fsfreeze_count++; | ||
321 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
322 | return error; | ||
323 | } | ||
324 | } | ||
325 | |||
326 | out_unfrozen: | ||
327 | sb->s_frozen = SB_UNFROZEN; | ||
328 | smp_wmb(); | ||
329 | wake_up(&sb->s_wait_unfrozen); | ||
330 | 284 | ||
331 | if (sb) | 285 | error = thaw_super(sb); |
332 | deactivate_locked_super(sb); | 286 | if (error) { |
333 | out_unlock: | 287 | bdev->bd_fsfreeze_count++; |
288 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | ||
289 | return error; | ||
290 | } | ||
291 | out: | ||
334 | mutex_unlock(&bdev->bd_fsfreeze_mutex); | 292 | mutex_unlock(&bdev->bd_fsfreeze_mutex); |
335 | return 0; | 293 | return 0; |
336 | } | 294 | } |
diff --git a/fs/ioctl.c b/fs/ioctl.c index 7faefb4da939..2d140a713861 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c | |||
@@ -525,15 +525,8 @@ static int ioctl_fsfreeze(struct file *filp) | |||
525 | if (sb->s_op->freeze_fs == NULL) | 525 | if (sb->s_op->freeze_fs == NULL) |
526 | return -EOPNOTSUPP; | 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 | /* Freeze */ | 528 | /* Freeze */ |
533 | sb = freeze_bdev(sb->s_bdev); | 529 | return freeze_super(sb); |
534 | if (IS_ERR(sb)) | ||
535 | return PTR_ERR(sb); | ||
536 | return 0; | ||
537 | } | 530 | } |
538 | 531 | ||
539 | static int ioctl_fsthaw(struct file *filp) | 532 | static int ioctl_fsthaw(struct file *filp) |
@@ -543,12 +536,8 @@ static int ioctl_fsthaw(struct file *filp) | |||
543 | if (!capable(CAP_SYS_ADMIN)) | 536 | if (!capable(CAP_SYS_ADMIN)) |
544 | return -EPERM; | 537 | return -EPERM; |
545 | 538 | ||
546 | /* If a blockdevice-backed filesystem isn't specified, return EINVAL. */ | ||
547 | if (sb->s_bdev == NULL) | ||
548 | return -EINVAL; | ||
549 | |||
550 | /* Thaw */ | 539 | /* Thaw */ |
551 | return thaw_bdev(sb->s_bdev, sb); | 540 | return thaw_super(sb); |
552 | } | 541 | } |
553 | 542 | ||
554 | /* | 543 | /* |
diff --git a/fs/super.c b/fs/super.c index c248ac6a1a21..89afca5055ab 100644 --- a/fs/super.c +++ b/fs/super.c | |||
@@ -944,6 +944,96 @@ out: | |||
944 | 944 | ||
945 | EXPORT_SYMBOL_GPL(vfs_kern_mount); | 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 | static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype) | 1037 | static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype) |
948 | { | 1038 | { |
949 | int err; | 1039 | int err; |