summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2025-09-05 12:58:19 +0100
committerDavid Sterba <dsterba@suse.com>2025-09-23 08:49:21 +0200
commit2753e49176240f21e4bb10e03514f99e732704bb (patch)
tree0d719e4d60f4b483880ab77baedc23d1e6a1be85
parent5a0565cad3ef7cbf4cf43d1dd1e849b156205292 (diff)
btrfs: dump detailed info and specific messages on log replay failures
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>
-rw-r--r--fs/btrfs/fs.h2
-rw-r--r--fs/btrfs/messages.c1
-rw-r--r--fs/btrfs/tree-log.c432
3 files changed, 349 insertions, 86 deletions
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index bf4a1b75b0cf..ba63a40b2bde 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -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
diff --git a/fs/btrfs/messages.c b/fs/btrfs/messages.c
index 363fd28c0268..a0cf8effe008 100644
--- a/fs/btrfs/messages.c
+++ b/fs/btrfs/messages.c
@@ -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',
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 83b79023baae..144b12725365 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -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)