Skip to content

Commit 5c0b299

Browse files
committed
btrfs-progs: check/lowmem: fix a false alert when counting the refs
[BUG] For a btrfs which is under metadata balance and interrupted (powerloss etc), we can have the following extent backref item for a data extent: item 22 key (13631488 EXTENT_ITEM 524288) itemoff 3161 itemsize 108 refs 180 gen 9 flags DATA (178 0xdfb591fbbf5f519) extent data backref root FS_TREE objectid 258 offset 0 count 77 (178 0xdfb591fa80d95ea) extent data backref root FS_TREE objectid 257 offset 0 count 1 (184 0x151e000) shared data backref parent 22142976 count 51 (184 0x512000) shared data backref parent 5316608 count 51 Then lowmem mode will cause the following false alert: [3/8] checking extents ERROR: extent[13631488, 524288] referencer count mismatch (root: 5, owner: 258, offset: 0) wanted: 77, have: 128 ERROR: errors found in extent allocation tree or chunk allocation [CAUSE] When shared and keyed backref items are found, we must follow the following rules to avoid incorrect refs count: - If the leaf belongs to the shared backref parent Then any found EXTENT_DATA inside the leaf will be contributed to the shared backref values. - Otherwise any found EXTENT_DATA can contributed to the keyed backref In above case, if our leaf is 5316608 or 22142976, then we should not contribute the number of found EXTENT_DATA to the keyed backref. Unfortunately the original fix d53d42f ("btrfs-progs: lowmem: fix false alerts of referencer count mismatch for blocks relocated") is not following the above strict rule, but relying on the flag of the leaf. However that RELOC flag is not a correct indicator, e.g in above case the leaf at 5316608 is not yet being relocated, thus no RELOC flag. [FIX] Instead of check the unreliable RELOC flag, follow the correct rule when checking the leaf. Before we start checking the content of a leaf for EXTENT_DATA items, make sure the leaf's bytenr is not in any shared backref item. If so skip to the next leaf. Fixes: d53d42f ("btrfs-progs: lowmem: fix false alerts of referencer count mismatch for blocks relocated") Signed-off-by: Qu Wenruo <[email protected]>
1 parent 2c8f5c7 commit 5c0b299

File tree

1 file changed

+139
-6
lines changed

1 file changed

+139
-6
lines changed

check/mode-lowmem.c

Lines changed: 139 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3977,6 +3977,139 @@ static int check_shared_block_backref(u64 parent, u64 bytenr, int level)
39773977
return 0;
39783978
}
39793979

