]> www.infradead.org Git - users/hch/misc.git/commitdiff
btrfs: dump detailed info and specific messages on log replay failures
authorFilipe Manana <fdmanana@suse.com>
Fri, 5 Sep 2025 11:58:19 +0000 (12:58 +0100)
committerDavid Sterba <dsterba@suse.com>
Tue, 23 Sep 2025 06:49:21 +0000 (08:49 +0200)
Currently debugging log replay failures can be harder than needed, since
all we do now is abort a transaction, which gives us a line number, a
stack trace and an error code. But that is most of the times not enough
to give some clue about what went wrong. So add a new helper to abort
log replay and provide contextual information:

1) Dump the current leaf of the log tree being processed and print the
   slot we are currently at and the key at that slot;

2) Dump the current subvolume tree leaf if we have any;

3) Print the current stage of log replay;

4) Print the id of the subvolume root associated with the log tree we
   are currently processing (as we can have multiple);

5) Print some error message to mention what we were trying to do when we
   got an error.

Replace all transaction abort calls (btrfs_abort_transaction()) with the
new helper btrfs_abort_log_replay(), which besides dumping all that extra
information, it also aborts the current transaction.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/fs.h
fs/btrfs/messages.c
fs/btrfs/tree-log.c

index bf4a1b75b0cf48c158332f19391eb5419ec31f48..ba63a40b2bde1f86be015548d6203151ed257fc4 100644 (file)
@@ -104,6 +104,8 @@ enum {
        BTRFS_FS_STATE_RO,
        /* Track if a transaction abort has been reported on this filesystem */
        BTRFS_FS_STATE_TRANS_ABORTED,
+       /* Track if log replay has failed. */
+       BTRFS_FS_STATE_LOG_REPLAY_ABORTED,
        /*
         * Bio operations should be blocked on this filesystem because a source
         * or target device is being destroyed as part of a device replace
index 363fd28c026880525b0c014e3bd10fa8d23900f8..a0cf8effe008e14950c92d595e7677fc30b67423 100644 (file)
@@ -18,6 +18,7 @@ static const char fs_state_chars[] = {
        [BTRFS_FS_STATE_REMOUNTING]             = 'M',
        [BTRFS_FS_STATE_RO]                     = 0,
        [BTRFS_FS_STATE_TRANS_ABORTED]          = 'A',
+       [BTRFS_FS_STATE_LOG_REPLAY_ABORTED]     = 'O',
        [BTRFS_FS_STATE_DEV_REPLACING]          = 'R',
        [BTRFS_FS_STATE_DUMMY_FS_INFO]          = 0,
        [BTRFS_FS_STATE_NO_DATA_CSUMS]          = 'C',
index 83b79023baaed5995905166b4579f00a6e638d43..144b12725365062d5565a2503ab1ddd9f5f2bf2b 100644 (file)
@@ -27,6 +27,7 @@
 #include "file-item.h"
 #include "file.h"
 #include "orphan.h"
+#include "print-tree.h"
 #include "tree-checker.h"
 
 #define MAX_CONFLICT_INODES 10
@@ -167,6 +168,62 @@ struct walk_control {
        struct btrfs_path *subvol_path;
 };
 
+static void do_abort_log_replay(struct walk_control *wc, const char *function,
+                               unsigned int line, int error, const char *fmt, ...)
+{
+       struct btrfs_fs_info *fs_info = wc->trans->fs_info;
+       struct va_format vaf;
+       va_list args;
+
+       /*
+        * Do nothing if we already aborted, to avoid dumping leaves again which
+        * can be verbose. Further more, only the first call is useful since it
+        * is where we have a problem. Note that we do not use the flag
+        * BTRFS_FS_STATE_TRANS_ABORTED because log replay calls functions that
+        * are outside of tree-log.c that can abort transactions (such as
+        * btrfs_add_link() for example), so if that happens we still want to
+        * dump all log replay specific information below.
+        */
+       if (test_and_set_bit(BTRFS_FS_STATE_LOG_REPLAY_ABORTED, &fs_info->fs_state))
+               return;
+
+       btrfs_abort_transaction(wc->trans, error);
+
+       if (wc->subvol_path->nodes[0]) {
+               btrfs_crit(fs_info,
+                          "subvolume (root %llu) leaf currently being processed:",
+                          btrfs_root_id(wc->root));
+               btrfs_print_leaf(wc->subvol_path->nodes[0]);
+       }
+
+       if (wc->log_leaf) {
+               btrfs_crit(fs_info,
+         "log tree (for root %llu) leaf currently being processed (slot %d key %llu %u %llu):",
+                          btrfs_root_id(wc->root), wc->log_slot,
+                          wc->log_key.objectid, wc->log_key.type, wc->log_key.offset);
+               btrfs_print_leaf(wc->log_leaf);
+       }
+
+       va_start(args, fmt);
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       btrfs_crit(fs_info,
+          "log replay failed in %s:%u for root %llu, stage %d, with error %d: %pV",
+                  function, line, btrfs_root_id(wc->root), wc->stage, error, &vaf);
+
+       va_end(args);
+}
+
+/*
+ * Use this for aborting a transaction during log replay while we are down the
+ * call chain of replay_one_buffer(), so that we get a lot more useful
+ * information for debugging issues when compared to a plain call to
+ * btrfs_abort_transaction().
+ */
+#define btrfs_abort_log_replay(wc, error, fmt, args...) \
+       do_abort_log_replay((wc), __func__, __LINE__, (error), fmt, ##args)
+
 static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                           struct btrfs_inode *inode,
                           int inode_only,
@@ -452,7 +509,10 @@ static int overwrite_item(struct walk_control *wc)
        /* Look for the key in the destination tree. */
        ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0);
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+               "failed to search subvolume tree for key (%llu %u %llu) root %llu",
+                                      wc->log_key.objectid, wc->log_key.type,
+                                      wc->log_key.offset, btrfs_root_id(root));
                return ret;
        }
 
