Message ID | 366f92a7ec5a69dc92290dc2cf6e8603f566495c.1630514529.git.osandov@fb.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | btrfs: add ioctls and send/receive support for reading/writing compressed data | expand |
On 1.09.21 г. 20:01, Omar Sandoval wrote: > From: Omar Sandoval <osandov@fb.com> > > Now that all of the pieces are in place, we can use the ENCODED_WRITE > command to send compressed extents when appropriate. > > Signed-off-by: Omar Sandoval <osandov@fb.com> Overall looks sane but consider some of the nits below. <snip> > +static int send_encoded_extent(struct send_ctx *sctx, struct btrfs_path *path, > + u64 offset, u64 len) > +{ > + struct btrfs_root *root = sctx->send_root; > + struct btrfs_fs_info *fs_info = root->fs_info; > + struct inode *inode; > + struct fs_path *p; > + struct extent_buffer *leaf = path->nodes[0]; > + struct btrfs_key key; > + struct btrfs_file_extent_item *ei; > + u64 block_start; > + u64 block_len; > + u32 data_offset; > + struct btrfs_cmd_header *hdr; > + u32 crc; > + int ret; > + > + inode = btrfs_iget(fs_info->sb, sctx->cur_ino, root); > + if (IS_ERR(inode)) > + return PTR_ERR(inode); > + > + p = fs_path_alloc(); > + if (!p) { > + ret = -ENOMEM; > + goto out; > + } > + > + ret = begin_cmd(sctx, BTRFS_SEND_C_ENCODED_WRITE); > + if (ret < 0) > + goto out; > + > + ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p); > + if (ret < 0) > + goto out; > + > + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); > + ei = btrfs_item_ptr(leaf, path->slots[0], > + struct btrfs_file_extent_item); > + block_start = btrfs_file_extent_disk_bytenr(leaf, ei); block_start is somewhat ambiguous here, this is just the disk bytenr of the extent. > + block_len = btrfs_file_extent_disk_num_bytes(leaf, ei); Why is this called block_len when it's just the size in bytes on-disk? > + > + TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p); > + TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset); > + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_FILE_LEN, > + min(key.offset + btrfs_file_extent_num_bytes(leaf, ei) - offset, > + len)); > + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_LEN, > + btrfs_file_extent_ram_bytes(leaf, ei)); > + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_OFFSET, > + offset - key.offset + btrfs_file_extent_offset(leaf, ei)); > + ret = btrfs_encoded_io_compression_from_extent( > + btrfs_file_extent_compression(leaf, ei)); > + if (ret < 0) > + goto out; > + TLV_PUT_U32(sctx, BTRFS_SEND_A_COMPRESSION, ret); > + TLV_PUT_U32(sctx, BTRFS_SEND_A_ENCRYPTION, 0); > + > + ret = put_data_header(sctx, block_len); > + if (ret < 0) > + goto out; > + > + data_offset = ALIGN(sctx->send_size, PAGE_SIZE); nit: The whole data_offset warrants a comment here, since send_buf is now mapped from send_buf_pages, so all the TLV you've put before are actually stored in the beginning of send_buf_pages, so by doing the above you ensure the data write begins on a clean page boundary ... > + if (data_offset > sctx->send_max_size || > + sctx->send_max_size - data_offset < block_len) { > + ret = -EOVERFLOW; > + goto out; > + } > + > + ret = btrfs_encoded_read_regular_fill_pages(inode, block_start, > + block_len, > + sctx->send_buf_pages + > + (data_offset >> PAGE_SHIFT)); > + if (ret) > + goto out; > + > + hdr = (struct btrfs_cmd_header *)sctx->send_buf; > + hdr->len = cpu_to_le32(sctx->send_size + block_len - sizeof(*hdr)); > + hdr->crc = 0; > + crc = btrfs_crc32c(0, sctx->send_buf, sctx->send_size); > + crc = btrfs_crc32c(crc, sctx->send_buf + data_offset, block_len); ... and because of that you can't simply use send_cmd ;( > + hdr->crc = cpu_to_le32(crc); > + > + ret = write_buf(sctx->send_filp, sctx->send_buf, sctx->send_size, > + &sctx->send_off); > + if (!ret) { > + ret = write_buf(sctx->send_filp, sctx->send_buf + data_offset, > + block_len, &sctx->send_off); > + } > + sctx->total_send_size += sctx->send_size + block_len; > + sctx->cmd_send_size[le16_to_cpu(hdr->cmd)] += > + sctx->send_size + block_len; > + sctx->send_size = 0; > + > +tlv_put_failure: > +out: > + fs_path_free(p); > + iput(inode); > + return ret; > +} > + > +static int send_extent_data(struct send_ctx *sctx, struct btrfs_path *path, > + const u64 offset, const u64 len) nit: Instead of sending around a btrfs_path struct around and "polluting" callees to deal with the oddities of our btree interface i.e btrfs_item_ptr et al. Why not refactor the code so that when we know we are about to send an extent data simply initialize some struct extent_info with all the necessary data items: extent type, compression type, based on the extent type properly initialize a size attribute etc and pass that. Right now you have send_extent_data fiddling with path->nodes[0], then based on that you either call send_encoded_inline_extent or send_encoded_extent, instead pass extent_info to send_extent_data/clone_range and be done with it. <snip>
On Mon, Oct 18, 2021 at 02:59:08PM +0300, Nikolay Borisov wrote: > > > On 1.09.21 г. 20:01, Omar Sandoval wrote: > > From: Omar Sandoval <osandov@fb.com> > > > > Now that all of the pieces are in place, we can use the ENCODED_WRITE > > command to send compressed extents when appropriate. > > > > Signed-off-by: Omar Sandoval <osandov@fb.com> > > Overall looks sane but consider some of the nits below. > > > <snip> > > > +static int send_encoded_extent(struct send_ctx *sctx, struct btrfs_path *path, > > + u64 offset, u64 len) > > +{ > > + struct btrfs_root *root = sctx->send_root; > > + struct btrfs_fs_info *fs_info = root->fs_info; > > + struct inode *inode; > > + struct fs_path *p; > > + struct extent_buffer *leaf = path->nodes[0]; > > + struct btrfs_key key; > > + struct btrfs_file_extent_item *ei; > > + u64 block_start; > > + u64 block_len; > > + u32 data_offset; > > + struct btrfs_cmd_header *hdr; > > + u32 crc; > > + int ret; > > + > > + inode = btrfs_iget(fs_info->sb, sctx->cur_ino, root); > > + if (IS_ERR(inode)) > > + return PTR_ERR(inode); > > + > > + p = fs_path_alloc(); > > + if (!p) { > > + ret = -ENOMEM; > > + goto out; > > + } > > + > > + ret = begin_cmd(sctx, BTRFS_SEND_C_ENCODED_WRITE); > > + if (ret < 0) > > + goto out; > > + > > + ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p); > > + if (ret < 0) > > + goto out; > > + > > + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); > > + ei = btrfs_item_ptr(leaf, path->slots[0], > > + struct btrfs_file_extent_item); > > + block_start = btrfs_file_extent_disk_bytenr(leaf, ei); > > block_start is somewhat ambiguous here, this is just the disk bytenr of > the extent. > > > + block_len = btrfs_file_extent_disk_num_bytes(leaf, ei); > > Why is this called block_len when it's just the size in bytes on-disk? I copied this naming from extent_map since btrfs_encoded_read() was the reference for this code, but I'll change the naming here. > > + > > + TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p); > > + TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset); > > + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_FILE_LEN, > > + min(key.offset + btrfs_file_extent_num_bytes(leaf, ei) - offset, > > + len)); > > + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_LEN, > > + btrfs_file_extent_ram_bytes(leaf, ei)); > > + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_OFFSET, > > + offset - key.offset + btrfs_file_extent_offset(leaf, ei)); > > + ret = btrfs_encoded_io_compression_from_extent( > > + btrfs_file_extent_compression(leaf, ei)); > > + if (ret < 0) > > + goto out; > > + TLV_PUT_U32(sctx, BTRFS_SEND_A_COMPRESSION, ret); > > + TLV_PUT_U32(sctx, BTRFS_SEND_A_ENCRYPTION, 0); > > + > > + ret = put_data_header(sctx, block_len); > > + if (ret < 0) > > + goto out; > > + > > + data_offset = ALIGN(sctx->send_size, PAGE_SIZE); > > nit: The whole data_offset warrants a comment here, since send_buf is > now mapped from send_buf_pages, so all the TLV you've put before are > actually stored in the beginning of send_buf_pages, so by doing the > above you ensure the data write begins on a clean page boundary ... Yup, I'll add a comment. > > + if (data_offset > sctx->send_max_size || > > + sctx->send_max_size - data_offset < block_len) { > > + ret = -EOVERFLOW; > > + goto out; > > + } > > + > > + ret = btrfs_encoded_read_regular_fill_pages(inode, block_start, > > + block_len, > > + sctx->send_buf_pages + > > + (data_offset >> PAGE_SHIFT)); > > + if (ret) > > + goto out; > > + > > + hdr = (struct btrfs_cmd_header *)sctx->send_buf; > > + hdr->len = cpu_to_le32(sctx->send_size + block_len - sizeof(*hdr)); > > + hdr->crc = 0; > > + crc = btrfs_crc32c(0, sctx->send_buf, sctx->send_size); > > + crc = btrfs_crc32c(crc, sctx->send_buf + data_offset, block_len); > > ... and because of that you can't simply use send_cmd ;( > > > + hdr->crc = cpu_to_le32(crc); > > + > > + ret = write_buf(sctx->send_filp, sctx->send_buf, sctx->send_size, > > + &sctx->send_off); > > + if (!ret) { > > + ret = write_buf(sctx->send_filp, sctx->send_buf + data_offset, > > + block_len, &sctx->send_off); > > + } > > + sctx->total_send_size += sctx->send_size + block_len; > > + sctx->cmd_send_size[le16_to_cpu(hdr->cmd)] += > > + sctx->send_size + block_len; > > + sctx->send_size = 0; > > + > > +tlv_put_failure: > > +out: > > + fs_path_free(p); > > + iput(inode); > > + return ret; > > +} > > + > > +static int send_extent_data(struct send_ctx *sctx, struct btrfs_path *path, > > + const u64 offset, const u64 len) > > nit: Instead of sending around a btrfs_path struct around and > "polluting" callees to deal with the oddities of our btree interface i.e > btrfs_item_ptr et al. Why not refactor the code so that when we know we > are about to send an extent data simply initialize some struct > extent_info with all the necessary data items: extent type, compression > type, based on the extent type properly initialize a size attribute etc > and pass that. Right now you have send_extent_data fiddling with > path->nodes[0], then based on that you either call > send_encoded_inline_extent or send_encoded_extent, instead pass > extent_info to send_extent_data/clone_range and be done with it. I don't like this for a few reasons: * An extra "struct extent_info" layer of abstraction would just be extra cognitive overhead. I hate having to trace back where the fields in some struct came from when it's information that's readily available in more well-known data structures. * send_encoded_inline_extent() (called by send_extent_data()) needs the btrfs_path in order to get the inline data anyways. * clone_range() also already deals with btrfs_paths, so it's not new.
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 7753976bc3c7..caec0422a5c4 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3223,6 +3223,10 @@ int btrfs_writepage_cow_fixup(struct page *page); void btrfs_writepage_endio_finish_ordered(struct btrfs_inode *inode, struct page *page, u64 start, u64 end, bool uptodate); +int btrfs_encoded_io_compression_from_extent(int compress_type); +int btrfs_encoded_read_regular_fill_pages(struct inode *inode, u64 offset, + u64 disk_io_size, + struct page **pages); struct btrfs_ioctl_encoded_io_args; ssize_t btrfs_encoded_read(struct kiocb *iocb, struct iov_iter *iter, struct btrfs_ioctl_encoded_io_args *encoded); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index e751af96c198..6c40419eb2b1 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10502,7 +10502,7 @@ void btrfs_set_range_writeback(struct btrfs_inode *inode, u64 start, u64 end) } } -static int btrfs_encoded_io_compression_from_extent(int compress_type) +int btrfs_encoded_io_compression_from_extent(int compress_type) { switch (compress_type) { case BTRFS_COMPRESS_NONE: @@ -10705,8 +10705,8 @@ static void btrfs_encoded_read_endio(struct bio *bio) bio_put(bio); } -static int btrfs_encoded_read_regular_fill_pages(struct inode *inode, u64 offset, - u64 disk_io_size, struct page **pages) +int btrfs_encoded_read_regular_fill_pages(struct inode *inode, u64 offset, + u64 disk_io_size, struct page **pages) { struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_encoded_read_private priv = { diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index a000efe2658a..0ba8dc3a9f56 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -595,6 +595,7 @@ static int tlv_put(struct send_ctx *sctx, u16 attr, const void *data, int len) return tlv_put(sctx, attr, &__tmp, sizeof(__tmp)); \ } +TLV_PUT_DEFINE_INT(32) TLV_PUT_DEFINE_INT(64) static int tlv_put_string(struct send_ctx *sctx, u16 attr, @@ -5203,16 +5204,211 @@ static int send_hole(struct send_ctx *sctx, u64 end) return ret; } -static int send_extent_data(struct send_ctx *sctx, - const u64 offset, - const u64 len) +static int send_encoded_inline_extent(struct send_ctx *sctx, + struct btrfs_path *path, u64 offset, + u64 len) { + struct btrfs_root *root = sctx->send_root; + struct btrfs_fs_info *fs_info = root->fs_info; + struct inode *inode; + struct fs_path *p; + struct extent_buffer *leaf = path->nodes[0]; + struct btrfs_key key; + struct btrfs_file_extent_item *ei; + u64 ram_bytes; + size_t inline_size; + int ret; + + inode = btrfs_iget(fs_info->sb, sctx->cur_ino, root); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + p = fs_path_alloc(); + if (!p) { + ret = -ENOMEM; + goto out; + } + + ret = begin_cmd(sctx, BTRFS_SEND_C_ENCODED_WRITE); + if (ret < 0) + goto out; + + ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p); + if (ret < 0) + goto out; + + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + ei = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_file_extent_item); + ram_bytes = btrfs_file_extent_ram_bytes(leaf, ei); + inline_size = btrfs_file_extent_inline_item_len(leaf, + btrfs_item_nr(path->slots[0])); + + TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p); + TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset); + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_FILE_LEN, + min(key.offset + ram_bytes - offset, len)); + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_LEN, ram_bytes); + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_OFFSET, offset - key.offset); + ret = btrfs_encoded_io_compression_from_extent( + btrfs_file_extent_compression(leaf, ei)); + if (ret < 0) + goto out; + TLV_PUT_U32(sctx, BTRFS_SEND_A_COMPRESSION, ret); + TLV_PUT_U32(sctx, BTRFS_SEND_A_ENCRYPTION, 0); + + ret = put_data_header(sctx, inline_size); + if (ret < 0) + goto out; + read_extent_buffer(leaf, sctx->send_buf + sctx->send_size, + btrfs_file_extent_inline_start(ei), inline_size); + sctx->send_size += inline_size; + + ret = send_cmd(sctx); + +tlv_put_failure: +out: + fs_path_free(p); + iput(inode); + return ret; +} + +static int send_encoded_extent(struct send_ctx *sctx, struct btrfs_path *path, + u64 offset, u64 len) +{ + struct btrfs_root *root = sctx->send_root; + struct btrfs_fs_info *fs_info = root->fs_info; + struct inode *inode; + struct fs_path *p; + struct extent_buffer *leaf = path->nodes[0]; + struct btrfs_key key; + struct btrfs_file_extent_item *ei; + u64 block_start; + u64 block_len; + u32 data_offset; + struct btrfs_cmd_header *hdr; + u32 crc; + int ret; + + inode = btrfs_iget(fs_info->sb, sctx->cur_ino, root); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + p = fs_path_alloc(); + if (!p) { + ret = -ENOMEM; + goto out; + } + + ret = begin_cmd(sctx, BTRFS_SEND_C_ENCODED_WRITE); + if (ret < 0) + goto out; + + ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p); + if (ret < 0) + goto out; + + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + ei = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_file_extent_item); + block_start = btrfs_file_extent_disk_bytenr(leaf, ei); + block_len = btrfs_file_extent_disk_num_bytes(leaf, ei); + + TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p); + TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset); + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_FILE_LEN, + min(key.offset + btrfs_file_extent_num_bytes(leaf, ei) - offset, + len)); + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_LEN, + btrfs_file_extent_ram_bytes(leaf, ei)); + TLV_PUT_U64(sctx, BTRFS_SEND_A_UNENCODED_OFFSET, + offset - key.offset + btrfs_file_extent_offset(leaf, ei)); + ret = btrfs_encoded_io_compression_from_extent( + btrfs_file_extent_compression(leaf, ei)); + if (ret < 0) + goto out; + TLV_PUT_U32(sctx, BTRFS_SEND_A_COMPRESSION, ret); + TLV_PUT_U32(sctx, BTRFS_SEND_A_ENCRYPTION, 0); + + ret = put_data_header(sctx, block_len); + if (ret < 0) + goto out; + + data_offset = ALIGN(sctx->send_size, PAGE_SIZE); + if (data_offset > sctx->send_max_size || + sctx->send_max_size - data_offset < block_len) { + ret = -EOVERFLOW; + goto out; + } + + ret = btrfs_encoded_read_regular_fill_pages(inode, block_start, + block_len, + sctx->send_buf_pages + + (data_offset >> PAGE_SHIFT)); + if (ret) + goto out; + + hdr = (struct btrfs_cmd_header *)sctx->send_buf; + hdr->len = cpu_to_le32(sctx->send_size + block_len - sizeof(*hdr)); + hdr->crc = 0; + crc = btrfs_crc32c(0, sctx->send_buf, sctx->send_size); + crc = btrfs_crc32c(crc, sctx->send_buf + data_offset, block_len); + hdr->crc = cpu_to_le32(crc); + + ret = write_buf(sctx->send_filp, sctx->send_buf, sctx->send_size, + &sctx->send_off); + if (!ret) { + ret = write_buf(sctx->send_filp, sctx->send_buf + data_offset, + block_len, &sctx->send_off); + } + sctx->total_send_size += sctx->send_size + block_len; + sctx->cmd_send_size[le16_to_cpu(hdr->cmd)] += + sctx->send_size + block_len; + sctx->send_size = 0; + +tlv_put_failure: +out: + fs_path_free(p); + iput(inode); + return ret; +} + +static int send_extent_data(struct send_ctx *sctx, struct btrfs_path *path, + const u64 offset, const u64 len) +{ + struct extent_buffer *leaf = path->nodes[0]; + struct btrfs_file_extent_item *ei; u64 read_size = max_send_read_size(sctx); u64 sent = 0; if (sctx->flags & BTRFS_SEND_FLAG_NO_FILE_DATA) return send_update_extent(sctx, offset, len); + ei = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_file_extent_item); + if ((sctx->flags & BTRFS_SEND_FLAG_COMPRESSED) && + btrfs_file_extent_compression(leaf, ei) != BTRFS_COMPRESS_NONE) { + bool is_inline = (btrfs_file_extent_type(leaf, ei) == + BTRFS_FILE_EXTENT_INLINE); + + /* + * Send the compressed extent unless the compressed data is + * larger than the decompressed data. This can happen if we're + * not sending the entire extent, either because it has been + * partially overwritten/truncated or because this is a part of + * the extent that we couldn't clone in clone_range(). + */ + if (is_inline && + btrfs_file_extent_inline_item_len(leaf, + btrfs_item_nr(path->slots[0])) <= len) { + return send_encoded_inline_extent(sctx, path, offset, + len); + } else if (!is_inline && + btrfs_file_extent_disk_num_bytes(leaf, ei) <= len) { + return send_encoded_extent(sctx, path, offset, len); + } + } + while (sent < len) { u64 size = min(len - sent, read_size); int ret; @@ -5283,12 +5479,9 @@ static int send_capabilities(struct send_ctx *sctx) return ret; } -static int clone_range(struct send_ctx *sctx, - struct clone_root *clone_root, - const u64 disk_byte, - u64 data_offset, - u64 offset, - u64 len) +static int clone_range(struct send_ctx *sctx, struct btrfs_path *dst_path, + struct clone_root *clone_root, const u64 disk_byte, + u64 data_offset, u64 offset, u64 len) { struct btrfs_path *path; struct btrfs_key key; @@ -5312,7 +5505,7 @@ static int clone_range(struct send_ctx *sctx, */ if (clone_root->offset == 0 && len == sctx->send_root->fs_info->sectorsize) - return send_extent_data(sctx, offset, len); + return send_extent_data(sctx, dst_path, offset, len); path = alloc_path_for_send(); if (!path) @@ -5409,7 +5602,8 @@ static int clone_range(struct send_ctx *sctx, if (hole_len > len) hole_len = len; - ret = send_extent_data(sctx, offset, hole_len); + ret = send_extent_data(sctx, dst_path, offset, + hole_len); if (ret < 0) goto out; @@ -5482,14 +5676,16 @@ static int clone_range(struct send_ctx *sctx, if (ret < 0) goto out; } - ret = send_extent_data(sctx, offset + slen, + ret = send_extent_data(sctx, dst_path, + offset + slen, clone_len - slen); } else { ret = send_clone(sctx, offset, clone_len, clone_root); } } else { - ret = send_extent_data(sctx, offset, clone_len); + ret = send_extent_data(sctx, dst_path, offset, + clone_len); } if (ret < 0) @@ -5521,7 +5717,7 @@ static int clone_range(struct send_ctx *sctx, } if (len > 0) - ret = send_extent_data(sctx, offset, len); + ret = send_extent_data(sctx, dst_path, offset, len); else ret = 0; out: @@ -5552,10 +5748,10 @@ static int send_write_or_clone(struct send_ctx *sctx, struct btrfs_file_extent_item); disk_byte = btrfs_file_extent_disk_bytenr(path->nodes[0], ei); data_offset = btrfs_file_extent_offset(path->nodes[0], ei); - ret = clone_range(sctx, clone_root, disk_byte, data_offset, - offset, end - offset); + ret = clone_range(sctx, path, clone_root, disk_byte, + data_offset, offset, end - offset); } else { - ret = send_extent_data(sctx, offset, end - offset); + ret = send_extent_data(sctx, path, offset, end - offset); } sctx->cur_inode_next_write_offset = end; return ret;