Skip to content

Commit 040ee61

Browse files
Robbie Kokdave
authored andcommitted
Btrfs: send, improve clone range
Improve clone_range in two scenarios. 1. Remove the limit of inode size when find clone inodes We can do partial clone, so there is no need to limit the size of the candidate inode. When clone a range, we clone the legal range only by bytenr, offset, len, inode size. 2. In the scenarios of rewrite or clone_range, data_offset rarely matches exactly, so the chance of a clone is missed. e.g. 1. Write a 1M file dd if=/dev/zero of=1M bs=1M count=1 2. Clone 1M file cp --reflink 1M clone 3. Rewrite 4k on the clone file dd if=/dev/zero of=clone bs=4k count=1 conv=notrunc The disk layout is as follows: item 16 key (257 EXTENT_DATA 0) itemoff 15353 itemsize 53 extent data disk byte 1103101952 nr 1048576 extent data offset 0 nr 1048576 ram 1048576 extent compression(none) ... item 22 key (258 EXTENT_DATA 0) itemoff 14959 itemsize 53 extent data disk byte 1104150528 nr 4096 extent data offset 0 nr 4096 ram 4096 extent compression(none) item 23 key (258 EXTENT_DATA 4096) itemoff 14906 itemsize 53 extent data disk byte 1103101952 nr 1048576 extent data offset 4096 nr 1044480 ram 1048576 extent compression(none) When send, inode 258 file offset 4096~1048576 (item 23) has a chance to clone_range, but because data_offset does not match inode 257 (item 16), it causes missed clone and can only transfer actual data. Improve the problem by judging whether the current data_offset has overlap with the file extent item, and if so, adjusting offset and extent_len so that we can clone correctly. Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Robbie Ko <robbieko@synology.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 8b4d1ef commit 040ee61

File tree

1 file changed

+33
-19
lines changed

1 file changed

+33
-19
lines changed

fs/btrfs/send.c

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,6 @@ static int get_inode_path(struct btrfs_root *root,
11601160
struct backref_ctx {
11611161
struct send_ctx *sctx;
11621162

1163-
struct btrfs_path *path;
11641163
/* number of total found references */
11651164
u64 found;
11661165

@@ -1213,8 +1212,6 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
12131212
{
12141213
struct backref_ctx *bctx = ctx_;
12151214
struct clone_root *found;
1216-
int ret;
1217-
u64 i_size;
12181215

12191216
/* First check if the root is in the list of accepted clone sources */
12201217
found = bsearch((void *)(uintptr_t)root, bctx->sctx->clone_roots,
@@ -1230,19 +1227,6 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
12301227
bctx->found_itself = 1;
12311228
}
12321229

1233-
/*
1234-
* There are inodes that have extents that lie behind its i_size. Don't
1235-
* accept clones from these extents.
1236-
*/
1237-
ret = __get_inode_info(found->root, bctx->path, ino, &i_size, NULL, NULL,
1238-
NULL, NULL, NULL);
1239-
btrfs_release_path(bctx->path);
1240-
if (ret < 0)
1241-
return ret;
1242-
1243-
if (offset + bctx->data_offset + bctx->extent_len > i_size)
1244-
return 0;
1245-
12461230
/*
12471231
* Make sure we don't consider clones from send_root that are
12481232
* behind the current inode/offset.
@@ -1319,8 +1303,6 @@ static int find_extent_clone(struct send_ctx *sctx,
13191303
goto out;
13201304
}
13211305

1322-
backref_ctx->path = tmp_path;
1323-
13241306
if (data_offset >= ino_size) {
13251307
/*
13261308
* There may be extents that lie behind the file's size.
@@ -5082,6 +5064,7 @@ static int clone_range(struct send_ctx *sctx,
50825064
struct btrfs_path *path;
50835065
struct btrfs_key key;
50845066
int ret;
5067+
u64 clone_src_i_size;
50855068

50865069
/*
50875070
* Prevent cloning from a zero offset with a length matching the sector
@@ -5106,6 +5089,16 @@ static int clone_range(struct send_ctx *sctx,
51065089
if (!path)
51075090
return -ENOMEM;
51085091

5092+
/*
5093+
* There are inodes that have extents that lie behind its i_size. Don't
5094+
* accept clones from these extents.
5095+
*/
5096+
ret = __get_inode_info(clone_root->root, path, clone_root->ino,
5097+
&clone_src_i_size, NULL, NULL, NULL, NULL, NULL);
5098+
btrfs_release_path(path);
5099+
if (ret < 0)
5100+
goto out;
5101+
51095102
/*
51105103
* We can't send a clone operation for the entire range if we find
51115104
* extent items in the respective range in the source file that
@@ -5148,6 +5141,7 @@ static int clone_range(struct send_ctx *sctx,
51485141
u8 type;
51495142
u64 ext_len;
51505143
u64 clone_len;
5144+
u64 clone_data_offset;
51515145

51525146
if (slot >= btrfs_header_nritems(leaf)) {
51535147
ret = btrfs_next_leaf(clone_root->root, path);
@@ -5201,10 +5195,30 @@ static int clone_range(struct send_ctx *sctx,
52015195
if (key.offset >= clone_root->offset + len)
52025196
break;
52035197

5198+
if (key.offset >= clone_src_i_size)
5199+
break;
5200+
5201+
if (key.offset + ext_len > clone_src_i_size)
5202+
ext_len = clone_src_i_size - key.offset;
5203+
5204+
clone_data_offset = btrfs_file_extent_offset(leaf, ei);
5205+
if (btrfs_file_extent_disk_bytenr(leaf, ei) == disk_byte) {
5206+
clone_root->offset = key.offset;
5207+
if (clone_data_offset < data_offset &&
5208+
clone_data_offset + ext_len > data_offset) {
5209+
u64 extent_offset;
5210+
5211+
extent_offset = data_offset - clone_data_offset;
5212+
ext_len -= extent_offset;
5213+
clone_data_offset += extent_offset;
5214+
clone_root->offset += extent_offset;
5215+
}
5216+
}
5217+
52045218
clone_len = min_t(u64, ext_len, len);
52055219

52065220
if (btrfs_file_extent_disk_bytenr(leaf, ei) == disk_byte &&
5207-
btrfs_file_extent_offset(leaf, ei) == data_offset)
5221+
clone_data_offset == data_offset)
52085222
ret = send_clone(sctx, offset, clone_len, clone_root);
52095223
else
52105224
ret = send_extent_data(sctx, offset, clone_len);

0 commit comments

Comments
 (0)