@@ -472,7 +532,8 @@ static int overwrite_item(struct walk_control *wc)
                }
                src_copy = kmalloc(item_size, GFP_NOFS);
                if (!src_copy) {
-                       btrfs_abort_transaction(trans, -ENOMEM);
+                       btrfs_abort_log_replay(wc, -ENOMEM,
+                              "failed to allocate memory for log leaf item");
                        return -ENOMEM;
                }
 
@@ -556,7 +617,10 @@ insert:
                else if (found_size < item_size)
                        btrfs_extend_item(trans, wc->subvol_path, item_size - found_size);
        } else if (ret) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                                      "failed to insert item for key (%llu %u %llu)",
+                                      wc->log_key.objectid, wc->log_key.type,
+                                      wc->log_key.offset);
                return ret;
        }
        dst_ptr = btrfs_item_ptr_offset(dst_eb, dst_slot);
@@ -691,18 +755,19 @@ static noinline int replay_one_extent(struct walk_control *wc)
                extent_end = ALIGN(start + size,
                                   fs_info->sectorsize);
        } else {
-               btrfs_abort_transaction(trans, -EUCLEAN);
-               btrfs_err(fs_info,
-                 "unexpected extent type=%d root=%llu inode=%llu offset=%llu",
-                         found_type, btrfs_root_id(root), wc->log_key.objectid,
-                         wc->log_key.offset);
+               btrfs_abort_log_replay(wc, -EUCLEAN,
+                      "unexpected extent type=%d root=%llu inode=%llu offset=%llu",
+                                      found_type, btrfs_root_id(root),
+                                      wc->log_key.objectid, wc->log_key.offset);
                return -EUCLEAN;
        }
 
        inode = btrfs_iget_logging(wc->log_key.objectid, root);
        if (IS_ERR(inode)) {
                ret = PTR_ERR(inode);
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                                      "failed to get inode %llu for root %llu",
+                                      wc->log_key.objectid, btrfs_root_id(root));
                return ret;
        }
 
@@ -743,7 +808,10 @@ static noinline int replay_one_extent(struct walk_control *wc)
        drop_args.path = wc->subvol_path;
        ret = btrfs_drop_extents(trans, root, inode, &drop_args);
        if (ret) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+              "failed to drop extents for inode %llu range [%llu, %llu) root %llu",
+                                      wc->log_key.objectid, start, extent_end,
+                                      btrfs_root_id(root));
                goto out;
        }
 
@@ -768,7 +836,10 @@ static noinline int replay_one_extent(struct walk_control *wc)
        ret = btrfs_insert_empty_item(trans, root, wc->subvol_path,
                                      &wc->log_key, sizeof(*item));
        if (ret) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                      "failed to insert item with key (%llu %u %llu) root %llu",
+                                      wc->log_key.objectid, wc->log_key.type,
+                                      wc->log_key.offset, btrfs_root_id(root));
                goto out;
        }
        dest_offset = btrfs_item_ptr_offset(wc->subvol_path->nodes[0],
@@ -801,7 +872,10 @@ static noinline int replay_one_extent(struct walk_control *wc)
         */
        ret = btrfs_qgroup_trace_extent(trans, ins.objectid, ins.offset);
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+"failed to trace extent for bytenr %llu disk_num_bytes %llu inode %llu root %llu",
+                                      ins.objectid, ins.offset,
+                                      wc->log_key.objectid, btrfs_root_id(root));
                goto out;
        }
 
