aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/ioctl.c36
1 files changed, 31 insertions, 5 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index fba7a004e7e5..362720a3fea2 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3410,15 +3410,41 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
3410 goto out_unlock; 3410 goto out_unlock;
3411 } 3411 }
3412 3412
3413 /* truncate page cache pages from target inode range */ 3413 /*
3414 truncate_inode_pages_range(&inode->i_data, destoff, 3414 * Lock the target range too. Right after we replace the file extent
3415 PAGE_CACHE_ALIGN(destoff + len) - 1); 3415 * items in the fs tree (which now point to the cloned data), we might
3416 * have a worker replace them with extent items relative to a write
3417 * operation that was issued before this clone operation (i.e. confront
3418 * with inode.c:btrfs_finish_ordered_io).
3419 */
3420 if (same_inode) {
3421 u64 lock_start = min_t(u64, off, destoff);
3422 u64 lock_len = max_t(u64, off, destoff) + len - lock_start;
3416 3423
3417 lock_extent_range(src, off, len); 3424 lock_extent_range(src, lock_start, lock_len);
3425 } else {
3426 lock_extent_range(src, off, len);
3427 lock_extent_range(inode, destoff, len);
3428 }
3418 3429
3419 ret = btrfs_clone(src, inode, off, olen, len, destoff); 3430 ret = btrfs_clone(src, inode, off, olen, len, destoff);
3420 3431
3421 unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1); 3432 if (same_inode) {
3433 u64 lock_start = min_t(u64, off, destoff);
3434 u64 lock_end = max_t(u64, off, destoff) + len - 1;
3435
3436 unlock_extent(&BTRFS_I(src)->io_tree, lock_start, lock_end);
3437 } else {
3438 unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1);
3439 unlock_extent(&BTRFS_I(inode)->io_tree, destoff,
3440 destoff + len - 1);
3441 }
3442 /*
3443 * Truncate page cache pages so that future reads will see the cloned
3444 * data immediately and not the previous data.
3445 */
3446 truncate_inode_pages_range(&inode->i_data, destoff,
3447 PAGE_CACHE_ALIGN(destoff + len) - 1);
3422out_unlock: 3448out_unlock:
3423 if (!same_inode) { 3449 if (!same_inode) {
3424 if (inode < src) { 3450 if (inode < src) {