btrfs-progs: check: enhance the error output for backref mismatch

[PROBLEM]
Btrfs check original mode output is not that reader friendly already, it
even includes pointer output:

  backref 15353727729664 parent 1140559929556992 not referenced back 0xc9133d70
  tree backref 15353727729664 parent 14660022714368 not found in extent tree
  incorrect global backref count on 15353727729664 found 3 wanted 2
  backpointer mismatch on [15353727729664 16384]

In above case, the "0xc9133d70" is completely useless, as it's a pointer
for the tree_backref structure.

And the term "backref" is quite abused in above case.

[ENHANCEMENT]
To enhance the situation, let's use some output format from lowmem mode
instead.

Now above example will be changed to:

  tree extent[15353727729664, 16384] parent 1140559929556992 has no tree block found
  tree extent[15353727729664, 16384] parent 14660022714368 has no backref item in extent tree
  incorrect global backref count on 15353727729664 found 3 wanted 2
  backpointer mismatch on [15353727729664 16384]

And some example for data backrefs:

  data extent[12845056, 1048576] bytenr mimsmatch, extent item bytenr 12845056 file item bytenr 0
  data extent[12845056, 1048576] referencer count mismatch (root 5 owner 257 offset 0) wanted 1 have 0

  data extent[14233600, 12288] referencer count mismatch (parent 42139648) wanted 0 have 1
  data extent[14233600, 12288] referencer count mismatch (root 5 owner 307 offset 0) wanted 0 have 1
  data extent[14233600, 12288] referencer count mismatch (parent 30507008) wanted 0 have 1

Furthermore, the original function print_tree_backref_error() is a mess
already, here we clean it up by exacting all the error output into a
dedicated helper, print_backref_error(), so the function itself only
need to find out errors.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo 2022-12-05 14:07:30 +08:00 committed by David Sterba
parent d8c6021727
commit 04930385fd

View file