3980+
/*
3981+
* A read-only version of lookup_inline_extent_backref().
3982+
* We can not reuse that function as it always assume COW.
3983+
*/
3984+
static int has_inline_shared_backref(u64 data_bytenr, u64 data_len, u64 parent)
3985+
{
3986+
struct btrfs_root *extent_root = btrfs_extent_root(gfs_info, data_bytenr);
3987+
struct btrfs_extent_inline_ref *iref;
3988+
struct btrfs_extent_item *ei;
3989+
struct btrfs_path path = { 0 };
3990+
struct extent_buffer *leaf;
3991+
struct btrfs_key key;
3992+
unsigned long ptr;
3993+
unsigned long end;
3994+
bool found = false;
3995+
u32 item_size;
3996+
u64 flags;
3997+
int ret;
3998+
3999+
key.objectid = data_bytenr;
4000+
key.type = BTRFS_EXTENT_ITEM_KEY;
4001+
key.offset = data_len;
4002+
ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
4003+
if (ret > 0)
4004+
ret = -ENOENT;
4005+
if (ret < 0)
4006+
goto out;
4007+
4008+
leaf = path.nodes[0];
4009+
item_size = btrfs_item_size(leaf, path.slots[0]);
4010+
if (item_size < sizeof(*ei)) {
4011+
error("extent item size %u < %zu, leaf %llu slot %u",
4012+
item_size, sizeof(*ei), leaf->start, path.slots[0]);
4013+
ret = -EUCLEAN;
4014+
goto out;
4015+
}
4016+
ei = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_extent_item);
4017+
flags = btrfs_extent_flags(leaf, ei);
4018+
4019+
if (!(flags & BTRFS_EXTENT_FLAG_DATA)) {
4020+
error("backref item flag for bytenr %llu is not data",
4021+
data_bytenr);
4022+
ret = -EUCLEAN;
4023+
goto out;
4024+
}
4025+
4026+
ptr = (unsigned long)(ei + 1);
4027+
end = (unsigned long)ei + item_size;
4028+
4029+
while (true) {
4030+
u64 ref_parent;
4031+
u8 type;
4032+
4033+
if (ptr >= end) {
4034+
if (ptr > end) {
4035+
error("inline extent item for %llu is not properly ended",
4036+
data_bytenr);
4037+
ret = -EUCLEAN;
4038+
goto out;
4039+
}
4040+
break;
4041+
}
4042+
iref = (struct btrfs_extent_inline_ref *)ptr;
4043+
type = btrfs_extent_inline_ref_type(leaf, iref);
4044+
if (type != BTRFS_SHARED_DATA_REF_KEY)
4045+
goto next;
4046+
4047+
ref_parent = btrfs_extent_inline_ref_offset(leaf, iref);
4048+
if (ref_parent == parent) {
4049+
found = true;
4050+
goto out;
4051+
}
4052+
next:
4053+
ptr += btrfs_extent_inline_ref_size(type);
4054+
}
4055+
4056+
out:
4057+
btrfs_release_path(&path);
4058+
if (ret < 0)
4059+
return ret;
4060+
return found;
4061+
}
4062+
4063+
static int has_keyed_shared_backref(u64 data_bytenr, u64 parent)
4064+
{
4065+
struct btrfs_root *extent_root = btrfs_extent_root(gfs_info, data_bytenr);
4066+
struct btrfs_path path = { 0 };
4067+
struct btrfs_key key;
4068+
int ret;
4069+
4070+
key.objectid = data_bytenr;
4071+
key.type = BTRFS_SHARED_DATA_REF_KEY;
4072+
key.offset = parent;
4073+
ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
4074+
btrfs_release_path(&path);
4075+
if (ret < 0)
4076+
return ret;
4077+
/* No keyed ref found, return 0. */
4078+
if (ret > 0)
4079+
return 0;
4080+
return 1;
4081+
}
4082+
4083+
/*
4084+
* A helper to determine if the @leaf already belongs to a shared data backref item.
4085+
* (with parent bytenr)
4086+
*
4087+
* Return >0 if the @leaf belongs to a shared data backref.
4088+
* Return 0 if not.
4089+
* Return <0 for critical error.
4090+
*/
4091+
static int is_leaf_shared(struct extent_buffer *leaf, u64 data_bytenr, u64 data_len)
4092+
{
4093+
int ret;
4094+
4095+
ret = has_inline_shared_backref(data_bytenr, data_len, leaf->start);
4096+
if (ret < 0) {
4097+
errno = -ret;
4098+
error("failed to search inlined shared backref for logical %llu len %llu, %m",
4099+
data_bytenr, data_len);
4100+
return ret;
4101+
}
4102+
if (ret > 0)
4103+
return ret;
4104+
ret = has_keyed_shared_backref(data_bytenr, leaf->start);
4105+
if (ret < 0) {
4106+
errno = -ret;
4107+
error("failed to search keyed shared backref for logical %llu len %llu, %m",
4108+
data_bytenr, data_len);
4109+
}
4110+
return ret;
4111+
}
4112+
39804113
/*
39814114
* Check referencer for normal (inlined) data ref
39824115
* If len == 0, it will be resolved by searching in extent tree
@@ -4049,13 +4182,13 @@ static int check_extent_data_backref(u64 root_id, u64 objectid, u64 offset,
40494182
btrfs_header_owner(leaf) != root_id)
40504183
goto next;
40514184
/*
4052-
* For tree blocks have been relocated, data backref are
4053-
* shared instead of keyed. Do not account it.
4185+
* If the node belongs to a shared backref item, we should not
4186+
* account the number.
40544187
*/
4055-
if (btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) {
4056-
/*
4057-
* skip the leaf to speed up.
4058-
*/
4188+
ret = is_leaf_shared(leaf, bytenr, len);
4189+
if (ret < 0)
4190+
break;
4191+
if (ret > 0) {
40594192
slot = btrfs_header_nritems(leaf);
40604193
goto next;
40614194
}

0 commit comments

Comments
 (0)