@@ -811,7 +885,10 @@ static noinline int replay_one_extent(struct walk_control *wc)
         */
        ret = btrfs_lookup_data_extent(fs_info, ins.objectid, ins.offset);
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+"failed to lookup data extent for bytenr %llu disk_num_bytes %llu inode %llu root %llu",
+                                      ins.objectid, ins.offset,
+                                      wc->log_key.objectid, btrfs_root_id(root));
                goto out;
        } else if (ret == 0) {
                struct btrfs_ref ref = {
@@ -825,7 +902,11 @@ static noinline int replay_one_extent(struct walk_control *wc)
                btrfs_init_data_ref(&ref, wc->log_key.objectid, offset, 0, false);
                ret = btrfs_inc_extent_ref(trans, &ref);
                if (ret) {
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+"failed to increment data extent for bytenr %llu disk_num_bytes %llu inode %llu root %llu",
+                                              ins.objectid, ins.offset,
+                                              wc->log_key.objectid,
+                                              btrfs_root_id(root));
                        goto out;
                }
        } else {
@@ -833,7 +914,10 @@ static noinline int replay_one_extent(struct walk_control *wc)
                ret = btrfs_alloc_logged_file_extent(trans, btrfs_root_id(root),
                                                     wc->log_key.objectid, offset, &ins);
                if (ret) {
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+"failed to allocate logged data extent for bytenr %llu disk_num_bytes %llu offset %llu inode %llu root %llu",
+                                              ins.objectid, ins.offset, offset,
+                                              wc->log_key.objectid, btrfs_root_id(root));
                        goto out;
                }
        }
@@ -851,7 +935,10 @@ static noinline int replay_one_extent(struct walk_control *wc)
        ret = btrfs_lookup_csums_list(root->log_root, csum_start, csum_end - 1,
                                      &ordered_sums, false);
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+              "failed to lookups csums for range [%llu, %llu) inode %llu root %llu",
+                                      csum_start, csum_end, wc->log_key.objectid,
+                                      btrfs_root_id(root));
                goto out;
        }
        ret = 0;
@@ -909,12 +996,22 @@ static noinline int replay_one_extent(struct walk_control *wc)
                        ret = btrfs_del_csums(trans, csum_root, sums->logical,
                                              sums->len);
                        if (ret)
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+              "failed to delete csums for range [%llu, %llu) inode %llu root %llu",
+                                                      sums->logical,
+                                                      sums->logical + sums->len,
+                                                      wc->log_key.objectid,
+                                                      btrfs_root_id(root));
                }
                if (!ret) {
                        ret = btrfs_csum_file_blocks(trans, csum_root, sums);
                        if (ret)
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+              "failed to add csums for range [%llu, %llu) inode %llu root %llu",
+                                                      sums->logical,
+                                                      sums->logical + sums->len,
+                                                      wc->log_key.objectid,
+                                                      btrfs_root_id(root));
                }
                list_del(&sums->list);
                kfree(sums);
@@ -925,14 +1022,19 @@ static noinline int replay_one_extent(struct walk_control *wc)
 update_inode:
        ret = btrfs_inode_set_file_extent_range(inode, start, extent_end - start);
        if (ret) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+              "failed to set file extent range [%llu, %llu) inode %llu root %llu",
+                                      start, extent_end, wc->log_key.objectid,
+                                      btrfs_root_id(root));
                goto out;
        }
 
        btrfs_update_inode_bytes(inode, nbytes, drop_args.bytes_found);
        ret = btrfs_update_inode(trans, inode);
        if (ret)
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                                      "failed to update inode %llu root %llu",
+                                      wc->log_key.objectid, btrfs_root_id(root));
 out:
        iput(&inode->vfs_inode);
        return ret;
@@ -948,7 +1050,10 @@ static int unlink_inode_for_log_replay(struct walk_control *wc,
 
        ret = btrfs_unlink_inode(trans, dir, inode, name);
        if (ret) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+              "failed to unlink inode %llu parent dir %llu name %.*s root %llu",
+                                      btrfs_ino(inode), btrfs_ino(dir), name->len,
+                                      name->name, btrfs_root_id(inode->root));
                return ret;
        }
        /*
@@ -959,7 +1064,11 @@ static int unlink_inode_for_log_replay(struct walk_control *wc,
         */
        ret = btrfs_run_delayed_items(trans);
        if (ret)
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+"failed to run delayed items current inode %llu parent dir %llu name %.*s root %llu",
+                                      btrfs_ino(inode), btrfs_ino(dir), name->len,
+                                      name->name, btrfs_root_id(inode->root));
+
        return ret;
 }
 
@@ -975,7 +1084,6 @@ static noinline int drop_one_dir_item(struct walk_control *wc,
                                      struct btrfs_inode *dir,
                                      struct btrfs_dir_item *di)
 {
-       struct btrfs_trans_handle *trans = wc->trans;
        struct btrfs_root *root = dir->root;
        struct btrfs_inode *inode;
        struct fscrypt_str name;
@@ -986,7 +1094,9 @@ static noinline int drop_one_dir_item(struct walk_control *wc,
        btrfs_dir_item_key_to_cpu(leaf, di, &location);
        ret = read_alloc_one_name(leaf, di + 1, btrfs_dir_name_len(leaf, di), &name);
        if (ret) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                                      "failed to allocate name for dir %llu root %llu",
+                                      btrfs_ino(dir), btrfs_root_id(root));
                return ret;
        }
 
