mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 18:30:06 +00:00
btrfs: improve leaf dump and error handling
Improve the leaf dump behavior by: - Always dump the leaf first, then the error message - Output the slot number if possible Especially in __btrfs_free_extent() the leaf dump of extent tree can be pretty large. With an extra slot number it's much easier to locate the problem. Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
6c75a589cb
commit
eee3b81178
2 changed files with 59 additions and 68 deletions
|
@ -2633,6 +2633,7 @@ void btrfs_set_item_key_safe(struct btrfs_fs_info *fs_info,
|
||||||
if (slot > 0) {
|
if (slot > 0) {
|
||||||
btrfs_item_key(eb, &disk_key, slot - 1);
|
btrfs_item_key(eb, &disk_key, slot - 1);
|
||||||
if (unlikely(comp_keys(&disk_key, new_key) >= 0)) {
|
if (unlikely(comp_keys(&disk_key, new_key) >= 0)) {
|
||||||
|
btrfs_print_leaf(eb);
|
||||||
btrfs_crit(fs_info,
|
btrfs_crit(fs_info,
|
||||||
"slot %u key (%llu %u %llu) new key (%llu %u %llu)",
|
"slot %u key (%llu %u %llu) new key (%llu %u %llu)",
|
||||||
slot, btrfs_disk_key_objectid(&disk_key),
|
slot, btrfs_disk_key_objectid(&disk_key),
|
||||||
|
@ -2640,13 +2641,13 @@ void btrfs_set_item_key_safe(struct btrfs_fs_info *fs_info,
|
||||||
btrfs_disk_key_offset(&disk_key),
|
btrfs_disk_key_offset(&disk_key),
|
||||||
new_key->objectid, new_key->type,
|
new_key->objectid, new_key->type,
|
||||||
new_key->offset);
|
new_key->offset);
|
||||||
btrfs_print_leaf(eb);
|
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slot < btrfs_header_nritems(eb) - 1) {
|
if (slot < btrfs_header_nritems(eb) - 1) {
|
||||||
btrfs_item_key(eb, &disk_key, slot + 1);
|
btrfs_item_key(eb, &disk_key, slot + 1);
|
||||||
if (unlikely(comp_keys(&disk_key, new_key) <= 0)) {
|
if (unlikely(comp_keys(&disk_key, new_key) <= 0)) {
|
||||||
|
btrfs_print_leaf(eb);
|
||||||
btrfs_crit(fs_info,
|
btrfs_crit(fs_info,
|
||||||
"slot %u key (%llu %u %llu) new key (%llu %u %llu)",
|
"slot %u key (%llu %u %llu) new key (%llu %u %llu)",
|
||||||
slot, btrfs_disk_key_objectid(&disk_key),
|
slot, btrfs_disk_key_objectid(&disk_key),
|
||||||
|
@ -2654,7 +2655,6 @@ void btrfs_set_item_key_safe(struct btrfs_fs_info *fs_info,
|
||||||
btrfs_disk_key_offset(&disk_key),
|
btrfs_disk_key_offset(&disk_key),
|
||||||
new_key->objectid, new_key->type,
|
new_key->objectid, new_key->type,
|
||||||
new_key->offset);
|
new_key->offset);
|
||||||
btrfs_print_leaf(eb);
|
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1164,15 +1164,10 @@ int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
|
||||||
* should not happen at all.
|
* should not happen at all.
|
||||||
*/
|
*/
|
||||||
if (owner < BTRFS_FIRST_FREE_OBJECTID) {
|
if (owner < BTRFS_FIRST_FREE_OBJECTID) {
|
||||||
btrfs_crit(trans->fs_info,
|
|
||||||
"adding refs to an existing tree ref, bytenr %llu num_bytes %llu root_objectid %llu",
|
|
||||||
bytenr, num_bytes, root_objectid);
|
|
||||||
if (IS_ENABLED(CONFIG_BTRFS_DEBUG)) {
|
|
||||||
WARN_ON(1);
|
|
||||||
btrfs_crit(trans->fs_info,
|
|
||||||
"path->slots[0]=%d path->nodes[0]:", path->slots[0]);
|
|
||||||
btrfs_print_leaf(path->nodes[0]);
|
btrfs_print_leaf(path->nodes[0]);
|
||||||
}
|
btrfs_crit(trans->fs_info,
|
||||||
|
"adding refs to an existing tree ref, bytenr %llu num_bytes %llu root_objectid %llu slot %u",
|
||||||
|
bytenr, num_bytes, root_objectid, path->slots[0]);
|
||||||
return -EUCLEAN;
|
return -EUCLEAN;
|
||||||
}
|
}
|
||||||
update_inline_extent_backref(path, iref, refs_to_add, extent_op);
|
update_inline_extent_backref(path, iref, refs_to_add, extent_op);
|
||||||
|
@ -2838,6 +2833,13 @@ static int do_free_extent_accounting(struct btrfs_trans_handle *trans,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define abort_and_dump(trans, path, fmt, args...) \
|
||||||
|
({ \
|
||||||
|
btrfs_abort_transaction(trans, -EUCLEAN); \
|
||||||
|
btrfs_print_leaf(path->nodes[0]); \
|
||||||
|
btrfs_crit(trans->fs_info, fmt, ##args); \
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Drop one or more refs of @node.
|
* Drop one or more refs of @node.
|
||||||
*
|
*
|
||||||
|
@ -2978,10 +2980,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||||
|
|
||||||
if (!found_extent) {
|
if (!found_extent) {
|
||||||
if (iref) {
|
if (iref) {
|
||||||
btrfs_crit(info,
|
abort_and_dump(trans, path,
|
||||||
"invalid iref, no EXTENT/METADATA_ITEM found but has inline extent ref");
|
"invalid iref slot %u, no EXTENT/METADATA_ITEM found but has inline extent ref",
|
||||||
btrfs_abort_transaction(trans, -EUCLEAN);
|
path->slots[0]);
|
||||||
goto err_dump;
|
ret = -EUCLEAN;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
/* Must be SHARED_* item, remove the backref first */
|
/* Must be SHARED_* item, remove the backref first */
|
||||||
ret = remove_extent_backref(trans, extent_root, path,
|
ret = remove_extent_backref(trans, extent_root, path,
|
||||||
|
@ -3029,11 +3032,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
btrfs_err(info,
|
|
||||||
"umm, got %d back from search, was looking for %llu",
|
|
||||||
ret, bytenr);
|
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
btrfs_print_leaf(path->nodes[0]);
|
btrfs_print_leaf(path->nodes[0]);
|
||||||
|
btrfs_err(info,
|
||||||
|
"umm, got %d back from search, was looking for %llu, slot %d",
|
||||||
|
ret, bytenr, path->slots[0]);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
btrfs_abort_transaction(trans, ret);
|
btrfs_abort_transaction(trans, ret);
|
||||||
|
@ -3042,12 +3045,10 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||||
extent_slot = path->slots[0];
|
extent_slot = path->slots[0];
|
||||||
}
|
}
|
||||||
} else if (WARN_ON(ret == -ENOENT)) {
|
} else if (WARN_ON(ret == -ENOENT)) {
|
||||||
btrfs_print_leaf(path->nodes[0]);
|
abort_and_dump(trans, path,
|
||||||
btrfs_err(info,
|
"unable to find ref byte nr %llu parent %llu root %llu owner %llu offset %llu slot %d",
|
||||||
"unable to find ref byte nr %llu parent %llu root %llu owner %llu offset %llu",
|
|
||||||
bytenr, parent, root_objectid, owner_objectid,
|
bytenr, parent, root_objectid, owner_objectid,
|
||||||
owner_offset);
|
owner_offset, path->slots[0]);
|
||||||
btrfs_abort_transaction(trans, ret);
|
|
||||||
goto out;
|
goto out;
|
||||||
} else {
|
} else {
|
||||||
btrfs_abort_transaction(trans, ret);
|
btrfs_abort_transaction(trans, ret);
|
||||||
|
@ -3067,14 +3068,15 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||||
if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID &&
|
if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID &&
|
||||||
key.type == BTRFS_EXTENT_ITEM_KEY) {
|
key.type == BTRFS_EXTENT_ITEM_KEY) {
|
||||||
struct btrfs_tree_block_info *bi;
|
struct btrfs_tree_block_info *bi;
|
||||||
|
|
||||||
if (item_size < sizeof(*ei) + sizeof(*bi)) {
|
if (item_size < sizeof(*ei) + sizeof(*bi)) {
|
||||||
btrfs_crit(info,
|
abort_and_dump(trans, path,
|
||||||
"invalid extent item size for key (%llu, %u, %llu) owner %llu, has %u expect >= %zu",
|
"invalid extent item size for key (%llu, %u, %llu) slot %u owner %llu, has %u expect >= %zu",
|
||||||
key.objectid, key.type, key.offset,
|
key.objectid, key.type, key.offset,
|
||||||
owner_objectid, item_size,
|
path->slots[0], owner_objectid, item_size,
|
||||||
sizeof(*ei) + sizeof(*bi));
|
sizeof(*ei) + sizeof(*bi));
|
||||||
btrfs_abort_transaction(trans, -EUCLEAN);
|
ret = -EUCLEAN;
|
||||||
goto err_dump;
|
goto out;
|
||||||
}
|
}
|
||||||
bi = (struct btrfs_tree_block_info *)(ei + 1);
|
bi = (struct btrfs_tree_block_info *)(ei + 1);
|
||||||
WARN_ON(owner_objectid != btrfs_tree_block_level(leaf, bi));
|
WARN_ON(owner_objectid != btrfs_tree_block_level(leaf, bi));
|
||||||
|
@ -3082,11 +3084,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||||
|
|
||||||
refs = btrfs_extent_refs(leaf, ei);
|
refs = btrfs_extent_refs(leaf, ei);
|
||||||
if (refs < refs_to_drop) {
|
if (refs < refs_to_drop) {
|
||||||
btrfs_crit(info,
|
abort_and_dump(trans, path,
|
||||||
"trying to drop %d refs but we only have %llu for bytenr %llu",
|
"trying to drop %d refs but we only have %llu for bytenr %llu slot %u",
|
||||||
refs_to_drop, refs, bytenr);
|
refs_to_drop, refs, bytenr, path->slots[0]);
|
||||||
btrfs_abort_transaction(trans, -EUCLEAN);
|
ret = -EUCLEAN;
|
||||||
goto err_dump;
|
goto out;
|
||||||
}
|
}
|
||||||
refs -= refs_to_drop;
|
refs -= refs_to_drop;
|
||||||
|
|
||||||
|
@ -3099,10 +3101,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||||
*/
|
*/
|
||||||
if (iref) {
|
if (iref) {
|
||||||
if (!found_extent) {
|
if (!found_extent) {
|
||||||
btrfs_crit(info,
|
abort_and_dump(trans, path,
|
||||||
"invalid iref, got inlined extent ref but no EXTENT/METADATA_ITEM found");
|
"invalid iref, got inlined extent ref but no EXTENT/METADATA_ITEM found, slot %u",
|
||||||
btrfs_abort_transaction(trans, -EUCLEAN);
|
path->slots[0]);
|
||||||
goto err_dump;
|
ret = -EUCLEAN;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
btrfs_set_extent_refs(leaf, ei, refs);
|
btrfs_set_extent_refs(leaf, ei, refs);
|
||||||
|
@ -3121,21 +3124,21 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||||
if (found_extent) {
|
if (found_extent) {
|
||||||
if (is_data && refs_to_drop !=
|
if (is_data && refs_to_drop !=
|
||||||
extent_data_ref_count(path, iref)) {
|
extent_data_ref_count(path, iref)) {
|
||||||
btrfs_crit(info,
|
abort_and_dump(trans, path,
|
||||||
"invalid refs_to_drop, current refs %u refs_to_drop %u",
|
"invalid refs_to_drop, current refs %u refs_to_drop %u slot %u",
|
||||||
extent_data_ref_count(path, iref),
|
extent_data_ref_count(path, iref),
|
||||||
refs_to_drop);
|
refs_to_drop, path->slots[0]);
|
||||||
btrfs_abort_transaction(trans, -EUCLEAN);
|
ret = -EUCLEAN;
|
||||||
goto err_dump;
|
goto out;
|
||||||
}
|
}
|
||||||
if (iref) {
|
if (iref) {
|
||||||
if (path->slots[0] != extent_slot) {
|
if (path->slots[0] != extent_slot) {
|
||||||
btrfs_crit(info,
|
abort_and_dump(trans, path,
|
||||||
"invalid iref, extent item key (%llu %u %llu) doesn't have wanted iref",
|
"invalid iref, extent item key (%llu %u %llu) slot %u doesn't have wanted iref",
|
||||||
key.objectid, key.type,
|
key.objectid, key.type,
|
||||||
key.offset);
|
key.offset, path->slots[0]);
|
||||||
btrfs_abort_transaction(trans, -EUCLEAN);
|
ret = -EUCLEAN;
|
||||||
goto err_dump;
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
|
@ -3145,10 +3148,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||||
* [ EXTENT/METADATA_ITEM ][ SHARED_* ITEM ]
|
* [ EXTENT/METADATA_ITEM ][ SHARED_* ITEM ]
|
||||||
*/
|
*/
|
||||||
if (path->slots[0] != extent_slot + 1) {
|
if (path->slots[0] != extent_slot + 1) {
|
||||||
btrfs_crit(info,
|
abort_and_dump(trans, path,
|
||||||
"invalid SHARED_* item, previous item is not EXTENT/METADATA_ITEM");
|
"invalid SHARED_* item slot %u, previous item is not EXTENT/METADATA_ITEM",
|
||||||
btrfs_abort_transaction(trans, -EUCLEAN);
|
path->slots[0]);
|
||||||
goto err_dump;
|
ret = -EUCLEAN;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
path->slots[0] = extent_slot;
|
path->slots[0] = extent_slot;
|
||||||
num_to_del = 2;
|
num_to_del = 2;
|
||||||
|
@ -3170,19 +3174,6 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||||
out:
|
out:
|
||||||
btrfs_free_path(path);
|
btrfs_free_path(path);
|
||||||
return ret;
|
return ret;
|
||||||
err_dump:
|
|
||||||
/*
|
|
||||||
* Leaf dump can take up a lot of log buffer, so we only do full leaf
|
|
||||||
* dump for debug build.
|
|
||||||
*/
|
|
||||||
if (IS_ENABLED(CONFIG_BTRFS_DEBUG)) {
|
|
||||||
btrfs_crit(info, "path->slots[0]=%d extent_slot=%d",
|
|
||||||
path->slots[0], extent_slot);
|
|
||||||
btrfs_print_leaf(path->nodes[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
btrfs_free_path(path);
|
|
||||||
return -EUCLEAN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue