diff options
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/ioctl.c | 79 |
1 files changed, 40 insertions, 39 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a247991856a0..99eab7b3e5e1 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2914,8 +2914,6 @@ static void btrfs_cmp_data_free(struct cmp_pages *cmp) put_page(pg); } } - kfree(cmp->src_pages); - kfree(cmp->dst_pages); } static int btrfs_cmp_data_prepare(struct inode *src, u64 loff, @@ -2924,40 +2922,14 @@ static int btrfs_cmp_data_prepare(struct inode *src, u64 loff, { int ret; int num_pages = PAGE_ALIGN(len) >> PAGE_SHIFT; - struct page **src_pgarr, **dst_pgarr; - /* - * We must gather up all the pages before we initiate our - * extent locking. We use an array for the page pointers. Size - * of the array is bounded by len, which is in turn bounded by - * BTRFS_MAX_DEDUPE_LEN. - */ - src_pgarr = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); - dst_pgarr = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); - if (!src_pgarr || !dst_pgarr) { - kfree(src_pgarr); - kfree(dst_pgarr); - return -ENOMEM; - } cmp->num_pages = num_pages; - cmp->src_pages = src_pgarr; - cmp->dst_pages = dst_pgarr; - - /* - * If deduping ranges in the same inode, locking rules make it mandatory - * to always lock pages in ascending order to avoid deadlocks with - * concurrent tasks (such as starting writeback/delalloc). - */ - if (src == dst && dst_loff < loff) { - swap(src_pgarr, dst_pgarr); - swap(loff, dst_loff); - } - ret = gather_extent_pages(src, src_pgarr, cmp->num_pages, loff); + ret = gather_extent_pages(src, cmp->src_pages, num_pages, loff); if (ret) goto out; - ret = gather_extent_pages(dst, dst_pgarr, cmp->num_pages, dst_loff); + ret = gather_extent_pages(dst, cmp->dst_pages, num_pages, dst_loff); out: if (ret) @@ -3028,11 +3000,11 @@ static int extent_same_check_offsets(struct inode *inode, u64 off, u64 *plen, } static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen, - struct inode *dst, u64 dst_loff) + struct inode *dst, u64 dst_loff, + struct cmp_pages *cmp) { int ret; u64 len = olen; - struct cmp_pages cmp; bool same_inode = (src == dst); u64 same_lock_start = 0; u64 same_lock_len = 0; @@ -3072,7 +3044,7 @@ static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen, } again: - ret = btrfs_cmp_data_prepare(src, loff, dst, dst_loff, olen, &cmp); + ret = btrfs_cmp_data_prepare(src, loff, dst, dst_loff, olen, cmp); if (ret) return ret; @@ -3095,7 +3067,7 @@ again: * Ranges in the io trees already unlocked. Now unlock all * pages before waiting for all IO to complete. */ - btrfs_cmp_data_free(&cmp); + btrfs_cmp_data_free(cmp); if (same_inode) { btrfs_wait_ordered_range(src, same_lock_start, same_lock_len); @@ -3108,12 +3080,12 @@ again: ASSERT(ret == 0); if (WARN_ON(ret)) { /* ranges in the io trees already unlocked */ - btrfs_cmp_data_free(&cmp); + btrfs_cmp_data_free(cmp); return ret; } /* pass original length for comparison so we stay within i_size */ - ret = btrfs_cmp_data(olen, &cmp); + ret = btrfs_cmp_data(olen, cmp); if (ret == 0) ret = btrfs_clone(src, dst, loff, olen, len, dst_loff, 1); @@ -3123,7 +3095,7 @@ again: else btrfs_double_extent_unlock(src, loff, dst, dst_loff, len); - btrfs_cmp_data_free(&cmp); + btrfs_cmp_data_free(cmp); return ret; } @@ -3134,6 +3106,8 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, struct inode *dst, u64 dst_loff) { int ret; + struct cmp_pages cmp; + int num_pages = PAGE_ALIGN(BTRFS_MAX_DEDUPE_LEN) >> PAGE_SHIFT; bool same_inode = (src == dst); u64 i, tail_len, chunk_count; @@ -3154,10 +3128,33 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, tail_len = olen % BTRFS_MAX_DEDUPE_LEN; chunk_count = div_u64(olen, BTRFS_MAX_DEDUPE_LEN); + if (chunk_count == 0) + num_pages = PAGE_ALIGN(tail_len) >> PAGE_SHIFT; + + /* + * If deduping ranges in the same inode, locking rules make it + * mandatory to always lock pages in ascending order to avoid deadlocks + * with concurrent tasks (such as starting writeback/delalloc). + */ + if (same_inode && dst_loff < loff) + swap(loff, dst_loff); + + /* + * We must gather up all the pages before we initiate our extent + * locking. We use an array for the page pointers. Size of the array is + * bounded by len, which is in turn bounded by BTRFS_MAX_DEDUPE_LEN. + */ + cmp.src_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); + cmp.dst_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); + if (!cmp.src_pages || !cmp.dst_pages) { + kfree(cmp.src_pages); + kfree(cmp.dst_pages); + return -ENOMEM; + } for (i = 0; i < chunk_count; i++) { ret = btrfs_extent_same_range(src, loff, BTRFS_MAX_DEDUPE_LEN, - dst, dst_loff); + dst, dst_loff, &cmp); if (ret) goto out_unlock; @@ -3166,7 +3163,8 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, } if (tail_len > 0) - ret = btrfs_extent_same_range(src, loff, tail_len, dst, dst_loff); + ret = btrfs_extent_same_range(src, loff, tail_len, dst, + dst_loff, &cmp); out_unlock: if (same_inode) @@ -3174,6 +3172,9 @@ out_unlock: else btrfs_double_inode_unlock(src, dst); + kfree(cmp.src_pages); + kfree(cmp.dst_pages); + return ret; } |