@@ -995,7 +1105,10 @@ static noinline int drop_one_dir_item(struct walk_control *wc,
        inode = btrfs_iget_logging(location.objectid, root);
        if (IS_ERR(inode)) {
                ret = PTR_ERR(inode);
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                      "failed to open inode %llu parent dir %llu name %.*s root %llu",
+                                      location.objectid, btrfs_ino(dir),
+                                      name.len, name.name, btrfs_root_id(root));
                inode = NULL;
                goto out;
        }
@@ -1115,7 +1228,6 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
        ptr = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]);
        ptr_end = ptr + btrfs_item_size(leaf, wc->subvol_path->slots[0]);
        while (ptr < ptr_end) {
-               struct btrfs_trans_handle *trans = wc->trans;
                struct fscrypt_str victim_name;
                struct btrfs_inode_ref *victim_ref;
                int ret;
@@ -1125,17 +1237,25 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
                                          btrfs_inode_ref_name_len(leaf, victim_ref),
                                          &victim_name);
                if (ret) {
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+              "failed to allocate name for inode %llu parent dir %llu root %llu",
+                                              btrfs_ino(inode), btrfs_ino(dir),
+                                              btrfs_root_id(inode->root));
                        return ret;
                }
 
                ret = backref_in_log(wc->log, search_key, btrfs_ino(dir), &victim_name);
                if (ret) {
-                       kfree(victim_name.name);
                        if (ret < 0) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+"failed to check if backref is in log tree for inode %llu parent dir %llu name %.*s root %llu",
+                                                      btrfs_ino(inode), btrfs_ino(dir),
+                                                      victim_name.len, victim_name.name,
+                                                      btrfs_root_id(inode->root));
+                               kfree(victim_name.name);
                                return ret;
                        }
+                       kfree(victim_name.name);
                        ptr = (unsigned long)(victim_ref + 1) + victim_name.len;
                        continue;
                }
@@ -1164,7 +1284,6 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
        u32 cur_offset = 0;
 
        while (cur_offset < item_size) {
-               struct btrfs_trans_handle *trans = wc->trans;
                struct btrfs_root *log_root = wc->log;
                struct btrfs_inode_extref *extref;
                struct fscrypt_str victim_name;
@@ -1179,7 +1298,10 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
                ret = read_alloc_one_name(leaf, &extref->name, victim_name.len,
                                          &victim_name);
                if (ret) {
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+              "failed to allocate name for inode %llu parent dir %llu root %llu",
+                                              btrfs_ino(inode), btrfs_ino(dir),
+                                              btrfs_root_id(inode->root));
                        return ret;
                }
 
@@ -1190,11 +1312,16 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
                                                       victim_name.len);
                ret = backref_in_log(log_root, search_key, btrfs_ino(dir), &victim_name);
                if (ret) {
-                       kfree(victim_name.name);
                        if (ret < 0) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+"failed to check if backref is in log tree for inode %llu parent dir %llu name %.*s root %llu",
+                                                      btrfs_ino(inode), btrfs_ino(dir),
+                                                      victim_name.len, victim_name.name,
+                                                      btrfs_root_id(inode->root));
+                               kfree(victim_name.name);
                                return ret;
                        }
+                       kfree(victim_name.name);
 next:
                        cur_offset += victim_name.len + sizeof(*extref);
                        continue;
@@ -1232,7 +1359,10 @@ again:
        search_key.offset = btrfs_ino(dir);
        ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+              "failed to search subvolume tree for key (%llu %u %llu) root %llu",
+                                      search_key.objectid, search_key.type,
+                                      search_key.offset, btrfs_root_id(root));
                return ret;
        } else if (ret == 0) {
                /*
@@ -1269,7 +1399,10 @@ again:
                                         ref_index, name, 0);
        if (IS_ERR(di)) {
                ret = PTR_ERR(di);
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+"failed to lookup dir index item for dir %llu ref_index %llu name %.*s root %llu",
+                                      btrfs_ino(dir), ref_index, name->len,
+                                      name->name, btrfs_root_id(root));
                return ret;
        } else if (di) {
                ret = drop_one_dir_item(wc, dir, di);
@@ -1282,7 +1415,10 @@ again:
        di = btrfs_lookup_dir_item(trans, root, wc->subvol_path, btrfs_ino(dir), name, 0);
        if (IS_ERR(di)) {
                ret = PTR_ERR(di);
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+       "failed to lookup dir item for dir %llu name %.*s root %llu",
+                                      btrfs_ino(dir), name->len, name->name,
+                                      btrfs_root_id(root));
                return ret;
        } else if (di) {
                ret = drop_one_dir_item(wc, dir, di);
@@ -1344,7 +1480,6 @@ static int ref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
  */
 static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *inode)
 {
-       struct btrfs_trans_handle *trans = wc->trans;
        struct btrfs_root *root = wc->root;
        int ret;
        unsigned long ref_ptr;
@@ -1359,7 +1494,10 @@ again:
                goto out;
        }
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+              "failed to search subvolume tree for key (%llu %u %llu) root %llu",
+                                      wc->log_key.objectid, wc->log_key.type,
+                                      wc->log_key.offset, btrfs_root_id(root));
                goto out;
        }
 