@ -3991,10 +3991,115 @@ static int do_check_fs_roots(struct cache_tree *root_cache)
return ret;
}
/*
* Define the minimal size for a buffer to describe the data backref.
* It needs to support something like:
*
* root <U64_MAX> owner <U64_MAX> offset <U64_MAX>
*
* Or
*
* parent <U64_MAX>
*
* Obviously the first pattern needs longer buffer size. The minimal size
* (including the tailing NUL) would be:
*
* 5 + 20 + 7 + 20 + 8 + 20 = 80.
*
* Just round it to 128 to provide extra wiggle room.
*/
#define DATA_EXTENT_DESC_BUF_LEN (128)
static void describe_data_extent_backref(char *buf, struct data_backref *dback)
{
if (dback->node.full_backref)
sprintf(buf, "parent %llu", dback->parent);
else
sprintf(buf, "root %llu owner %llu offset %llu",
dback->root, dback->owner, dback->offset);
}
static void print_data_backref_error(struct extent_record *rec,
struct data_backref *dback)
{
struct extent_backref *back = &dback->node;
char desc[DATA_EXTENT_DESC_BUF_LEN] = { 0 };
u32 found_refs;
u32 expected_refs;
if (!back->found_extent_tree) {
/* No backref item in extent tree. Thus expected refs should be 0. */
expected_refs = 0;
found_refs = dback->found_ref;
} else {
expected_refs = dback->num_refs;
found_refs = dback->found_ref;
}
/* Extent item bytenr mismatch with found file extent item. */
if (dback->disk_bytenr != rec->start)
fprintf(stderr,
"data extent[%llu, %llu] bytenr mimsmatch, extent item bytenr %llu file item bytenr %llu\n",
rec->start, rec->max_size, rec->start,
dback->disk_bytenr);
/* Extent item size mismatch with found file item. */
if (dback->bytes != rec->nr)
fprintf(stderr,
"data extent[%llu, %llu] size mimsmatch, extent item size %llu file item size %llu\n",
rec->start, rec->max_size, rec->nr, dback->bytes);
if (expected_refs != found_refs) {
describe_data_extent_backref(desc, dback);
fprintf(stderr,
"data extent[%llu, %llu] referencer count mismatch (%s) wanted %u have %u\n",
rec->start, rec->max_size, desc, expected_refs,
found_refs);
}
}
static void print_tree_backref_error(struct extent_record *rec, struct tree_backref *tback)
{
struct extent_backref *back = &tback->node;
/*
* For tree blocks, we only handle two cases here:
*
* - No backref item in extent tree
* - No tree block found (but with backref item)
*
* The refs count check is done by the global backref check at
* all_backpointers_checked().
*/
if (!back->found_extent_tree) {
fprintf(stderr,
"tree extent[%llu, %llu] %s %llu has no backref item in extent tree\n",
rec->start, rec->max_size,
(back->full_backref ? "parent" : "root"),
(back->full_backref ? tback->parent : tback->root));
return;
}
if (!back->found_ref) {
fprintf(stderr,
"tree extent[%llu, %llu] %s %llu has no tree block found\n",
rec->start, rec->max_size,
(back->full_backref ? "parent" : "root"),
(back->full_backref ? tback->parent : tback->root));
return;
}
}
static void print_backref_error(struct extent_record *rec,
struct extent_backref *back)
{
if (back->is_data)
print_data_backref_error(rec, to_data_backref(back));
else
print_tree_backref_error(rec, to_tree_backref(back));
}
static int all_backpointers_checked(struct extent_record *rec, int print_errs)
{
struct extent_backref *back, *tmp;
struct tree_backref *tback;
struct data_backref *dback;
u64 found = 0;
int err = 0;
@ -4005,42 +4110,11 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
err = 1;
if (!print_errs)
goto out;
if (back->is_data) {
dback = to_data_backref(back);
fprintf(stderr,
"data backref %llu %s %llu owner %llu offset %llu num_refs %lu not found in extent tree\n",
(unsigned long long)rec->start,
back->full_backref ?
"parent" : "root",
back->full_backref ?
(unsigned long long)dback->parent :
(unsigned long long)dback->root,
(unsigned long long)dback->owner,
(unsigned long long)dback->offset,
(unsigned long)dback->num_refs);
} else {
tback = to_tree_backref(back);
fprintf(stderr,
"tree backref %llu %s %llu not found in extent tree\n",
(unsigned long long)rec->start,
back->full_backref ? "parent" : "root",
back->full_backref ?
(unsigned long long)tback->parent :
(unsigned long long)tback->root);
}
}
if (!back->is_data && !back->found_ref) {
if (!back->found_ref) {
err = 1;
if (!print_errs)
goto out;
tback = to_tree_backref(back);
fprintf(stderr,
"backref %llu %s %llu not referenced back %p\n",
(unsigned long long)rec->start,
back->full_backref ? "parent" : "root",
back->full_backref ?
(unsigned long long)tback->parent :
(unsigned long long)tback->root, back);
}
if (back->is_data) {
dback = to_data_backref(back);
@ -4048,38 +4122,17 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
err = 1;
if (!print_errs)
goto out;
fprintf(stderr,
"incorrect local backref count on %llu %s %llu owner %llu offset %llu found %u wanted %u back %p\n",
(unsigned long long)rec->start,
back->full_backref ?
"parent" : "root",
back->full_backref ?
(unsigned long long)dback->parent :
(unsigned long long)dback->root,
(unsigned long long)dback->owner,
(unsigned long long)dback->offset,
dback->found_ref, dback->num_refs,
back);
}
if (dback->disk_bytenr != rec->start) {
err = 1;
if (!print_errs)
goto out;
fprintf(stderr,
"backref disk bytenr does not match extent record, bytenr=%llu, ref bytenr=%llu\n",
(unsigned long long)rec->start,
(unsigned long long)dback->disk_bytenr);
}
if (dback->bytes != rec->nr) {
err = 1;
if (!print_errs)
goto out;
fprintf(stderr,
"backref bytes do not match extent backref, bytenr=%llu, ref bytes=%llu, backref bytes=%llu\n",
(unsigned long long)rec->start,
(unsigned long long)rec->nr,
(unsigned long long)dback->bytes);
}
}
if (!back->is_data) {
@ -4088,6 +4141,8 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
dback = to_data_backref(back);
found += dback->found_ref;
}
if (err)
print_backref_error(rec, back);
}
if (found != rec->refs) {
err = 1;