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. |