@@ -1374,14 +1512,20 @@ again:
                        ret = extref_get_fields(eb, ref_ptr, &name,
                                                NULL, &parent_id);
                        if (ret) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+                              "failed to get extref details for inode %llu root %llu",
+                                                      btrfs_ino(inode),
+                                                      btrfs_root_id(root));
                                goto out;
                        }
                } else {
                        parent_id = wc->log_key.offset;
                        ret = ref_get_fields(eb, ref_ptr, &name, NULL);
                        if (ret) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+              "failed to get ref details for inode %llu parent_id %llu root %llu",
+                                                      btrfs_ino(inode), parent_id,
+                                                      btrfs_root_id(root));
                                goto out;
                        }
                }
@@ -1401,7 +1545,9 @@ again:
                        if (IS_ERR(dir)) {
                                ret = PTR_ERR(dir);
                                kfree(name.name);
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+                                      "failed to lookup dir inode %llu root %llu",
+                                                      parent_id, btrfs_root_id(root));
                                goto out;
                        }
                        ret = unlink_inode_for_log_replay(wc, dir, inode, &name);
@@ -1472,7 +1618,9 @@ static noinline int add_inode_ref(struct walk_control *wc)
                if (ret == -ENOENT)
                        ret = 0;
                else
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+                              "failed to lookup dir inode %llu root %llu",
+                                              parent_objectid, btrfs_root_id(root));
                dir = NULL;
                goto out;
        }
@@ -1480,7 +1628,9 @@ static noinline int add_inode_ref(struct walk_control *wc)
        inode = btrfs_iget_logging(inode_objectid, root);
        if (IS_ERR(inode)) {
                ret = PTR_ERR(inode);
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                                      "failed to lookup inode %llu root %llu",
+                                      inode_objectid, btrfs_root_id(root));
                inode = NULL;
                goto out;
        }
@@ -1490,7 +1640,10 @@ static noinline int add_inode_ref(struct walk_control *wc)
                        ret = extref_get_fields(wc->log_leaf, ref_ptr, &name,
                                                &ref_index, &parent_objectid);
                        if (ret) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+                              "failed to get extref details for inode %llu root %llu",
+                                                      btrfs_ino(inode),
+                                                      btrfs_root_id(root));
                                goto out;
                        }
                        /*
@@ -1518,7 +1671,10 @@ static noinline int add_inode_ref(struct walk_control *wc)
                                                ret = 0;
                                                goto next;
                                        } else {
-                                               btrfs_abort_transaction(trans, ret);
+                                               btrfs_abort_log_replay(wc, ret,
+                                      "failed to lookup dir inode %llu root %llu",
+                                                                      parent_objectid,
+                                                                      btrfs_root_id(root));
                                        }
                                        goto out;
                                }
@@ -1526,7 +1682,11 @@ static noinline int add_inode_ref(struct walk_control *wc)
                } else {
                        ret = ref_get_fields(wc->log_leaf, ref_ptr, &name, &ref_index);
                        if (ret) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+       "failed to get ref details for inode %llu parent_objectid %llu root %llu",
+                                                      btrfs_ino(inode),
+                                                      parent_objectid,
+                                                      btrfs_root_id(root));
                                goto out;
                        }
                }
@@ -1534,7 +1694,11 @@ static noinline int add_inode_ref(struct walk_control *wc)
                ret = inode_in_dir(root, wc->subvol_path, btrfs_ino(dir),
                                   btrfs_ino(inode), ref_index, &name);
                if (ret < 0) {
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+"failed to check if inode %llu is in dir %llu ref_index %llu name %.*s root %llu",
+                                              btrfs_ino(inode), btrfs_ino(dir),
+                                              ref_index, name.len, name.name,
+                                              btrfs_root_id(root));
                        goto out;
                } else if (ret == 0) {
                        /*
@@ -1554,13 +1718,21 @@ static noinline int add_inode_ref(struct walk_control *wc)
                        /* insert our name */
                        ret = btrfs_add_link(trans, dir, inode, &name, 0, ref_index);
                        if (ret) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+"failed to add link for inode %llu in dir %llu ref_index %llu name %.*s root %llu",
+                                                      btrfs_ino(inode),
+                                                      btrfs_ino(dir), ref_index,
+                                                      name.len, name.name,
+                                                      btrfs_root_id(root));
                                goto out;
                        }
 
                        ret = btrfs_update_inode(trans, inode);
                        if (ret) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+                                      "failed to update inode %llu root %llu",
+                                                      btrfs_ino(inode),
+                                                      btrfs_root_id(root));
                                goto out;
                        }
                }
@@ -1831,7 +2003,9 @@ static noinline int link_to_fixup_dir(struct walk_control *wc, u64 objectid)
        inode = btrfs_iget_logging(objectid, root);
        if (IS_ERR(inode)) {
                ret = PTR_ERR(inode);
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                                      "failed to lookup inode %llu root %llu",
+                                      objectid, btrfs_root_id(root));
                return ret;
        }
 
