]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: improve leaf dump and error handling
authorQu Wenruo <wqu@suse.com>
Thu, 27 Apr 2023 12:16:28 +0000 (20:16 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 19 Jun 2023 11:59:23 +0000 (13:59 +0200)
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>
fs/btrfs/ctree.c
fs/btrfs/extent-tree.c

index f76da470d60b1e49778b9296f80418ec8e5e8681..4b33bc087fc785e7dbb367b1af09e7cd3b226cb9 100644 (file)
@@ -2633,6 +2633,7 @@ void btrfs_set_item_key_safe(struct btrfs_fs_info *fs_info,
        if (slot > 0) {
                btrfs_item_key(eb, &disk_key, slot - 1);
                if (unlikely(comp_keys(&disk_key, new_key) >= 0)) {
+                       btrfs_print_leaf(eb);
                        btrfs_crit(fs_info,
                "slot %u key (%llu %u %llu) new key (%llu %u %llu)",
                                   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),
                                   new_key->objectid, new_key->type,
                                   new_key->offset);
-                       btrfs_print_leaf(eb);
                        BUG();
                }
        }
        if (slot < btrfs_header_nritems(eb) - 1) {
                btrfs_item_key(eb, &disk_key, slot + 1);
                if (unlikely(comp_keys(&disk_key, new_key) <= 0)) {
+                       btrfs_print_leaf(eb);
                        btrfs_crit(fs_info,
                "slot %u key (%llu %u %llu) new key (%llu %u %llu)",
                                   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),
                                   new_key->objectid, new_key->type,
                                   new_key->offset);
-                       btrfs_print_leaf(eb);
                        BUG();
                }
        }
index e1d5198d1f5e03136ade34f892f6d1fb2d8b9271..1c326d530764039b01cea6cd918ce778ebdac3db 100644 (file)
@@ -1164,15 +1164,10 @@ int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
                 * should not happen at all.
                 */
                if (owner < BTRFS_FIRST_FREE_OBJECTID) {
+                       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",
-                                  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]);
-                       }
+"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;
                }
                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;
 }
 
+#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.
  *
