diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 49 |
1 files changed, 43 insertions, 6 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index fab9443f6a42..9c8e1734429c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -3221,6 +3221,26 @@ static void btrfs_double_inode_lock(struct inode *inode1, struct inode *inode2) | |||
3221 | inode_lock_nested(inode2, I_MUTEX_CHILD); | 3221 | inode_lock_nested(inode2, I_MUTEX_CHILD); |
3222 | } | 3222 | } |
3223 | 3223 | ||
3224 | static void btrfs_double_extent_unlock(struct inode *inode1, u64 loff1, | ||
3225 | struct inode *inode2, u64 loff2, u64 len) | ||
3226 | { | ||
3227 | unlock_extent(&BTRFS_I(inode1)->io_tree, loff1, loff1 + len - 1); | ||
3228 | unlock_extent(&BTRFS_I(inode2)->io_tree, loff2, loff2 + len - 1); | ||
3229 | } | ||
3230 | |||
3231 | static void btrfs_double_extent_lock(struct inode *inode1, u64 loff1, | ||
3232 | struct inode *inode2, u64 loff2, u64 len) | ||
3233 | { | ||
3234 | if (inode1 < inode2) { | ||
3235 | swap(inode1, inode2); | ||
3236 | swap(loff1, loff2); | ||
3237 | } else if (inode1 == inode2 && loff2 < loff1) { | ||
3238 | swap(loff1, loff2); | ||
3239 | } | ||
3240 | lock_extent(&BTRFS_I(inode1)->io_tree, loff1, loff1 + len - 1); | ||
3241 | lock_extent(&BTRFS_I(inode2)->io_tree, loff2, loff2 + len - 1); | ||
3242 | } | ||
3243 | |||
3224 | static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen, | 3244 | static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen, |
3225 | struct inode *dst, u64 dst_loff) | 3245 | struct inode *dst, u64 dst_loff) |
3226 | { | 3246 | { |
@@ -3242,11 +3262,12 @@ static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen, | |||
3242 | return -EINVAL; | 3262 | return -EINVAL; |
3243 | 3263 | ||
3244 | /* | 3264 | /* |
3245 | * Lock destination range to serialize with concurrent readpages(). | 3265 | * Lock destination range to serialize with concurrent readpages() and |
3266 | * source range to serialize with relocation. | ||
3246 | */ | 3267 | */ |
3247 | lock_extent(&BTRFS_I(dst)->io_tree, dst_loff, dst_loff + len - 1); | 3268 | btrfs_double_extent_lock(src, loff, dst, dst_loff, len); |
3248 | ret = btrfs_clone(src, dst, loff, olen, len, dst_loff, 1); | 3269 | ret = btrfs_clone(src, dst, loff, olen, len, dst_loff, 1); |
3249 | unlock_extent(&BTRFS_I(dst)->io_tree, dst_loff, dst_loff + len - 1); | 3270 | btrfs_double_extent_unlock(src, loff, dst, dst_loff, len); |
3250 | 3271 | ||
3251 | return ret; | 3272 | return ret; |
3252 | } | 3273 | } |
@@ -3905,17 +3926,33 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src, | |||
3905 | len = ALIGN(src->i_size, bs) - off; | 3926 | len = ALIGN(src->i_size, bs) - off; |
3906 | 3927 | ||
3907 | if (destoff > inode->i_size) { | 3928 | if (destoff > inode->i_size) { |
3929 | const u64 wb_start = ALIGN_DOWN(inode->i_size, bs); | ||
3930 | |||
3908 | ret = btrfs_cont_expand(inode, inode->i_size, destoff); | 3931 | ret = btrfs_cont_expand(inode, inode->i_size, destoff); |
3909 | if (ret) | 3932 | if (ret) |
3910 | return ret; | 3933 | return ret; |
3934 | /* | ||
3935 | * We may have truncated the last block if the inode's size is | ||
3936 | * not sector size aligned, so we need to wait for writeback to | ||
3937 | * complete before proceeding further, otherwise we can race | ||
3938 | * with cloning and attempt to increment a reference to an | ||
3939 | * extent that no longer exists (writeback completed right after | ||
3940 | * we found the previous extent covering eof and before we | ||
3941 | * attempted to increment its reference count). | ||
3942 | */ | ||
3943 | ret = btrfs_wait_ordered_range(inode, wb_start, | ||
3944 | destoff - wb_start); | ||
3945 | if (ret) | ||
3946 | return ret; | ||
3911 | } | 3947 | } |
3912 | 3948 | ||
3913 | /* | 3949 | /* |
3914 | * Lock destination range to serialize with concurrent readpages(). | 3950 | * Lock destination range to serialize with concurrent readpages() and |
3951 | * source range to serialize with relocation. | ||
3915 | */ | 3952 | */ |
3916 | lock_extent(&BTRFS_I(inode)->io_tree, destoff, destoff + len - 1); | 3953 | btrfs_double_extent_lock(src, off, inode, destoff, len); |
3917 | ret = btrfs_clone(src, inode, off, olen, len, destoff, 0); | 3954 | ret = btrfs_clone(src, inode, off, olen, len, destoff, 0); |
3918 | unlock_extent(&BTRFS_I(inode)->io_tree, destoff, destoff + len - 1); | 3955 | btrfs_double_extent_unlock(src, off, inode, destoff, len); |
3919 | /* | 3956 | /* |
3920 | * Truncate page cache pages so that future reads will see the cloned | 3957 | * Truncate page cache pages so that future reads will see the cloned |
3921 | * data immediately and not the previous data. | 3958 | * data immediately and not the previous data. |