@@ -1850,11 +2024,15 @@ static noinline int link_to_fixup_dir(struct walk_control *wc, u64 objectid)
                        inc_nlink(vfs_inode);
                ret = btrfs_update_inode(trans, inode);
                if (ret)
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+                                      "failed to update inode %llu root %llu",
+                                              objectid, btrfs_root_id(root));
        } else if (ret == -EEXIST) {
                ret = 0;
        } else {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                      "failed to insert fixup item for inode %llu root %llu",
+                                      objectid, btrfs_root_id(root));
        }
        iput(vfs_inode);
 
@@ -1959,14 +2137,18 @@ static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_it
        dir = btrfs_iget_logging(wc->log_key.objectid, root);
        if (IS_ERR(dir)) {
                ret = PTR_ERR(dir);
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                                      "failed to lookup dir inode %llu root %llu",
+                                      wc->log_key.objectid, btrfs_root_id(root));
                return ret;
        }
 
        ret = read_alloc_one_name(wc->log_leaf, di + 1,
                                  btrfs_dir_name_len(wc->log_leaf, di), &name);
        if (ret) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                              "failed to allocate name for dir %llu root %llu",
+                                      btrfs_ino(dir), btrfs_root_id(root));
                goto out;
        }
 
@@ -1975,7 +2157,9 @@ static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_it
        ret = btrfs_lookup_inode(trans, root, wc->subvol_path, &log_key, 0);
        btrfs_release_path(wc->subvol_path);
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                                      "failed to lookup inode %llu root %llu",
+                                      log_key.objectid, btrfs_root_id(root));
                goto out;
        }
        exists = (ret == 0);
@@ -1985,13 +2169,19 @@ static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_it
                                           wc->log_key.objectid, &name, 1);
        if (IS_ERR(dir_dst_di)) {
                ret = PTR_ERR(dir_dst_di);
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                      "failed to lookup dir item for dir %llu name %.*s root %llu",
+                                      wc->log_key.objectid, name.len, name.name,
+                                      btrfs_root_id(root));
                goto out;
        } else if (dir_dst_di) {
                ret = delete_conflicting_dir_entry(wc, dir, dir_dst_di,
                                                   &log_key, log_flags, exists);
                if (ret < 0) {
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+              "failed to delete conflicting entry for dir %llu name %.*s root %llu",
+                                              btrfs_ino(dir), name.len, name.name,
+                                              btrfs_root_id(root));
                        goto out;
                }
                dir_dst_matches = (ret == 1);
@@ -2004,13 +2194,19 @@ static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_it
                                                   wc->log_key.offset, &name, 1);
        if (IS_ERR(index_dst_di)) {
                ret = PTR_ERR(index_dst_di);
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+              "failed to lookup dir index item for dir %llu name %.*s root %llu",
+                                      wc->log_key.objectid, name.len, name.name,
+                                      btrfs_root_id(root));
                goto out;
        } else if (index_dst_di) {
                ret = delete_conflicting_dir_entry(wc, dir, index_dst_di,
                                                   &log_key, log_flags, exists);
                if (ret < 0) {
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+              "failed to delete conflicting entry for dir %llu name %.*s root %llu",
+                                              btrfs_ino(dir), name.len, name.name,
+                                              btrfs_root_id(root));
                        goto out;
                }
                index_dst_matches = (ret == 1);
@@ -2033,7 +2229,10 @@ static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_it
        search_key.offset = wc->log_key.objectid;
        ret = backref_in_log(root->log_root, &search_key, 0, &name);
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+"failed to check if ref item is logged for inode %llu dir %llu name %.*s root %llu",
+                                      search_key.objectid, btrfs_ino(dir),
+                                      name.len, name.name, btrfs_root_id(root));
                goto out;
        } else if (ret) {
                /* The dentry will be added later. */
@@ -2047,7 +2246,10 @@ static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_it
        search_key.offset = btrfs_extref_hash(wc->log_key.objectid, name.name, name.len);
        ret = backref_in_log(root->log_root, &search_key, wc->log_key.objectid, &name);
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+"failed to check if extref item is logged for inode %llu dir %llu name %.*s root %llu",
+                                      search_key.objectid, btrfs_ino(dir),
+                                      name.len, name.name, btrfs_root_id(root));
                goto out;
        } else if (ret) {
                /* The dentry will be added later. */
@@ -2058,7 +2260,10 @@ static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_it
        ret = insert_one_name(trans, root, wc->log_key.objectid, wc->log_key.offset,
                              &name, &log_key);
        if (ret && ret != -ENOENT && ret != -EEXIST) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                      "failed to insert name %.*s for inode %llu dir %llu root %llu",
+                                      name.len, name.name, log_key.objectid,
+                                      btrfs_ino(dir), btrfs_root_id(root));
                goto out;
        }
        if (!ret)
