diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 63600dc2ac4c..d60b6caf09e8 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -747,6 +747,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, | |||
747 | struct btrfs_pending_snapshot *pending_snapshot; | 747 | struct btrfs_pending_snapshot *pending_snapshot; |
748 | struct btrfs_trans_handle *trans; | 748 | struct btrfs_trans_handle *trans; |
749 | int ret; | 749 | int ret; |
750 | bool snapshot_force_cow = false; | ||
750 | 751 | ||
751 | if (!test_bit(BTRFS_ROOT_REF_COWS, &root->state)) | 752 | if (!test_bit(BTRFS_ROOT_REF_COWS, &root->state)) |
752 | return -EINVAL; | 753 | return -EINVAL; |
@@ -763,6 +764,11 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, | |||
763 | goto free_pending; | 764 | goto free_pending; |
764 | } | 765 | } |
765 | 766 | ||
767 | /* | ||
768 | * Force new buffered writes to reserve space even when NOCOW is | ||
769 | * possible. This is to avoid later writeback (running dealloc) to | ||
770 | * fallback to COW mode and unexpectedly fail with ENOSPC. | ||
771 | */ | ||
766 | atomic_inc(&root->will_be_snapshotted); | 772 | atomic_inc(&root->will_be_snapshotted); |
767 | smp_mb__after_atomic(); | 773 | smp_mb__after_atomic(); |
768 | /* wait for no snapshot writes */ | 774 | /* wait for no snapshot writes */ |
@@ -773,6 +779,14 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, | |||
773 | if (ret) | 779 | if (ret) |
774 | goto dec_and_free; | 780 | goto dec_and_free; |
775 | 781 | ||
782 | /* | ||
783 | * All previous writes have started writeback in NOCOW mode, so now | ||
784 | * we force future writes to fallback to COW mode during snapshot | ||
785 | * creation. | ||
786 | */ | ||
787 | atomic_inc(&root->snapshot_force_cow); | ||
788 | snapshot_force_cow = true; | ||
789 | |||
776 | btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); | 790 | btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); |
777 | 791 | ||
778 | btrfs_init_block_rsv(&pending_snapshot->block_rsv, | 792 | btrfs_init_block_rsv(&pending_snapshot->block_rsv, |
@@ -837,6 +851,8 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, | |||
837 | fail: | 851 | fail: |
838 | btrfs_subvolume_release_metadata(fs_info, &pending_snapshot->block_rsv); | 852 | btrfs_subvolume_release_metadata(fs_info, &pending_snapshot->block_rsv); |
839 | dec_and_free: | 853 | dec_and_free: |
854 | if (snapshot_force_cow) | ||
855 | atomic_dec(&root->snapshot_force_cow); | ||
840 | if (atomic_dec_and_test(&root->will_be_snapshotted)) | 856 | if (atomic_dec_and_test(&root->will_be_snapshotted)) |
841 | wake_up_var(&root->will_be_snapshotted); | 857 | wake_up_var(&root->will_be_snapshotted); |
842 | free_pending: | 858 | free_pending: |
@@ -3453,6 +3469,25 @@ static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen, | |||
3453 | 3469 | ||
3454 | same_lock_start = min_t(u64, loff, dst_loff); | 3470 | same_lock_start = min_t(u64, loff, dst_loff); |
3455 | same_lock_len = max_t(u64, loff, dst_loff) + len - same_lock_start; | 3471 | same_lock_len = max_t(u64, loff, dst_loff) + len - same_lock_start; |
3472 | } else { | ||
3473 | /* | ||
3474 | * If the source and destination inodes are different, the | ||
3475 | * source's range end offset matches the source's i_size, that | ||
3476 | * i_size is not a multiple of the sector size, and the | ||
3477 | * destination range does not go past the destination's i_size, | ||
3478 | * we must round down the length to the nearest sector size | ||
3479 | * multiple. If we don't do this adjustment we end replacing | ||
3480 | * with zeroes the bytes in the range that starts at the | ||
3481 | * deduplication range's end offset and ends at the next sector | ||
3482 | * size multiple. | ||
3483 | */ | ||
3484 | if (loff + olen == i_size_read(src) && | ||
3485 | dst_loff + len < i_size_read(dst)) { | ||
3486 | const u64 sz = BTRFS_I(src)->root->fs_info->sectorsize; | ||
3487 | |||
3488 | len = round_down(i_size_read(src), sz) - loff; | ||
3489 | olen = len; | ||
3490 | } | ||
3456 | } | 3491 | } |
3457 | 3492 | ||
3458 | again: | 3493 | again: |