diff options
-rw-r--r-- | fs/btrfs/ioctl.c | 36 |
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); | ||
3422 | out_unlock: | 3448 | out_unlock: |
3423 | if (!same_inode) { | 3449 | if (!same_inode) { |
3424 | if (inode < src) { | 3450 | if (inode < src) { |