@@ -2071,7 +2276,9 @@ out:
                btrfs_i_size_write(dir, dir->vfs_inode.i_size + name.len * 2);
                ret = btrfs_update_inode(trans, dir);
                if (ret)
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+                                      "failed to update dir inode %llu root %llu",
+                                              btrfs_ino(dir), btrfs_root_id(root));
        }
        kfree(name.name);
        iput(&dir->vfs_inode);
@@ -2246,7 +2453,10 @@ static noinline int check_item_in_log(struct walk_control *wc,
        di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
        ret = read_alloc_one_name(eb, di + 1, btrfs_dir_name_len(eb, di), &name);
        if (ret) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                      "failed to allocate name for dir %llu index %llu root %llu",
+                                      btrfs_ino(dir), dir_key->offset,
+                                      btrfs_root_id(root));
                goto out;
        }
 
@@ -2258,7 +2468,11 @@ static noinline int check_item_in_log(struct walk_control *wc,
                                                     dir_key->offset, &name, 0);
                if (IS_ERR(log_di)) {
                        ret = PTR_ERR(log_di);
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+       "failed to lookup dir index item for dir %llu index %llu name %.*s root %llu",
+                                              btrfs_ino(dir), dir_key->offset,
+                                              name.len, name.name,
+                                              btrfs_root_id(root));
                        goto out;
                } else if (log_di) {
                        /* The dentry exists in the log, we have nothing to do. */
@@ -2274,7 +2488,9 @@ static noinline int check_item_in_log(struct walk_control *wc,
        if (IS_ERR(inode)) {
                ret = PTR_ERR(inode);
                inode = NULL;
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                                      "failed to lookup inode %llu root %llu",
+                                      location.objectid, btrfs_root_id(root));
                goto out;
        }
 
@@ -2311,7 +2527,7 @@ static int replay_xattr_deletes(struct walk_control *wc)
 
        log_path = btrfs_alloc_path();
        if (!log_path) {
-               btrfs_abort_transaction(trans, -ENOMEM);
+               btrfs_abort_log_replay(wc, -ENOMEM, "failed to allocate path");
                return -ENOMEM;
        }
 
@@ -2321,7 +2537,9 @@ static int replay_xattr_deletes(struct walk_control *wc)
 again:
        ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);
        if (ret < 0) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                              "failed to search xattrs for inode %llu root %llu",
+                                      ino, btrfs_root_id(root));
                goto out;
        }
 process_leaf:
@@ -2351,7 +2569,9 @@ process_leaf:
                        name = kmalloc(name_len, GFP_NOFS);
                        if (!name) {
                                ret = -ENOMEM;
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+                                      "failed to allocate memory for name of length %u",
+                                                      name_len);
                                goto out;
                        }
                        read_extent_buffer(wc->subvol_path->nodes[0], name,
@@ -2365,29 +2585,41 @@ process_leaf:
                                btrfs_release_path(wc->subvol_path);
                                di = btrfs_lookup_xattr(trans, root, wc->subvol_path, ino,
                                                        name, name_len, -1);
-                               kfree(name);
                                if (IS_ERR(di)) {
                                        ret = PTR_ERR(di);
-                                       btrfs_abort_transaction(trans, ret);
+                                       btrfs_abort_log_replay(wc, ret,
+                      "failed to lookup xattr with name %.*s for inode %llu root %llu",
+                                                              name_len, name, ino,
+                                                              btrfs_root_id(root));
+                                       kfree(name);
                                        goto out;
                                }
                                ASSERT(di);
                                ret = btrfs_delete_one_dir_name(trans, root,
                                                                wc->subvol_path, di);
                                if (ret) {
-                                       btrfs_abort_transaction(trans, ret);
+                                       btrfs_abort_log_replay(wc, ret,
+                      "failed to delete xattr with name %.*s for inode %llu root %llu",
+                                                              name_len, name, ino,
+                                                              btrfs_root_id(root));
+                                       kfree(name);
                                        goto out;
                                }
                                btrfs_release_path(wc->subvol_path);
+                               kfree(name);
                                search_key = key;
                                goto again;
                        }
-                       kfree(name);
                        if (IS_ERR(log_di)) {
                                ret = PTR_ERR(log_di);
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+       "failed to lookup xattr in log tree with name %.*s for inode %llu root %llu",
+                                                      name_len, name, ino,
+                                                      btrfs_root_id(root));
+                               kfree(name);
                                goto out;
                        }
+                       kfree(name);
                        cur += this_len;
                        di = (struct btrfs_dir_item *)((char *)di + this_len);
                }
@@ -2398,7 +2630,9 @@ process_leaf:
        else if (ret == 0)
                goto process_leaf;
        else
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                              "failed to get next leaf in subvolume root %llu",
+                                      btrfs_root_id(root));
 out:
        btrfs_free_path(log_path);
        btrfs_release_path(wc->subvol_path);