@@ -2978,10 +2980,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
 
                if (!found_extent) {
                        if (iref) {
-                               btrfs_crit(info,
-"invalid iref, no EXTENT/METADATA_ITEM found but has inline extent ref");
-                               btrfs_abort_transaction(trans, -EUCLEAN);
-                               goto err_dump;
+                               abort_and_dump(trans, path,
+"invalid iref slot %u, no EXTENT/METADATA_ITEM found but has inline extent ref",
+                                          path->slots[0]);
+                               ret = -EUCLEAN;
+                               goto out;
                        }
                        /* Must be SHARED_* item, remove the backref first */
                        ret = remove_extent_backref(trans, extent_root, path,
@@ -3029,11 +3032,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                        }
 
                        if (ret) {
-                               btrfs_err(info,
-                                         "umm, got %d back from search, was looking for %llu",
-                                         ret, bytenr);
                                if (ret > 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) {
                                btrfs_abort_transaction(trans, ret);
@@ -3042,12 +3045,10 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                        extent_slot = path->slots[0];
                }
        } else if (WARN_ON(ret == -ENOENT)) {
-               btrfs_print_leaf(path->nodes[0]);
-               btrfs_err(info,
-                       "unable to find ref byte nr %llu parent %llu root %llu  owner %llu offset %llu",
-                       bytenr, parent, root_objectid, owner_objectid,
-                       owner_offset);
-               btrfs_abort_transaction(trans, ret);
+               abort_and_dump(trans, path,
+"unable to find ref byte nr %llu parent %llu root %llu owner %llu offset %llu slot %d",
+                              bytenr, parent, root_objectid, owner_objectid,
+                              owner_offset, path->slots[0]);
                goto out;
        } else {
                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 &&
            key.type == BTRFS_EXTENT_ITEM_KEY) {
                struct btrfs_tree_block_info *bi;
+
                if (item_size < sizeof(*ei) + sizeof(*bi)) {
-                       btrfs_crit(info,
-"invalid extent item size for key (%llu, %u, %llu) owner %llu, has %u expect >= %zu",
-                                  key.objectid, key.type, key.offset,
-                                  owner_objectid, item_size,
-                                  sizeof(*ei) + sizeof(*bi));
-                       btrfs_abort_transaction(trans, -EUCLEAN);
-                       goto err_dump;
+                       abort_and_dump(trans, path,
+"invalid extent item size for key (%llu, %u, %llu) slot %u owner %llu, has %u expect >= %zu",
+                                      key.objectid, key.type, key.offset,
+                                      path->slots[0], owner_objectid, item_size,
+                                      sizeof(*ei) + sizeof(*bi));
+                       ret = -EUCLEAN;
+                       goto out;
                }
                bi = (struct btrfs_tree_block_info *)(ei + 1);
                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);
        if (refs < refs_to_drop) {
-               btrfs_crit(info,
-               "trying to drop %d refs but we only have %llu for bytenr %llu",
-                         refs_to_drop, refs, bytenr);
-               btrfs_abort_transaction(trans, -EUCLEAN);
-               goto err_dump;
+               abort_and_dump(trans, path,
+               "trying to drop %d refs but we only have %llu for bytenr %llu slot %u",
+                              refs_to_drop, refs, bytenr, path->slots[0]);
+               ret = -EUCLEAN;
+               goto out;
        }
        refs -= refs_to_drop;
 
@@ -3099,10 +3101,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                 */
                if (iref) {
                        if (!found_extent) {
-                               btrfs_crit(info,
-"invalid iref, got inlined extent ref but no EXTENT/METADATA_ITEM found");
-                               btrfs_abort_transaction(trans, -EUCLEAN);
-                               goto err_dump;
+                               abort_and_dump(trans, path,
+"invalid iref, got inlined extent ref but no EXTENT/METADATA_ITEM found, slot %u",
+                                              path->slots[0]);
+                               ret = -EUCLEAN;
+                               goto out;
                        }
                } else {
                        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 (is_data && refs_to_drop !=
                            extent_data_ref_count(path, iref)) {
-                               btrfs_crit(info,
-               "invalid refs_to_drop, current refs %u refs_to_drop %u",
-                                          extent_data_ref_count(path, iref),
-                                          refs_to_drop);
-                               btrfs_abort_transaction(trans, -EUCLEAN);
-                               goto err_dump;
+                               abort_and_dump(trans, path,
+               "invalid refs_to_drop, current refs %u refs_to_drop %u slot %u",
+                                              extent_data_ref_count(path, iref),
+                                              refs_to_drop, path->slots[0]);
+                               ret = -EUCLEAN;
+                               goto out;
                        }
                        if (iref) {
                                if (path->slots[0] != extent_slot) {
-                                       btrfs_crit(info,
-"invalid iref, extent item key (%llu %u %llu) doesn't have wanted iref",
-                                                  key.objectid, key.type,
-                                                  key.offset);
-                                       btrfs_abort_transaction(trans, -EUCLEAN);
-                                       goto err_dump;
+                                       abort_and_dump(trans, path,
+"invalid iref, extent item key (%llu %u %llu) slot %u doesn't have wanted iref",
+                                                      key.objectid, key.type,
+                                                      key.offset, path->slots[0]);
+                                       ret = -EUCLEAN;
+                                       goto out;
                                }
                        } else {
                                /*
@@ -3145,10 +3148,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                 * [ EXTENT/METADATA_ITEM ][ SHARED_* ITEM ]
                                 */
                                if (path->slots[0] != extent_slot + 1) {
-                                       btrfs_crit(info,
-       "invalid SHARED_* item, previous item is not EXTENT/METADATA_ITEM");
-                                       btrfs_abort_transaction(trans, -EUCLEAN);
-                                       goto err_dump;
+                                       abort_and_dump(trans, path,
+       "invalid SHARED_* item slot %u, previous item is not EXTENT/METADATA_ITEM",
+                                                      path->slots[0]);
+                                       ret = -EUCLEAN;
+                                       goto out;
                                }
                                path->slots[0] = extent_slot;
                                num_to_del = 2;
@@ -3170,19 +3174,6 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
 out:
        btrfs_free_path(path);
        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;
 }
 
 /*