@@ -2419,7 +2653,6 @@ out:
 static noinline int replay_dir_deletes(struct walk_control *wc,
                                       u64 dirid, bool del_all)
 {
-       struct btrfs_trans_handle *trans = wc->trans;
        struct btrfs_root *root = wc->root;
        struct btrfs_root *log = (del_all ? NULL : wc->log);
        u64 range_start;
@@ -2434,7 +2667,7 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
        dir_key.type = BTRFS_DIR_INDEX_KEY;
        log_path = btrfs_alloc_path();
        if (!log_path) {
-               btrfs_abort_transaction(trans, -ENOMEM);
+               btrfs_abort_log_replay(wc, -ENOMEM, "failed to allocate path");
                return -ENOMEM;
        }
 
@@ -2449,7 +2682,9 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
                if (ret == -ENOENT)
                        ret = 0;
                else
-                       btrfs_abort_transaction(trans, ret);
+                       btrfs_abort_log_replay(wc, ret,
+                              "failed to lookup dir inode %llu root %llu",
+                                              dirid, btrfs_root_id(root));
                return ret;
        }
 
@@ -2462,7 +2697,9 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
                        ret = find_dir_range(log, wc->subvol_path, dirid,
                                             &range_start, &range_end);
                        if (ret < 0) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+                              "failed to find range for dir %llu in log tree root %llu",
+                                                      dirid, btrfs_root_id(root));
                                goto out;
                        } else if (ret > 0) {
                                break;
@@ -2475,7 +2712,11 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
                        ret = btrfs_search_slot(NULL, root, &dir_key,
                                                wc->subvol_path, 0, 0);
                        if (ret < 0) {
-                               btrfs_abort_transaction(trans, ret);
+                               btrfs_abort_log_replay(wc, ret,
+                              "failed to search root %llu for key (%llu %u %llu)",
+                                                      btrfs_root_id(root),
+                                                      dir_key.objectid, dir_key.type,
+                                                      dir_key.offset);
                                goto out;
                        }
 
@@ -2485,7 +2726,9 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
                                if (ret == 1) {
                                        break;
                                } else if (ret < 0) {
-                                       btrfs_abort_transaction(trans, ret);
+                                       btrfs_abort_log_replay(wc, ret,
+                                      "failed to get next leaf in subvolume root %llu",
+                                                              btrfs_root_id(root));
                                        goto out;
                                }
                        }
@@ -2546,16 +2789,23 @@ static int replay_one_buffer(struct extent_buffer *eb,
        if (level != 0)
                return 0;
 
+       /*
+        * Set to NULL since it was not yet read and in case we abort log replay
+        * on error, we have no valid log tree leaf to dump.
+        */
+       wc->log_leaf = NULL;
        ret = btrfs_read_extent_buffer(eb, &check);
        if (ret) {
-               btrfs_abort_transaction(trans, ret);
+               btrfs_abort_log_replay(wc, ret,
+                      "failed to read log tree leaf %llu for root %llu",
+                                      eb->start, btrfs_root_id(root));
                return ret;
        }
 
        ASSERT(wc->subvol_path == NULL);
        wc->subvol_path = btrfs_alloc_path();
        if (!wc->subvol_path) {
-               btrfs_abort_transaction(trans, -ENOMEM);
+               btrfs_abort_log_replay(wc, -ENOMEM, "failed to allocate path");
                return -ENOMEM;
        }
 
@@ -2631,7 +2881,10 @@ static int replay_one_buffer(struct extent_buffer *eb,
                                inode = btrfs_iget_logging(wc->log_key.objectid, root);
                                if (IS_ERR(inode)) {
                                        ret = PTR_ERR(inode);
-                                       btrfs_abort_transaction(trans, ret);
+                                       btrfs_abort_log_replay(wc, ret,
+                                              "failed to lookup inode %llu root %llu",
+                                                              wc->log_key.objectid,
+                                                              btrfs_root_id(root));
                                        break;
                                }
                                from = ALIGN(i_size_read(&inode->vfs_inode),
@@ -2642,14 +2895,21 @@ static int replay_one_buffer(struct extent_buffer *eb,
                                drop_args.path = wc->subvol_path;
                                ret = btrfs_drop_extents(trans, root, inode,  &drop_args);
                                if (ret) {
-                                       btrfs_abort_transaction(trans, ret);
+                                       btrfs_abort_log_replay(wc, ret,
+                      "failed to drop extents for inode %llu root %llu offset %llu",
+                                                              btrfs_ino(inode),
+                                                              btrfs_root_id(root),
+                                                              from);
                                } else {
                                        inode_sub_bytes(&inode->vfs_inode,
                                                        drop_args.bytes_found);
                                        /* Update the inode's nbytes. */
                                        ret = btrfs_update_inode(trans, inode);
                                        if (ret)
-                                               btrfs_abort_transaction(trans, ret);
+                                               btrfs_abort_log_replay(wc, ret,
+                                              "failed to update inode %llu root %llu",
+                                                                      btrfs_ino(inode),
+                                                                      btrfs_root_id(root));
                                }
                                iput(&inode->vfs_inode);
                                if (ret)