* [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes
@ 2025-09-05 16:09 fdmanana
2025-09-05 16:09 ` [PATCH 01/33] btrfs: fix invalid extref key setup when replaying dentry fdmanana
` (33 more replies)
0 siblings, 34 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
The following are a bug fix for an extref key offset computation, several
cleanups to eliminate duplicated or not needed code, memory allocations
and preparation work for the final change which is to make log replay
failures dump contextual information useful to help debug failures during
log replay. Details in the change logs.
Filipe Manana (33):
btrfs: fix invalid extref key setup when replaying dentry
btrfs: use booleans in walk control structure for log replay
btrfs: rename replay_dest member of struct walk_control to root
btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree()
btrfs: add and use a log root field to struct walk_control
btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees()
btrfs: stop passing transaction parameter to log tree walk functions
btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees()
btrfs: always drop log root tree reference in btrfs_replay_log()
btrfs: pass walk_control structure to replay_xattr_deletes()
btrfs: move up the definition of struct walk_control
btrfs: pass walk_control structure to replay_dir_deletes()
btrfs: pass walk_control structure to check_item_in_log()
btrfs: pass walk_control structure to replay_one_extent()
btrfs: pass walk_control structure to add_inode_ref() and helpers
btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name()
btrfs: pass walk_control structure to drop_one_dir_item() and helpers
btrfs: pass walk_control structure to replay_one_extent()
btrfs: use level argument in log tree walk callback process_one_buffer()
btrfs: use level argument in log tree walk callback replay_one_buffer()
btrfs: use the inode item boolean everywhere in overwrite_item()
btrfs: add current log leaf, key and slot to struct walk_control
btrfs: avoid unnecessary path allocation at fixup_inode_link_count()
btrfs: avoid path allocations when dropping extents during log replay
btrfs: avoid unnecessary path allocation when replaying a dir item
btrfs: remove redundant path release when processing dentry during log replay
btrfs: remove redundant path release when overwriting item during log replay
btrfs: add path for subvolume tree changes to struct walk_control
btrfs: stop passing inode object IDs to __add_inode_ref() in log replay
btrfs: remove pointless inode lookup when processing extrefs during log replay
btrfs: abort transaction if we fail to find dir item during log replay
btrfs: abort transaction if we fail to update inode in log replay dir fixup
btrfs: dump detailed info and specific messages on log replay failures
fs/btrfs/disk-io.c | 2 +-
fs/btrfs/fs.h | 2 +
fs/btrfs/tree-log.c | 1292 +++++++++++++++++++++++++------------------
3 files changed, 762 insertions(+), 534 deletions(-)
--
2.47.2
^ permalink raw reply [flat|nested] 72+ messages in thread
* [PATCH 01/33] btrfs: fix invalid extref key setup when replaying dentry
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 02/33] btrfs: use booleans in walk control structure for log replay fdmanana
` (32 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
The offset for an extref item's key is not the object ID of the parent
dir, otherwise we would not need the extref item and would use plain ref
items. Instead the offset is the result of a hash computation that uses
the object ID of the parent dir and the name associated to the entry.
So fix this by setting the key offset at replay_one_name() to be the
result of calling btrfs_extref_hash().
Fixes: 725af92a6251 ("btrfs: Open-code name_in_log_ref in replay_one_name")
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index b91cc7ac28d8..861f96ef28cf 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2058,7 +2058,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
search_key.objectid = log_key.objectid;
search_key.type = BTRFS_INODE_EXTREF_KEY;
- search_key.offset = key->objectid;
+ search_key.offset = btrfs_extref_hash(key->objectid, name.name, name.len);
ret = backref_in_log(root->log_root, &search_key, key->objectid, &name);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 02/33] btrfs: use booleans in walk control structure for log replay
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
2025-09-05 16:09 ` [PATCH 01/33] btrfs: fix invalid extref key setup when replaying dentry fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 03/33] btrfs: rename replay_dest member of struct walk_control to root fdmanana
` (31 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
The 'free' and 'pin' member of struct walk_control, used during log replay
and when freeing a log tree, are defined as integers but in practice are
used as booleans. Change their type to bool and while at it update their
comments to be more detailed and comply with the preferred comment style
(first word in a sentence is capitalized, sentences end with punctuation
and the comment opening (/*) is on a line of its own).
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 861f96ef28cf..c5c5fc05eabb 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -306,15 +306,20 @@ void btrfs_end_log_trans(struct btrfs_root *root)
* are state fields used for that specific part
*/
struct walk_control {
- /* should we free the extent on disk when done? This is used
- * at transaction commit time while freeing a log tree
+ /*
+ * Signal that we are freeing the metadata extents of a log tree.
+ * This is used at transaction commit time while freeing a log tree.
*/
- int free;
+ bool free;
- /* pin only walk, we record which extents on disk belong to the
- * log trees
+ /*
+ * Signal that we are pinning the metadata extents of a log tree and the
+ * data extents its leaves point to (if using mixed block groups).
+ * This happens in the first stage of log replay to ensure that during
+ * replay, while we are modifying subvolume trees, we don't overwrite
+ * the metadata extents of log trees.
*/
- int pin;
+ bool pin;
/* what stage of the replay code we're currently in */
int stage;
@@ -3415,7 +3420,7 @@ static void free_log_tree(struct btrfs_trans_handle *trans,
{
int ret;
struct walk_control wc = {
- .free = 1,
+ .free = true,
.process_func = process_one_buffer
};
@@ -7433,7 +7438,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
}
wc.trans = trans;
- wc.pin = 1;
+ wc.pin = true;
ret = walk_log_tree(trans, log_root_tree, &wc);
if (ret) {
@@ -7557,7 +7562,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
/* step one is to pin it all, step two is to replay just inodes */
if (wc.pin) {
- wc.pin = 0;
+ wc.pin = false;
wc.process_func = replay_one_buffer;
wc.stage = LOG_WALK_REPLAY_INODES;
goto again;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 03/33] btrfs: rename replay_dest member of struct walk_control to root
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
2025-09-05 16:09 ` [PATCH 01/33] btrfs: fix invalid extref key setup when replaying dentry fdmanana
2025-09-05 16:09 ` [PATCH 02/33] btrfs: use booleans in walk control structure for log replay fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 04/33] btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree() fdmanana
` (30 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Everywhere else we refer to a subvolume root we are replaying to simply
as 'root', so rename from 'replay_dest' to 'root' for consistency and
having a more meaningful and shorter name. While at it also update the
comment to be more detailed and comply to preferred style (first word in
a sentence is capitalized and sentence ends with punctuation).
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 32 +++++++++++++++++---------------
1 file changed, 17 insertions(+), 15 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index c5c5fc05eabb..c0cc94efbcaa 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -330,8 +330,11 @@ struct walk_control {
*/
bool ignore_cur_inode;
- /* the root we are currently replaying */
- struct btrfs_root *replay_dest;
+ /*
+ * The root we are currently replaying to. This is NULL for the replay
+ * stage LOG_WALK_PIN_ONLY.
+ */
+ struct btrfs_root *root;
/* the trans handle for the current replay */
struct btrfs_trans_handle *trans;
@@ -2575,7 +2578,7 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
.level = level
};
struct btrfs_path *path;
- struct btrfs_root *root = wc->replay_dest;
+ struct btrfs_root *root = wc->root;
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_key key;
int i;
@@ -7479,11 +7482,10 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
goto error;
}
- wc.replay_dest = btrfs_get_fs_root(fs_info, found_key.offset,
- true);
- if (IS_ERR(wc.replay_dest)) {
- ret = PTR_ERR(wc.replay_dest);
- wc.replay_dest = NULL;
+ wc.root = btrfs_get_fs_root(fs_info, found_key.offset, true);
+ if (IS_ERR(wc.root)) {
+ ret = PTR_ERR(wc.root);
+ wc.root = NULL;
if (ret != -ENOENT) {
btrfs_put_root(log);
btrfs_abort_transaction(trans, ret);
@@ -7510,8 +7512,8 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
goto next;
}
- wc.replay_dest->log_root = log;
- ret = btrfs_record_root_in_trans(trans, wc.replay_dest);
+ wc.root->log_root = log;
+ ret = btrfs_record_root_in_trans(trans, wc.root);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
@@ -7524,9 +7526,9 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
}
if (wc.stage == LOG_WALK_REPLAY_ALL) {
- struct btrfs_root *root = wc.replay_dest;
+ struct btrfs_root *root = wc.root;
- ret = fixup_inode_link_counts(trans, wc.replay_dest, path);
+ ret = fixup_inode_link_counts(trans, root, path);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
@@ -7546,9 +7548,9 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
}
}
next:
- if (wc.replay_dest) {
- wc.replay_dest->log_root = NULL;
- btrfs_put_root(wc.replay_dest);
+ if (wc.root) {
+ wc.root->log_root = NULL;
+ btrfs_put_root(wc.root);
}
btrfs_put_root(log);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 04/33] btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (2 preceding siblings ...)
2025-09-05 16:09 ` [PATCH 03/33] btrfs: rename replay_dest member of struct walk_control to root fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 05/33] btrfs: add and use a log root field to struct walk_control fdmanana
` (29 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Everywhere we have a log root we name it as 'log' or 'log_root' except in
walk_down_log_tree() and walk_up_log_tree() where we name it as 'root',
which not only it's inconsistent, it's also confusing since we typically
use 'root' when naming variables that refer to a subvolume tree. So for
clairty and consistency rename the 'root' argument to 'log'.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index c0cc94efbcaa..321ec0de0733 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2779,11 +2779,11 @@ static int clean_log_buffer(struct btrfs_trans_handle *trans,
}
static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+ struct btrfs_root *log,
struct btrfs_path *path, int *level,
struct walk_control *wc)
{
- struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_fs_info *fs_info = log->fs_info;
u64 bytenr;
u64 ptr_gen;
struct extent_buffer *next;
@@ -2821,8 +2821,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
}
if (*level == 1) {
- ret = wc->process_func(root, next, wc, ptr_gen,
- *level - 1);
+ ret = wc->process_func(log, next, wc, ptr_gen, *level - 1);
if (ret) {
free_extent_buffer(next);
return ret;
@@ -2873,7 +2872,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
}
static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+ struct btrfs_root *log,
struct btrfs_path *path, int *level,
struct walk_control *wc)
{
@@ -2889,7 +2888,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
WARN_ON(*level == 0);
return 0;
} else {
- ret = wc->process_func(root, path->nodes[*level], wc,
+ ret = wc->process_func(log, path->nodes[*level], wc,
btrfs_header_generation(path->nodes[*level]),
*level);
if (ret)
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 05/33] btrfs: add and use a log root field to struct walk_control
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (3 preceding siblings ...)
2025-09-05 16:09 ` [PATCH 04/33] btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree() fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 06/33] btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees() fdmanana
` (28 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing an extra log root parameter for the log tree walk
functions and callbacks, add the log tree to struct walk_control and
make those functions and callbacks extract the log root from that
structure, reducing the number of parameters. This also simplifies
further upcoming changes to report log tree replay failures.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 62 +++++++++++++++++++++++++--------------------
1 file changed, 35 insertions(+), 27 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 321ec0de0733..7d1b21df698d 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -336,6 +336,9 @@ struct walk_control {
*/
struct btrfs_root *root;
+ /* The log tree we are currently processing (not NULL for any stage). */
+ struct btrfs_root *log;
+
/* the trans handle for the current replay */
struct btrfs_trans_handle *trans;
@@ -344,17 +347,17 @@ struct walk_control {
* passed in, and it must be checked or read if you need the data
* inside it
*/
- int (*process_func)(struct btrfs_root *log, struct extent_buffer *eb,
+ int (*process_func)(struct extent_buffer *eb,
struct walk_control *wc, u64 gen, int level);
};
/*
* process_func used to pin down extents, write them or wait on them
*/
-static int process_one_buffer(struct btrfs_root *log,
- struct extent_buffer *eb,
+static int process_one_buffer(struct extent_buffer *eb,
struct walk_control *wc, u64 gen, int level)
{
+ struct btrfs_root *log = wc->log;
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_fs_info *fs_info = log->fs_info;
int ret = 0;
@@ -2569,7 +2572,7 @@ static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,
* only in the log (references come from either directory items or inode
* back refs).
*/
-static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
+static int replay_one_buffer(struct extent_buffer *eb,
struct walk_control *wc, u64 gen, int level)
{
int nritems;
@@ -2579,6 +2582,7 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
};
struct btrfs_path *path;
struct btrfs_root *root = wc->root;
+ struct btrfs_root *log = wc->log;
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_key key;
int i;
@@ -2779,11 +2783,10 @@ static int clean_log_buffer(struct btrfs_trans_handle *trans,
}
static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *log,
struct btrfs_path *path, int *level,
struct walk_control *wc)
{
- struct btrfs_fs_info *fs_info = log->fs_info;
+ struct btrfs_fs_info *fs_info = wc->log->fs_info;
u64 bytenr;
u64 ptr_gen;
struct extent_buffer *next;
@@ -2821,7 +2824,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
}
if (*level == 1) {
- ret = wc->process_func(log, next, wc, ptr_gen, *level - 1);
+ ret = wc->process_func(next, wc, ptr_gen, *level - 1);
if (ret) {
free_extent_buffer(next);
return ret;
@@ -2872,7 +2875,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
}
static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *log,
struct btrfs_path *path, int *level,
struct walk_control *wc)
{
@@ -2888,7 +2890,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
WARN_ON(*level == 0);
return 0;
} else {
- ret = wc->process_func(log, path->nodes[*level], wc,
+ ret = wc->process_func(path->nodes[*level], wc,
btrfs_header_generation(path->nodes[*level]),
*level);
if (ret)
@@ -2912,9 +2914,9 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
* the tree freeing any blocks that have a ref count of zero after being
* decremented.
*/
-static int walk_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *log, struct walk_control *wc)
+static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *wc)
{
+ struct btrfs_root *log = wc->log;
int ret = 0;
int wret;
int level;
@@ -2932,7 +2934,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
path->slots[level] = 0;
while (1) {
- wret = walk_down_log_tree(trans, log, path, &level, wc);
+ wret = walk_down_log_tree(trans, path, &level, wc);
if (wret > 0)
break;
if (wret < 0) {
@@ -2940,7 +2942,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
goto out;
}
- wret = walk_up_log_tree(trans, log, path, &level, wc);
+ wret = walk_up_log_tree(trans, path, &level, wc);
if (wret > 0)
break;
if (wret < 0) {
@@ -2951,7 +2953,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
/* was the root node processed? if not, catch it here */
if (path->nodes[orig_level]) {
- ret = wc->process_func(log, path->nodes[orig_level], wc,
+ ret = wc->process_func(path->nodes[orig_level], wc,
btrfs_header_generation(path->nodes[orig_level]),
orig_level);
if (ret)
@@ -3423,11 +3425,12 @@ static void free_log_tree(struct btrfs_trans_handle *trans,
int ret;
struct walk_control wc = {
.free = true,
- .process_func = process_one_buffer
+ .process_func = process_one_buffer,
+ .log = log,
};
if (log->node) {
- ret = walk_log_tree(trans, log, &wc);
+ ret = walk_log_tree(trans, &wc);
if (ret) {
/*
* We weren't able to traverse the entire log tree, the
@@ -7441,8 +7444,10 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
wc.trans = trans;
wc.pin = true;
+ wc.log = log_root_tree;
- ret = walk_log_tree(trans, log_root_tree, &wc);
+ ret = walk_log_tree(trans, &wc);
+ wc.log = NULL;
if (ret) {
btrfs_abort_transaction(trans, ret);
goto error;
@@ -7454,7 +7459,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
key.offset = (u64)-1;
while (1) {
- struct btrfs_root *log;
struct btrfs_key found_key;
ret = btrfs_search_slot(NULL, log_root_tree, &key, path, 0, 0);
@@ -7474,9 +7478,10 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
if (found_key.objectid != BTRFS_TREE_LOG_OBJECTID)
break;
- log = btrfs_read_tree_root(log_root_tree, &found_key);
- if (IS_ERR(log)) {
- ret = PTR_ERR(log);
+ wc.log = btrfs_read_tree_root(log_root_tree, &found_key);
+ if (IS_ERR(wc.log)) {
+ ret = PTR_ERR(wc.log);
+ wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
@@ -7486,7 +7491,8 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
ret = PTR_ERR(wc.root);
wc.root = NULL;
if (ret != -ENOENT) {
- btrfs_put_root(log);
+ btrfs_put_root(wc.log);
+ wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
@@ -7502,23 +7508,24 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
* block from being modified, and we'll just bail for
* each subsequent pass.
*/
- ret = btrfs_pin_extent_for_log_replay(trans, log->node);
+ ret = btrfs_pin_extent_for_log_replay(trans, wc.log->node);
if (ret) {
- btrfs_put_root(log);
+ btrfs_put_root(wc.log);
+ wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
goto next;
}
- wc.root->log_root = log;
+ wc.root->log_root = wc.log;
ret = btrfs_record_root_in_trans(trans, wc.root);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
}
- ret = walk_log_tree(trans, log, &wc);
+ ret = walk_log_tree(trans, &wc);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
@@ -7551,7 +7558,8 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
wc.root->log_root = NULL;
btrfs_put_root(wc.root);
}
- btrfs_put_root(log);
+ btrfs_put_root(wc.log);
+ wc.log = NULL;
if (ret)
goto error;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 06/33] btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (4 preceding siblings ...)
2025-09-05 16:09 ` [PATCH 05/33] btrfs: add and use a log root field to struct walk_control fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 07/33] btrfs: stop passing transaction parameter to log tree walk functions fdmanana
` (27 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of duplicating the dropping of a log tree in case we jump to the
'error' label, move the dropping under the 'error' label and get rid of the
the unnecessary setting of the log root to NULL since we return immediately
after.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 7d1b21df698d..f039f0613a38 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -7491,8 +7491,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
ret = PTR_ERR(wc.root);
wc.root = NULL;
if (ret != -ENOENT) {
- btrfs_put_root(wc.log);
- wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
@@ -7510,8 +7508,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
*/
ret = btrfs_pin_extent_for_log_replay(trans, wc.log->node);
if (ret) {
- btrfs_put_root(wc.log);
- wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
@@ -7597,6 +7593,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
error:
if (wc.trans)
btrfs_end_transaction(wc.trans);
+ btrfs_put_root(wc.log);
clear_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags);
btrfs_free_path(path);
return ret;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 07/33] btrfs: stop passing transaction parameter to log tree walk functions
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (5 preceding siblings ...)
2025-09-05 16:09 ` [PATCH 06/33] btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees() fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 08/33] btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees() fdmanana
` (26 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
It's unncessary to pass a transaction parameter since struct walk_control
already has a member that points to the transaction, so we can make the
functions access the structure.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index f039f0613a38..dee306101d8e 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2782,10 +2782,10 @@ static int clean_log_buffer(struct btrfs_trans_handle *trans,
return 0;
}
-static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_path *path, int *level,
- struct walk_control *wc)
+static noinline int walk_down_log_tree(struct btrfs_path *path, int *level,
+ struct walk_control *wc)
{
+ struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_fs_info *fs_info = wc->log->fs_info;
u64 bytenr;
u64 ptr_gen;
@@ -2874,9 +2874,8 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
return 0;
}
-static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_path *path, int *level,
- struct walk_control *wc)
+static noinline int walk_up_log_tree(struct btrfs_path *path, int *level,
+ struct walk_control *wc)
{
int i;
int slot;
@@ -2897,7 +2896,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
return ret;
if (wc->free) {
- ret = clean_log_buffer(trans, path->nodes[*level]);
+ ret = clean_log_buffer(wc->trans, path->nodes[*level]);
if (ret)
return ret;
}
@@ -2914,7 +2913,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
* the tree freeing any blocks that have a ref count of zero after being
* decremented.
*/
-static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *wc)
+static int walk_log_tree(struct walk_control *wc)
{
struct btrfs_root *log = wc->log;
int ret = 0;
@@ -2934,7 +2933,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *
path->slots[level] = 0;
while (1) {
- wret = walk_down_log_tree(trans, path, &level, wc);
+ wret = walk_down_log_tree(path, &level, wc);
if (wret > 0)
break;
if (wret < 0) {
@@ -2942,7 +2941,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *
goto out;
}
- wret = walk_up_log_tree(trans, path, &level, wc);
+ wret = walk_up_log_tree(path, &level, wc);
if (wret > 0)
break;
if (wret < 0) {
@@ -2959,7 +2958,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *
if (ret)
goto out;
if (wc->free)
- ret = clean_log_buffer(trans, path->nodes[orig_level]);
+ ret = clean_log_buffer(wc->trans, path->nodes[orig_level]);
}
out:
@@ -3427,10 +3426,11 @@ static void free_log_tree(struct btrfs_trans_handle *trans,
.free = true,
.process_func = process_one_buffer,
.log = log,
+ .trans = trans,
};
if (log->node) {
- ret = walk_log_tree(trans, &wc);
+ ret = walk_log_tree(&wc);
if (ret) {
/*
* We weren't able to traverse the entire log tree, the
@@ -7446,7 +7446,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
wc.pin = true;
wc.log = log_root_tree;
- ret = walk_log_tree(trans, &wc);
+ ret = walk_log_tree(&wc);
wc.log = NULL;
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -7521,7 +7521,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
goto next;
}
- ret = walk_log_tree(trans, &wc);
+ ret = walk_log_tree(&wc);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 08/33] btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (6 preceding siblings ...)
2025-09-05 16:09 ` [PATCH 07/33] btrfs: stop passing transaction parameter to log tree walk functions fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 09/33] btrfs: always drop log root tree reference in btrfs_replay_log() fdmanana
` (25 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
There's no point in setting log_root_tree->log_root to NULL as this is
already NULL, we never assigned anything to it before and it's meaningless
as a log root never has a value other than NULL for the ->log_root field,
that can be not NULL only for non log roots.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index dee306101d8e..ab2f6bab096b 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -7585,7 +7585,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
if (ret)
return ret;
- log_root_tree->log_root = NULL;
clear_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags);
btrfs_put_root(log_root_tree);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 09/33] btrfs: always drop log root tree reference in btrfs_replay_log()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (7 preceding siblings ...)
2025-09-05 16:09 ` [PATCH 08/33] btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees() fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 10/33] btrfs: pass walk_control structure to replay_xattr_deletes() fdmanana
` (24 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Currently we have this odd behaviour:
1) At btrfs_replay_log() we drop the reference of the log root tree if
the call to btrfs_recover_log_trees() failed;
2) But if the call to btrfs_recover_log_trees() did not fail, we don't
drop the reference in btrfs_replay_log() - we expect that
btrfs_recover_log_trees() does it in case it returns success.
Let's simplify this and make btrfs_replay_log() always drop the reference
on the log root tree, not only this simplifies code as it's what makes
sense since it's btrfs_replay_log() who grabbed the reference in the first
place.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/disk-io.c | 2 +-
fs/btrfs/tree-log.c | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 7b06bbc40898..8dbb6a12ec24 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -2088,10 +2088,10 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info,
/* returns with log_tree_root freed on success */
ret = btrfs_recover_log_trees(log_tree_root);
+ btrfs_put_root(log_tree_root);
if (ret) {
btrfs_handle_fs_error(fs_info, ret,
"Failed to recover log tree");
- btrfs_put_root(log_tree_root);
return ret;
}
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index ab2f6bab096b..4d34aee0cafa 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -7586,7 +7586,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
return ret;
clear_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags);
- btrfs_put_root(log_root_tree);
return 0;
error:
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 10/33] btrfs: pass walk_control structure to replay_xattr_deletes()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (8 preceding siblings ...)
2025-09-05 16:09 ` [PATCH 09/33] btrfs: always drop log root tree reference in btrfs_replay_log() fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:09 ` [PATCH 11/33] btrfs: move up the definition of struct walk_control fdmanana
` (23 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction, subvolume root and log tree as
arguments to replay_xattr_deletes(), pass the walk_control structure as
we can grab all of those from the structure. This reduces the number of
arguments passed and it's going to be needed by an incoming change that
improves error reporting for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 4d34aee0cafa..cd4c5ae3e0a3 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2336,12 +2336,13 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
return ret;
}
-static int replay_xattr_deletes(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_root *log,
- struct btrfs_path *path,
- const u64 ino)
+static int replay_xattr_deletes(struct walk_control *wc,
+ struct btrfs_path *path,
+ const u64 ino)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
+ struct btrfs_root *log = wc->log;
struct btrfs_key search_key;
struct btrfs_path *log_path;
int i;
@@ -2645,7 +2646,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
wc->stage == LOG_WALK_REPLAY_INODES) {
u32 mode;
- ret = replay_xattr_deletes(trans, root, log, path, key.objectid);
+ ret = replay_xattr_deletes(wc, path, key.objectid);
if (ret)
break;
mode = btrfs_inode_mode(eb, inode_item);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 11/33] btrfs: move up the definition of struct walk_control
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (9 preceding siblings ...)
2025-09-05 16:09 ` [PATCH 10/33] btrfs: pass walk_control structure to replay_xattr_deletes() fdmanana
@ 2025-09-05 16:09 ` fdmanana
2025-09-05 16:10 ` [PATCH 12/33] btrfs: pass walk_control structure to replay_dir_deletes() fdmanana
` (22 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:09 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
In upcoming changes we need to pass struct walk_control as an argument to
replay_dir_deletes() and link_to_fixup_dir() so we need to move its
definition above the prototypes of those functions. So move it up right
below the enum that defines log replay stages and before any functions and
function prototypes are declared. Also fixup the comments while moving it
so that they comply with the preferred code style (capitalize the first
word in a sentence, end sentences with punctuation, makes lines wider and
closer to the 80 characters limit).
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 103 ++++++++++++++++++++++----------------------
1 file changed, 51 insertions(+), 52 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index cd4c5ae3e0a3..2780f0e1db01 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -101,6 +101,57 @@ enum {
LOG_WALK_REPLAY_ALL,
};
+/*
+ * The walk control struct is used to pass state down the chain when processing
+ * the log tree. The stage field tells us which part of the log tree processing
+ * we are currently doing.
+ */
+struct walk_control {
+ /*
+ * Signal that we are freeing the metadata extents of a log tree.
+ * This is used at transaction commit time while freeing a log tree.
+ */
+ bool free;
+
+ /*
+ * Signal that we are pinning the metadata extents of a log tree and the
+ * data extents its leaves point to (if using mixed block groups).
+ * This happens in the first stage of log replay to ensure that during
+ * replay, while we are modifying subvolume trees, we don't overwrite
+ * the metadata extents of log trees.
+ */
+ bool pin;
+
+ /* What stage of the replay code we're currently in. */
+ int stage;
+
+ /*
+ * Ignore any items from the inode currently being processed. Needs
+ * to be set every time we find a BTRFS_INODE_ITEM_KEY.
+ */
+ bool ignore_cur_inode;
+
+ /*
+ * The root we are currently replaying to. This is NULL for the replay
+ * stage LOG_WALK_PIN_ONLY.
+ */
+ struct btrfs_root *root;
+
+ /* The log tree we are currently processing (not NULL for any stage). */
+ struct btrfs_root *log;
+
+ /* The transaction handle used for replaying all log trees. */
+ struct btrfs_trans_handle *trans;
+
+ /*
+ * The function that gets used to process blocks we find in the tree.
+ * Note the extent_buffer might not be up to date when it is passed in,
+ * and it must be checked or read if you need the data inside it.
+ */
+ int (*process_func)(struct extent_buffer *eb,
+ struct walk_control *wc, u64 gen, int level);
+};
+
static int btrfs_log_inode(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode,
int inode_only,
@@ -299,58 +350,6 @@ void btrfs_end_log_trans(struct btrfs_root *root)
}
}
-/*
- * the walk control struct is used to pass state down the chain when
- * processing the log tree. The stage field tells us which part
- * of the log tree processing we are currently doing. The others
- * are state fields used for that specific part
- */
-struct walk_control {
- /*
- * Signal that we are freeing the metadata extents of a log tree.
- * This is used at transaction commit time while freeing a log tree.
- */
- bool free;
-
- /*
- * Signal that we are pinning the metadata extents of a log tree and the
- * data extents its leaves point to (if using mixed block groups).
- * This happens in the first stage of log replay to ensure that during
- * replay, while we are modifying subvolume trees, we don't overwrite
- * the metadata extents of log trees.
- */
- bool pin;
-
- /* what stage of the replay code we're currently in */
- int stage;
-
- /*
- * Ignore any items from the inode currently being processed. Needs
- * to be set every time we find a BTRFS_INODE_ITEM_KEY.
- */
- bool ignore_cur_inode;
-
- /*
- * The root we are currently replaying to. This is NULL for the replay
- * stage LOG_WALK_PIN_ONLY.
- */
- struct btrfs_root *root;
-
- /* The log tree we are currently processing (not NULL for any stage). */
- struct btrfs_root *log;
-
- /* the trans handle for the current replay */
- struct btrfs_trans_handle *trans;
-
- /* the function that gets used to process blocks we find in the
- * tree. Note the extent_buffer might not be up to date when it is
- * passed in, and it must be checked or read if you need the data
- * inside it
- */
- int (*process_func)(struct extent_buffer *eb,
- struct walk_control *wc, u64 gen, int level);
-};
-
/*
* process_func used to pin down extents, write them or wait on them
*/
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 12/33] btrfs: pass walk_control structure to replay_dir_deletes()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (10 preceding siblings ...)
2025-09-05 16:09 ` [PATCH 11/33] btrfs: move up the definition of struct walk_control fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 13/33] btrfs: pass walk_control structure to check_item_in_log() fdmanana
` (21 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction, subvolume root and log tree as
arguments to replay_dir_deletes(), pass the walk_control structure as
we can grab all of those from the structure. This reduces the number of
arguments passed and it's going to be needed by an incoming change that
improves error reporting for log replay. This also requires changing
fixup_inode_link_counts() and fixup_inode_link_count() to take that
structure as an argument since fixup_inode_link_count() makes a call
to replay_dir_deletes().
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 2780f0e1db01..460dc51e8c82 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -159,9 +159,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
static int link_to_fixup_dir(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, u64 objectid);
-static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_root *log,
+static noinline int replay_dir_deletes(struct walk_control *wc,
struct btrfs_path *path,
u64 dirid, bool del_all);
static void wait_log_commit(struct btrfs_root *root, int transid);
@@ -1727,9 +1725,10 @@ static int count_inode_refs(struct btrfs_inode *inode, struct btrfs_path *path)
* number of back refs found. If it goes down to zero, the iput
* will free the inode.
*/
-static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans,
+static noinline int fixup_inode_link_count(struct walk_control *wc,
struct btrfs_inode *inode)
{
+ struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = inode->root;
struct btrfs_path *path;
int ret;
@@ -1765,7 +1764,7 @@ static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans,
if (inode->vfs_inode.i_nlink == 0) {
if (S_ISDIR(inode->vfs_inode.i_mode)) {
- ret = replay_dir_deletes(trans, root, NULL, path, ino, true);
+ ret = replay_dir_deletes(wc, path, ino, true);
if (ret)
goto out;
}
@@ -1779,8 +1778,7 @@ static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans,
return ret;
}
-static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int fixup_inode_link_counts(struct walk_control *wc,
struct btrfs_path *path)
{
int ret;
@@ -1790,6 +1788,8 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans,
key.type = BTRFS_ORPHAN_ITEM_KEY;
key.offset = (u64)-1;
while (1) {
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_inode *inode;
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
@@ -1819,7 +1819,7 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans,
break;
}
- ret = fixup_inode_link_count(trans, inode);
+ ret = fixup_inode_link_count(wc, inode);
iput(&inode->vfs_inode);
if (ret)
break;
@@ -2455,12 +2455,13 @@ static int replay_xattr_deletes(struct walk_control *wc,
* Anything we don't find in the log is unlinked and removed from the
* directory.
*/
-static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_root *log,
+static noinline int replay_dir_deletes(struct walk_control *wc,
struct btrfs_path *path,
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;
u64 range_end;
int ret = 0;
@@ -2650,8 +2651,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
mode = btrfs_inode_mode(eb, inode_item);
if (S_ISDIR(mode)) {
- ret = replay_dir_deletes(trans, root, log, path,
- key.objectid, false);
+ ret = replay_dir_deletes(wc, path, key.objectid, false);
if (ret)
break;
}
@@ -7530,7 +7530,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
if (wc.stage == LOG_WALK_REPLAY_ALL) {
struct btrfs_root *root = wc.root;
- ret = fixup_inode_link_counts(trans, root, path);
+ ret = fixup_inode_link_counts(&wc, path);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 13/33] btrfs: pass walk_control structure to check_item_in_log()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (11 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 12/33] btrfs: pass walk_control structure to replay_dir_deletes() fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 14/33] btrfs: pass walk_control structure to replay_one_extent() fdmanana
` (20 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction and log tree as arguments to
check_item_in_log(), pass the walk_control structure as we can grab those
from the structure. This reduces the number of arguments passed and it's
going to be needed by an incoming change that improves error reporting for
log replay. Notice that a NULL log root argument to check_item_in_log()
makes it unconditionally delete a directory entry, so since the
walk_control always has a non-NULL log root, we add an extra boolean to
check_item_in_log() to tell it if it should unconditionally delete a
directory entry, preserving the behaviour and also making it a bit more
clear.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 460dc51e8c82..ca0946f947df 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2254,13 +2254,14 @@ static noinline int find_dir_range(struct btrfs_root *root,
* item is not in the log, the item is removed and the inode it points
* to is unlinked
*/
-static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
- struct btrfs_root *log,
+static noinline int check_item_in_log(struct walk_control *wc,
struct btrfs_path *path,
struct btrfs_path *log_path,
struct btrfs_inode *dir,
- struct btrfs_key *dir_key)
+ struct btrfs_key *dir_key,
+ bool force_remove)
{
+ struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = dir->root;
int ret;
struct extent_buffer *eb;
@@ -2287,10 +2288,10 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
goto out;
}
- if (log) {
+ if (!force_remove) {
struct btrfs_dir_item *log_di;
- log_di = btrfs_lookup_dir_index_item(trans, log, log_path,
+ log_di = btrfs_lookup_dir_index_item(trans, wc->log, log_path,
dir_key->objectid,
dir_key->offset, &name, 0);
if (IS_ERR(log_di)) {
@@ -2540,9 +2541,8 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
if (found_key.offset > range_end)
break;
- ret = check_item_in_log(trans, log, path,
- log_path, dir,
- &found_key);
+ ret = check_item_in_log(wc, path, log_path, dir,
+ &found_key, del_all);
if (ret)
goto out;
if (found_key.offset == (u64)-1)
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 14/33] btrfs: pass walk_control structure to replay_one_extent()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (12 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 13/33] btrfs: pass walk_control structure to check_item_in_log() fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 15/33] btrfs: pass walk_control structure to add_inode_ref() and helpers fdmanana
` (19 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction and subvolume root as arguments to
replay_one_extent(), pass the walk_control structure as we can grab all
of those from the structure. This reduces the number of arguments passed
and it's going to be needed by an incoming change that improves error
reporting for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index ca0946f947df..aac648ae30fb 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -642,12 +642,13 @@ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len,
* The extent is inserted into the file, dropping any existing extents
* from the file that overlap the new one.
*/
-static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int replay_one_extent(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb, int slot,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_drop_extents_args drop_args = { 0 };
struct btrfs_fs_info *fs_info = root->fs_info;
int found_type;
@@ -2728,7 +2729,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (ret)
break;
} else if (key.type == BTRFS_EXTENT_DATA_KEY) {
- ret = replay_one_extent(trans, root, path, eb, i, &key);
+ ret = replay_one_extent(wc, path, eb, i, &key);
if (ret)
break;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 15/33] btrfs: pass walk_control structure to add_inode_ref() and helpers
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (13 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 14/33] btrfs: pass walk_control structure to replay_one_extent() fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 16/33] btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name() fdmanana
` (18 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction, subvolume root and log tree as
arguments to add_inode_ref() and its helpers (__add_inode_ref(),
unlink_refs_not_in_log(), unlink_extrefs_not_in_log() and
unlink_old_inode_refs()), pass the walk_control structure as we can
access all of those from the structure. This reduces the number of
arguments passed and it's going to be needed by an incoming change
that improves error reporting for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 47 ++++++++++++++++++++++-----------------------
1 file changed, 23 insertions(+), 24 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index aac648ae30fb..2ec9252115fd 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1088,9 +1088,8 @@ static noinline int backref_in_log(struct btrfs_root *log,
return ret;
}
-static int unlink_refs_not_in_log(struct btrfs_trans_handle *trans,
+static int unlink_refs_not_in_log(struct walk_control *wc,
struct btrfs_path *path,
- struct btrfs_root *log_root,
struct btrfs_key *search_key,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
@@ -1108,6 +1107,7 @@ static int unlink_refs_not_in_log(struct btrfs_trans_handle *trans,
ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
ptr_end = ptr + btrfs_item_size(leaf, 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;
@@ -1121,7 +1121,7 @@ static int unlink_refs_not_in_log(struct btrfs_trans_handle *trans,
return ret;
}
- ret = backref_in_log(log_root, search_key, parent_objectid, &victim_name);
+ ret = backref_in_log(wc->log, search_key, parent_objectid, &victim_name);
if (ret) {
kfree(victim_name.name);
if (ret < 0) {
@@ -1145,10 +1145,8 @@ static int unlink_refs_not_in_log(struct btrfs_trans_handle *trans,
return 0;
}
-static int unlink_extrefs_not_in_log(struct btrfs_trans_handle *trans,
+static int unlink_extrefs_not_in_log(struct walk_control *wc,
struct btrfs_path *path,
- struct btrfs_root *root,
- struct btrfs_root *log_root,
struct btrfs_key *search_key,
struct btrfs_inode *inode,
u64 inode_objectid,
@@ -1160,6 +1158,9 @@ static int unlink_extrefs_not_in_log(struct btrfs_trans_handle *trans,
u32 cur_offset = 0;
while (cur_offset < item_size) {
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
+ struct btrfs_root *log_root = wc->log;
struct btrfs_inode_extref *extref;
struct btrfs_inode *victim_parent;
struct fscrypt_str victim_name;
@@ -1218,16 +1219,16 @@ static int unlink_extrefs_not_in_log(struct btrfs_trans_handle *trans,
return 0;
}
-static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static inline int __add_inode_ref(struct walk_control *wc,
struct btrfs_path *path,
- struct btrfs_root *log_root,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
u64 inode_objectid, u64 parent_objectid,
u64 ref_index, struct fscrypt_str *name)
{
int ret;
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_dir_item *di;
struct btrfs_key search_key;
struct btrfs_inode_extref *extref;
@@ -1249,8 +1250,8 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
if (search_key.objectid == search_key.offset)
return 1;
- ret = unlink_refs_not_in_log(trans, path, log_root, &search_key,
- dir, inode, parent_objectid);
+ ret = unlink_refs_not_in_log(wc, path, &search_key, dir, inode,
+ parent_objectid);
if (ret == -EAGAIN)
goto again;
else if (ret)
@@ -1263,8 +1264,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
if (IS_ERR(extref)) {
return PTR_ERR(extref);
} else if (extref) {
- ret = unlink_extrefs_not_in_log(trans, path, root, log_root,
- &search_key, inode,
+ ret = unlink_extrefs_not_in_log(wc, path, &search_key, inode,
inode_objectid, parent_objectid);
if (ret == -EAGAIN)
goto again;
@@ -1349,14 +1349,15 @@ static int ref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
* proper unlink of that name (that is, remove its entry from the inode
* reference item and both dir index keys).
*/
-static int unlink_old_inode_refs(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int unlink_old_inode_refs(struct walk_control *wc,
struct btrfs_path *path,
struct btrfs_inode *inode,
struct extent_buffer *log_eb,
int log_slot,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
int ret;
unsigned long ref_ptr;
unsigned long ref_end;
@@ -1441,13 +1442,13 @@ static int unlink_old_inode_refs(struct btrfs_trans_handle *trans,
* root is the destination we are replaying into, and path is for temp
* use by this function. (it should be released on return).
*/
-static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_root *log,
+static noinline int add_inode_ref(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb, int slot,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_inode *dir = NULL;
struct btrfs_inode *inode = NULL;
unsigned long ref_ptr;
@@ -1559,9 +1560,8 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
* overwrite any existing back reference, and we don't
* want to create dangling pointers in the directory.
*/
- ret = __add_inode_ref(trans, root, path, log, dir, inode,
- inode_objectid, parent_objectid,
- ref_index, &name);
+ ret = __add_inode_ref(wc, path, dir, inode, inode_objectid,
+ parent_objectid, ref_index, &name);
if (ret) {
if (ret == 1)
ret = 0;
@@ -1601,7 +1601,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
* dir index entries exist for a name but there is no inode reference
* item with the same name.
*/
- ret = unlink_old_inode_refs(trans, root, path, inode, eb, slot, key);
+ ret = unlink_old_inode_refs(wc, path, inode, eb, slot, key);
if (ret)
goto out;
@@ -2584,7 +2584,6 @@ static int replay_one_buffer(struct extent_buffer *eb,
};
struct btrfs_path *path;
struct btrfs_root *root = wc->root;
- struct btrfs_root *log = wc->log;
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_key key;
int i;
@@ -2725,7 +2724,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
} else if (key.type == BTRFS_INODE_REF_KEY ||
key.type == BTRFS_INODE_EXTREF_KEY) {
- ret = add_inode_ref(trans, root, log, path, eb, i, &key);
+ ret = add_inode_ref(wc, path, eb, i, &key);
if (ret)
break;
} else if (key.type == BTRFS_EXTENT_DATA_KEY) {
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 16/33] btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (14 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 15/33] btrfs: pass walk_control structure to add_inode_ref() and helpers fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 17/33] btrfs: pass walk_control structure to drop_one_dir_item() and helpers fdmanana
` (17 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction and subvolume root and log tree as
arguments, pass the walk_control structure as we can grab all of those
from the structure. This reduces the number of arguments passed and it's
going to be needed by an incoming change that improves error reporting
for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 2ec9252115fd..c4c2fbf291a1 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1961,13 +1961,14 @@ static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans,
* Returns < 0 on error, 0 if the name wasn't replayed (dentry points to a
* non-existing inode) and 1 if the name was replayed.
*/
-static noinline int replay_one_name(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int replay_one_name(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb,
struct btrfs_dir_item *di,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct fscrypt_str name = { 0 };
struct btrfs_dir_item *dir_dst_di;
struct btrfs_dir_item *index_dst_di;
@@ -2107,8 +2108,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
}
/* Replay one dir item from a BTRFS_DIR_INDEX_KEY key. */
-static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int replay_one_dir_item(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb, int slot,
struct btrfs_key *key)
@@ -2120,7 +2120,7 @@ static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
ASSERT(key->type == BTRFS_DIR_INDEX_KEY);
di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
- ret = replay_one_name(trans, root, path, eb, di, key);
+ ret = replay_one_name(wc, path, eb, di, key);
if (ret < 0)
return ret;
@@ -2156,12 +2156,12 @@ static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
fixup_path = btrfs_alloc_path();
if (!fixup_path) {
- btrfs_abort_transaction(trans, -ENOMEM);
+ btrfs_abort_transaction(wc->trans, -ENOMEM);
return -ENOMEM;
}
btrfs_dir_item_key_to_cpu(eb, di, &di_key);
- ret = link_to_fixup_dir(trans, root, fixup_path, di_key.objectid);
+ ret = link_to_fixup_dir(wc->trans, wc->root, fixup_path, di_key.objectid);
btrfs_free_path(fixup_path);
}
@@ -2709,7 +2709,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (key.type == BTRFS_DIR_INDEX_KEY &&
wc->stage == LOG_WALK_REPLAY_DIR_INDEX) {
- ret = replay_one_dir_item(trans, root, path, eb, i, &key);
+ ret = replay_one_dir_item(wc, path, eb, i, &key);
if (ret)
break;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 17/33] btrfs: pass walk_control structure to drop_one_dir_item() and helpers
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (15 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 16/33] btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name() fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 18/33] btrfs: pass walk_control structure to replay_one_extent() fdmanana
` (16 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction as an argument to drop_one_dir_item()
and its helpers (link_to_fixup_dir() and unlink_inode_for_log_replay()),
pass the walk_control structure as we can access the transaction from it
and the subvolume root. This is going to be needed by an incoming change
that improves error reporting for log replay and also reduces the number
of arguments passed to link_to_fixup_dir().
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 44 +++++++++++++++++++++++---------------------
1 file changed, 23 insertions(+), 21 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index c4c2fbf291a1..01a0f7cbcd4b 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -156,8 +156,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode,
int inode_only,
struct btrfs_log_ctx *ctx);
-static int link_to_fixup_dir(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int link_to_fixup_dir(struct walk_control *wc,
struct btrfs_path *path, u64 objectid);
static noinline int replay_dir_deletes(struct walk_control *wc,
struct btrfs_path *path,
@@ -927,11 +926,12 @@ static noinline int replay_one_extent(struct walk_control *wc,
return ret;
}
-static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans,
+static int unlink_inode_for_log_replay(struct walk_control *wc,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
const struct fscrypt_str *name)
{
+ struct btrfs_trans_handle *trans = wc->trans;
int ret;
ret = btrfs_unlink_inode(trans, dir, inode, name);
@@ -959,11 +959,12 @@ static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans,
* This is a helper function to do the unlink of a specific directory
* item
*/
-static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
+static noinline int drop_one_dir_item(struct walk_control *wc,
struct btrfs_path *path,
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;
@@ -990,11 +991,11 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
goto out;
}
- ret = link_to_fixup_dir(trans, root, path, location.objectid);
+ ret = link_to_fixup_dir(wc, path, location.objectid);
if (ret)
goto out;
- ret = unlink_inode_for_log_replay(trans, dir, inode, &name);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &name);
out:
kfree(name.name);
if (inode)
@@ -1135,7 +1136,7 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
inc_nlink(&inode->vfs_inode);
btrfs_release_path(path);
- ret = unlink_inode_for_log_replay(trans, dir, inode, &victim_name);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &victim_name);
kfree(victim_name.name);
if (ret)
return ret;
@@ -1207,7 +1208,7 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
inc_nlink(&inode->vfs_inode);
btrfs_release_path(path);
- ret = unlink_inode_for_log_replay(trans, victim_parent, inode,
+ ret = unlink_inode_for_log_replay(wc, victim_parent, inode,
&victim_name);
iput(&victim_parent->vfs_inode);
kfree(victim_name.name);
@@ -1281,7 +1282,7 @@ static inline int __add_inode_ref(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
return ret;
} else if (di) {
- ret = drop_one_dir_item(trans, path, dir, di);
+ ret = drop_one_dir_item(wc, path, dir, di);
if (ret)
return ret;
}
@@ -1292,7 +1293,7 @@ static inline int __add_inode_ref(struct walk_control *wc,
if (IS_ERR(di)) {
return PTR_ERR(di);
} else if (di) {
- ret = drop_one_dir_item(trans, path, dir, di);
+ ret = drop_one_dir_item(wc, path, dir, di);
if (ret)
return ret;
}
@@ -1415,7 +1416,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
goto out;
}
- ret = unlink_inode_for_log_replay(trans, dir, inode, &name);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &name);
kfree(name.name);
iput(&dir->vfs_inode);
if (ret)
@@ -1842,11 +1843,12 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
* count when replay is done. The link count is incremented here
* so the inode won't go away until we check it
*/
-static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int link_to_fixup_dir(struct walk_control *wc,
struct btrfs_path *path,
u64 objectid)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_key key;
int ret = 0;
struct btrfs_inode *inode;
@@ -1917,7 +1919,7 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans,
return ret;
}
-static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans,
+static int delete_conflicting_dir_entry(struct walk_control *wc,
struct btrfs_inode *dir,
struct btrfs_path *path,
struct btrfs_dir_item *dst_di,
@@ -1942,7 +1944,7 @@ static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans,
if (!exists)
return 0;
- return drop_one_dir_item(trans, path, dir, dst_di);
+ return drop_one_dir_item(wc, path, dir, dst_di);
}
/*
@@ -2014,7 +2016,7 @@ static noinline int replay_one_name(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
goto out;
} else if (dir_dst_di) {
- ret = delete_conflicting_dir_entry(trans, dir, path, dir_dst_di,
+ ret = delete_conflicting_dir_entry(wc, dir, path, dir_dst_di,
&log_key, log_flags, exists);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
@@ -2033,7 +2035,7 @@ static noinline int replay_one_name(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
goto out;
} else if (index_dst_di) {
- ret = delete_conflicting_dir_entry(trans, dir, path, index_dst_di,
+ ret = delete_conflicting_dir_entry(wc, dir, path, index_dst_di,
&log_key, log_flags, exists);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
@@ -2161,7 +2163,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
}
btrfs_dir_item_key_to_cpu(eb, di, &di_key);
- ret = link_to_fixup_dir(wc->trans, wc->root, fixup_path, di_key.objectid);
+ ret = link_to_fixup_dir(wc, fixup_path, di_key.objectid);
btrfs_free_path(fixup_path);
}
@@ -2317,12 +2319,12 @@ static noinline int check_item_in_log(struct walk_control *wc,
goto out;
}
- ret = link_to_fixup_dir(trans, root, path, location.objectid);
+ ret = link_to_fixup_dir(wc, path, location.objectid);
if (ret)
goto out;
inc_nlink(&inode->vfs_inode);
- ret = unlink_inode_for_log_replay(trans, dir, inode, &name);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &name);
/*
* Unlike dir item keys, dir index keys can only have one name (entry) in
* them, as there are no key collisions since each key has a unique offset
@@ -2699,7 +2701,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
}
- ret = link_to_fixup_dir(trans, root, path, key.objectid);
+ ret = link_to_fixup_dir(wc, path, key.objectid);
if (ret)
break;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 18/33] btrfs: pass walk_control structure to replay_one_extent()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (16 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 17/33] btrfs: pass walk_control structure to drop_one_dir_item() and helpers fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 19/33] btrfs: use level argument in log tree walk callback process_one_buffer() fdmanana
` (15 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction and subvolume root as arguments to
overwrite_item(), pass the walk_control structure as we can grab them
from the structure. This reduces the number of arguments passed and it's
going to be needed by an incoming change that improves error reporting
for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 01a0f7cbcd4b..2060f0d99f6e 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -410,12 +410,13 @@ static int process_one_buffer(struct extent_buffer *eb,
*
* If the key isn't in the destination yet, a new item is inserted.
*/
-static int overwrite_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int overwrite_item(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb, int slot,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
int ret;
u32 item_size;
u64 saved_i_size = 0;
@@ -739,7 +740,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
if (found_type == BTRFS_FILE_EXTENT_INLINE) {
/* inline extents are easy, we just overwrite them */
- ret = overwrite_item(trans, root, path, eb, slot, key);
+ ret = overwrite_item(wc, path, eb, slot, key);
if (ret)
goto out;
goto update_inode;
@@ -1607,7 +1608,7 @@ static noinline int add_inode_ref(struct walk_control *wc,
goto out;
/* finally write the back reference in the inode */
- ret = overwrite_item(trans, root, path, eb, slot, key);
+ ret = overwrite_item(wc, path, eb, slot, key);
out:
btrfs_release_path(path);
kfree(name.name);
@@ -2657,7 +2658,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (ret)
break;
}
- ret = overwrite_item(trans, root, path, eb, i, &key);
+ ret = overwrite_item(wc, path, eb, i, &key);
if (ret)
break;
@@ -2721,7 +2722,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
/* these keys are simply copied */
if (key.type == BTRFS_XATTR_ITEM_KEY) {
- ret = overwrite_item(trans, root, path, eb, i, &key);
+ ret = overwrite_item(wc, path, eb, i, &key);
if (ret)
break;
} else if (key.type == BTRFS_INODE_REF_KEY ||
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 19/33] btrfs: use level argument in log tree walk callback process_one_buffer()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (17 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 18/33] btrfs: pass walk_control structure to replay_one_extent() fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 20/33] btrfs: use level argument in log tree walk callback replay_one_buffer() fdmanana
` (14 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We already have the extent buffer's level in an argument, there's no need
to call btrfs_header_level(). So use the level argument and make the code
shorter.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 2060f0d99f6e..166ceb003a1e 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -386,8 +386,7 @@ static int process_one_buffer(struct extent_buffer *eb,
return ret;
}
- if (btrfs_buffer_uptodate(eb, gen, false) &&
- btrfs_header_level(eb) == 0) {
+ if (btrfs_buffer_uptodate(eb, gen, false) && level == 0) {
ret = btrfs_exclude_logged_extents(eb);
if (ret)
btrfs_abort_transaction(trans, ret);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 20/33] btrfs: use level argument in log tree walk callback replay_one_buffer()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (18 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 19/33] btrfs: use level argument in log tree walk callback process_one_buffer() fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 21/33] btrfs: use the inode item boolean everywhere in overwrite_item() fdmanana
` (13 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We already have the extent buffer's level in an argument, there's no need
to first ensure the extent buffer's data is loaded (by calling
btrfs_read_extent_buffer()) and then call btrfs_header_level() to check
the level. So use the level argument and do the check before calling
btrfs_read_extent_buffer().
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 166ceb003a1e..88e813bb28d8 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2591,17 +2591,15 @@ static int replay_one_buffer(struct extent_buffer *eb,
int i;
int ret;
+ if (level != 0)
+ return 0;
+
ret = btrfs_read_extent_buffer(eb, &check);
if (ret) {
btrfs_abort_transaction(trans, ret);
return ret;
}
- level = btrfs_header_level(eb);
-
- if (level != 0)
- return 0;
-
path = btrfs_alloc_path();
if (!path) {
btrfs_abort_transaction(trans, -ENOMEM);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 21/33] btrfs: use the inode item boolean everywhere in overwrite_item()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (19 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 20/33] btrfs: use level argument in log tree walk callback replay_one_buffer() fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 22/33] btrfs: add current log leaf, key and slot to struct walk_control fdmanana
` (12 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We have this boolean 'inode_item' to tell if we are processing an inode
item key and we use it in a couple of places while in another two places
we open code by checking if the key type matches the inode item type.
Make this consistent and use the boolean everywhere. Also rename it from
'inode_item' to 'is_inode_item', which makes it more clear that it's a
boolean and not an instance of struct btrfs_inode_item, and make it const
too.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 88e813bb28d8..d830c33be7c6 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -424,7 +424,7 @@ static int overwrite_item(struct walk_control *wc,
unsigned long dst_ptr;
struct extent_buffer *dst_eb;
int dst_slot;
- bool inode_item = key->type == BTRFS_INODE_ITEM_KEY;
+ const bool is_inode_item = (key->type == BTRFS_INODE_ITEM_KEY);
/*
* This is only used during log replay, so the root is always from a
@@ -486,7 +486,7 @@ static int overwrite_item(struct walk_control *wc,
* We need to load the old nbytes into the inode so when we
* replay the extents we've logged we get the right nbytes.
*/
- if (inode_item) {
+ if (is_inode_item) {
struct btrfs_inode_item *item;
u64 nbytes;
u32 mode;
@@ -507,7 +507,7 @@ static int overwrite_item(struct walk_control *wc,
if (S_ISDIR(mode))
btrfs_set_inode_size(eb, item, 0);
}
- } else if (inode_item) {
+ } else if (is_inode_item) {
struct btrfs_inode_item *item;
u32 mode;
@@ -561,7 +561,7 @@ static int overwrite_item(struct walk_control *wc,
* state of the tree found in the subvolume, and i_size is modified
* as it goes
*/
- if (key->type == BTRFS_INODE_ITEM_KEY && ret == -EEXIST) {
+ if (is_inode_item && ret == -EEXIST) {
struct btrfs_inode_item *src_item;
struct btrfs_inode_item *dst_item;
@@ -602,7 +602,7 @@ static int overwrite_item(struct walk_control *wc,
}
/* make sure the generation is filled in */
- if (key->type == BTRFS_INODE_ITEM_KEY) {
+ if (is_inode_item) {
struct btrfs_inode_item *dst_item;
dst_item = (struct btrfs_inode_item *)dst_ptr;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 22/33] btrfs: add current log leaf, key and slot to struct walk_control
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (20 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 21/33] btrfs: use the inode item boolean everywhere in overwrite_item() fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 23/33] btrfs: avoid unnecessary path allocation at fixup_inode_link_count() fdmanana
` (11 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
A lot of the log replay functions get passed the current log leaf being
processed as well as the current slot and the key at that slot. Instead
of passing them as parameters, add them to struct walk_control so that
we reduce the numbers of parameters. This is also going to be needed to
further changes that improve error reporting during log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 255 ++++++++++++++++++++++----------------------
1 file changed, 126 insertions(+), 129 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index d830c33be7c6..b4e901da9e8b 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -150,6 +150,18 @@ struct walk_control {
*/
int (*process_func)(struct extent_buffer *eb,
struct walk_control *wc, u64 gen, int level);
+
+ /*
+ * The following are used only when stage is >= LOG_WALK_REPLAY_INODES
+ * and by the replay_one_buffer() callback.
+ */
+
+ /* The current log leaf being processed. */
+ struct extent_buffer *log_leaf;
+ /* The key being processed of the current log leaf. */
+ struct btrfs_key log_key;
+ /* The slot being processed of the current log leaf. */
+ int log_slot;
};
static int btrfs_log_inode(struct btrfs_trans_handle *trans,
@@ -396,8 +408,9 @@ static int process_one_buffer(struct extent_buffer *eb,
}
/*
- * Item overwrite used by log replay. The given eb, slot and key all refer to
- * the source data we are copying out.
+ * Item overwrite used by log replay. The given log tree leaf, slot and key
+ * from the walk_control structure all refer to the source data we are copying
+ * out.
*
* The given root is for the tree we are copying into, and path is a scratch
* path for use in this function (it should be released on entry and will be
@@ -409,10 +422,7 @@ static int process_one_buffer(struct extent_buffer *eb,
*
* If the key isn't in the destination yet, a new item is inserted.
*/
-static int overwrite_item(struct walk_control *wc,
- struct btrfs_path *path,
- struct extent_buffer *eb, int slot,
- struct btrfs_key *key)
+static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -424,7 +434,7 @@ static int overwrite_item(struct walk_control *wc,
unsigned long dst_ptr;
struct extent_buffer *dst_eb;
int dst_slot;
- const bool is_inode_item = (key->type == BTRFS_INODE_ITEM_KEY);
+ const bool is_inode_item = (wc->log_key.type == BTRFS_INODE_ITEM_KEY);
/*
* This is only used during log replay, so the root is always from a
@@ -435,11 +445,11 @@ static int overwrite_item(struct walk_control *wc,
*/
ASSERT(btrfs_root_id(root) != BTRFS_TREE_LOG_OBJECTID);
- item_size = btrfs_item_size(eb, slot);
- src_ptr = btrfs_item_ptr_offset(eb, slot);
+ item_size = btrfs_item_size(wc->log_leaf, wc->log_slot);
+ src_ptr = btrfs_item_ptr_offset(wc->log_leaf, wc->log_slot);
/* Look for the key in the destination tree. */
- ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
return ret;
@@ -466,7 +476,7 @@ static int overwrite_item(struct walk_control *wc,
return -ENOMEM;
}
- read_extent_buffer(eb, src_copy, src_ptr, item_size);
+ read_extent_buffer(wc->log_leaf, src_copy, src_ptr, item_size);
dst_ptr = btrfs_item_ptr_offset(dst_eb, dst_slot);
ret = memcmp_extent_buffer(dst_eb, src_copy, dst_ptr, item_size);
@@ -494,18 +504,18 @@ static int overwrite_item(struct walk_control *wc,
item = btrfs_item_ptr(dst_eb, dst_slot,
struct btrfs_inode_item);
nbytes = btrfs_inode_nbytes(dst_eb, item);
- item = btrfs_item_ptr(eb, slot,
+ item = btrfs_item_ptr(wc->log_leaf, wc->log_slot,
struct btrfs_inode_item);
- btrfs_set_inode_nbytes(eb, item, nbytes);
+ btrfs_set_inode_nbytes(wc->log_leaf, item, nbytes);
/*
* If this is a directory we need to reset the i_size to
* 0 so that we can set it up properly when replaying
* the rest of the items in this log.
*/
- mode = btrfs_inode_mode(eb, item);
+ mode = btrfs_inode_mode(wc->log_leaf, item);
if (S_ISDIR(mode))
- btrfs_set_inode_size(eb, item, 0);
+ btrfs_set_inode_size(wc->log_leaf, item, 0);
}
} else if (is_inode_item) {
struct btrfs_inode_item *item;
@@ -515,24 +525,23 @@ static int overwrite_item(struct walk_control *wc,
* New inode, set nbytes to 0 so that the nbytes comes out
* properly when we replay the extents.
*/
- item = btrfs_item_ptr(eb, slot, struct btrfs_inode_item);
- btrfs_set_inode_nbytes(eb, item, 0);
+ item = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_inode_item);
+ btrfs_set_inode_nbytes(wc->log_leaf, item, 0);
/*
* If this is a directory we need to reset the i_size to 0 so
* that we can set it up properly when replaying the rest of
* the items in this log.
*/
- mode = btrfs_inode_mode(eb, item);
+ mode = btrfs_inode_mode(wc->log_leaf, item);
if (S_ISDIR(mode))
- btrfs_set_inode_size(eb, item, 0);
+ btrfs_set_inode_size(wc->log_leaf, item, 0);
}
insert:
btrfs_release_path(path);
/* try to insert the key into the destination tree */
path->skip_release_on_error = 1;
- ret = btrfs_insert_empty_item(trans, root, path,
- key, item_size);
+ ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, item_size);
path->skip_release_on_error = 0;
dst_eb = path->nodes[0];
@@ -568,8 +577,8 @@ static int overwrite_item(struct walk_control *wc,
src_item = (struct btrfs_inode_item *)src_ptr;
dst_item = (struct btrfs_inode_item *)dst_ptr;
- if (btrfs_inode_generation(eb, src_item) == 0) {
- const u64 ino_size = btrfs_inode_size(eb, src_item);
+ if (btrfs_inode_generation(wc->log_leaf, src_item) == 0) {
+ const u64 ino_size = btrfs_inode_size(wc->log_leaf, src_item);
/*
* For regular files an ino_size == 0 is used only when
@@ -578,21 +587,21 @@ static int overwrite_item(struct walk_control *wc,
* case don't set the size of the inode in the fs/subvol
* tree, otherwise we would be throwing valid data away.
*/
- if (S_ISREG(btrfs_inode_mode(eb, src_item)) &&
+ if (S_ISREG(btrfs_inode_mode(wc->log_leaf, src_item)) &&
S_ISREG(btrfs_inode_mode(dst_eb, dst_item)) &&
ino_size != 0)
btrfs_set_inode_size(dst_eb, dst_item, ino_size);
goto no_copy;
}
- if (S_ISDIR(btrfs_inode_mode(eb, src_item)) &&
+ if (S_ISDIR(btrfs_inode_mode(wc->log_leaf, src_item)) &&
S_ISDIR(btrfs_inode_mode(dst_eb, dst_item))) {
save_old_i_size = 1;
saved_i_size = btrfs_inode_size(dst_eb, dst_item);
}
}
- copy_extent_buffer(dst_eb, eb, dst_ptr, src_ptr, item_size);
+ copy_extent_buffer(dst_eb, wc->log_leaf, dst_ptr, src_ptr, item_size);
if (save_old_i_size) {
struct btrfs_inode_item *dst_item;
@@ -641,10 +650,7 @@ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len,
* The extent is inserted into the file, dropping any existing extents
* from the file that overlap the new one.
*/
-static noinline int replay_one_extent(struct walk_control *wc,
- struct btrfs_path *path,
- struct extent_buffer *eb, int slot,
- struct btrfs_key *key)
+static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path *path)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -652,7 +658,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
struct btrfs_fs_info *fs_info = root->fs_info;
int found_type;
u64 extent_end;
- u64 start = key->offset;
+ const u64 start = wc->log_key.offset;
u64 nbytes = 0;
u64 csum_start;
u64 csum_end;
@@ -665,34 +671,35 @@ static noinline int replay_one_extent(struct walk_control *wc,
unsigned long size;
int ret = 0;
- item = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
- found_type = btrfs_file_extent_type(eb, item);
+ item = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_file_extent_item);
+ found_type = btrfs_file_extent_type(wc->log_leaf, item);
if (found_type == BTRFS_FILE_EXTENT_REG ||
found_type == BTRFS_FILE_EXTENT_PREALLOC) {
- nbytes = btrfs_file_extent_num_bytes(eb, item);
+ nbytes = btrfs_file_extent_num_bytes(wc->log_leaf, item);
extent_end = start + nbytes;
/*
* We don't add to the inodes nbytes if we are prealloc or a
* hole.
*/
- if (btrfs_file_extent_disk_bytenr(eb, item) == 0)
+ if (btrfs_file_extent_disk_bytenr(wc->log_leaf, item) == 0)
nbytes = 0;
} else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
- size = btrfs_file_extent_ram_bytes(eb, item);
- nbytes = btrfs_file_extent_ram_bytes(eb, item);
+ size = btrfs_file_extent_ram_bytes(wc->log_leaf, item);
+ nbytes = btrfs_file_extent_ram_bytes(wc->log_leaf, item);
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), key->objectid, key->offset);
+ found_type, btrfs_root_id(root), wc->log_key.objectid,
+ wc->log_key.offset);
return -EUCLEAN;
}
- inode = btrfs_iget_logging(key->objectid, root);
+ inode = btrfs_iget_logging(wc->log_key.objectid, root);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
btrfs_abort_transaction(trans, ret);
@@ -719,7 +726,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
* we already have a pointer to this exact extent,
* we don't have to do anything
*/
- if (memcmp_extent_buffer(eb, &existing, (unsigned long)item,
+ if (memcmp_extent_buffer(wc->log_leaf, &existing, (unsigned long)item,
sizeof(existing)) == 0) {
btrfs_release_path(path);
goto out;
@@ -739,7 +746,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
if (found_type == BTRFS_FILE_EXTENT_INLINE) {
/* inline extents are easy, we just overwrite them */
- ret = overwrite_item(wc, path, eb, slot, key);
+ ret = overwrite_item(wc, path);
if (ret)
goto out;
goto update_inode;
@@ -751,18 +758,18 @@ static noinline int replay_one_extent(struct walk_control *wc,
*/
/* A hole and NO_HOLES feature enabled, nothing else to do. */
- if (btrfs_file_extent_disk_bytenr(eb, item) == 0 &&
+ if (btrfs_file_extent_disk_bytenr(wc->log_leaf, item) == 0 &&
btrfs_fs_incompat(fs_info, NO_HOLES))
goto update_inode;
- ret = btrfs_insert_empty_item(trans, root, path, key, sizeof(*item));
+ ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, sizeof(*item));
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
}
dest_offset = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]);
- copy_extent_buffer(path->nodes[0], eb, dest_offset, (unsigned long)item,
- sizeof(*item));
+ copy_extent_buffer(path->nodes[0], wc->log_leaf, dest_offset,
+ (unsigned long)item, sizeof(*item));
/*
* We have an explicit hole and NO_HOLES is not enabled. We have added
@@ -770,15 +777,15 @@ static noinline int replay_one_extent(struct walk_control *wc,
* anything else to do other than update the file extent item range and
* update the inode item.
*/
- if (btrfs_file_extent_disk_bytenr(eb, item) == 0) {
+ if (btrfs_file_extent_disk_bytenr(wc->log_leaf, item) == 0) {
btrfs_release_path(path);
goto update_inode;
}
- ins.objectid = btrfs_file_extent_disk_bytenr(eb, item);
+ ins.objectid = btrfs_file_extent_disk_bytenr(wc->log_leaf, item);
ins.type = BTRFS_EXTENT_ITEM_KEY;
- ins.offset = btrfs_file_extent_disk_num_bytes(eb, item);
- offset = key->offset - btrfs_file_extent_offset(eb, item);
+ ins.offset = btrfs_file_extent_disk_num_bytes(wc->log_leaf, item);
+ offset = wc->log_key.offset - btrfs_file_extent_offset(wc->log_leaf, item);
/*
* Manually record dirty extent, as here we did a shallow file extent
@@ -810,7 +817,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
.ref_root = btrfs_root_id(root),
};
- btrfs_init_data_ref(&ref, key->objectid, offset, 0, false);
+ 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);
@@ -819,7 +826,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
} else {
/* Insert the extent pointer in the extent tree. */
ret = btrfs_alloc_logged_file_extent(trans, btrfs_root_id(root),
- key->objectid, offset, &ins);
+ wc->log_key.objectid, offset, &ins);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
@@ -828,12 +835,12 @@ static noinline int replay_one_extent(struct walk_control *wc,
btrfs_release_path(path);
- if (btrfs_file_extent_compression(eb, item)) {
+ if (btrfs_file_extent_compression(wc->log_leaf, item)) {
csum_start = ins.objectid;
csum_end = csum_start + ins.offset;
} else {
- csum_start = ins.objectid + btrfs_file_extent_offset(eb, item);
- csum_end = csum_start + btrfs_file_extent_num_bytes(eb, item);
+ csum_start = ins.objectid + btrfs_file_extent_offset(wc->log_leaf, item);
+ csum_end = csum_start + btrfs_file_extent_num_bytes(wc->log_leaf, item);
}
ret = btrfs_lookup_csums_list(root->log_root, csum_start, csum_end - 1,
@@ -1352,10 +1359,7 @@ 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_path *path,
- struct btrfs_inode *inode,
- struct extent_buffer *log_eb,
- int log_slot,
- struct btrfs_key *key)
+ struct btrfs_inode *inode)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1366,7 +1370,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
again:
btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0);
if (ret > 0) {
ret = 0;
goto out;
@@ -1383,7 +1387,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
struct fscrypt_str name;
u64 parent_id;
- if (key->type == BTRFS_INODE_EXTREF_KEY) {
+ if (wc->log_key.type == BTRFS_INODE_EXTREF_KEY) {
ret = extref_get_fields(eb, ref_ptr, &name,
NULL, &parent_id);
if (ret) {
@@ -1391,7 +1395,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
goto out;
}
} else {
- parent_id = key->offset;
+ parent_id = wc->log_key.offset;
ret = ref_get_fields(eb, ref_ptr, &name, NULL);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -1399,11 +1403,12 @@ static int unlink_old_inode_refs(struct walk_control *wc,
}
}
- if (key->type == BTRFS_INODE_EXTREF_KEY)
- ret = !!btrfs_find_name_in_ext_backref(log_eb, log_slot,
+ if (wc->log_key.type == BTRFS_INODE_EXTREF_KEY)
+ ret = !!btrfs_find_name_in_ext_backref(wc->log_leaf, wc->log_slot,
parent_id, &name);
else
- ret = !!btrfs_find_name_in_backref(log_eb, log_slot, &name);
+ ret = !!btrfs_find_name_in_backref(wc->log_leaf, wc->log_slot,
+ &name);
if (!ret) {
struct btrfs_inode *dir;
@@ -1426,7 +1431,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
kfree(name.name);
ref_ptr += name.len;
- if (key->type == BTRFS_INODE_EXTREF_KEY)
+ if (wc->log_key.type == BTRFS_INODE_EXTREF_KEY)
ref_ptr += sizeof(struct btrfs_inode_extref);
else
ref_ptr += sizeof(struct btrfs_inode_ref);
@@ -1438,15 +1443,10 @@ static int unlink_old_inode_refs(struct walk_control *wc,
}
/*
- * replay one inode back reference item found in the log tree.
- * eb, slot and key refer to the buffer and key found in the log tree.
- * root is the destination we are replaying into, and path is for temp
- * use by this function. (it should be released on return).
+ * Replay one inode back reference item found in the log tree.
+ * Path is for temporary use by this function (it should be released on return).
*/
-static noinline int add_inode_ref(struct walk_control *wc,
- struct btrfs_path *path,
- struct extent_buffer *eb, int slot,
- struct btrfs_key *key)
+static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *path)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1456,26 +1456,26 @@ static noinline int add_inode_ref(struct walk_control *wc,
unsigned long ref_end;
struct fscrypt_str name = { 0 };
int ret;
- const bool is_extref_item = (key->type == BTRFS_INODE_EXTREF_KEY);
+ const bool is_extref_item = (wc->log_key.type == BTRFS_INODE_EXTREF_KEY);
u64 parent_objectid;
u64 inode_objectid;
u64 ref_index = 0;
int ref_struct_size;
- ref_ptr = btrfs_item_ptr_offset(eb, slot);
- ref_end = ref_ptr + btrfs_item_size(eb, slot);
+ ref_ptr = btrfs_item_ptr_offset(wc->log_leaf, wc->log_slot);
+ ref_end = ref_ptr + btrfs_item_size(wc->log_leaf, wc->log_slot);
if (is_extref_item) {
struct btrfs_inode_extref *r;
ref_struct_size = sizeof(struct btrfs_inode_extref);
r = (struct btrfs_inode_extref *)ref_ptr;
- parent_objectid = btrfs_inode_extref_parent(eb, r);
+ parent_objectid = btrfs_inode_extref_parent(wc->log_leaf, r);
} else {
ref_struct_size = sizeof(struct btrfs_inode_ref);
- parent_objectid = key->offset;
+ parent_objectid = wc->log_key.offset;
}
- inode_objectid = key->objectid;
+ inode_objectid = wc->log_key.objectid;
/*
* it is possible that we didn't log all the parent directories
@@ -1504,7 +1504,7 @@ static noinline int add_inode_ref(struct walk_control *wc,
while (ref_ptr < ref_end) {
if (is_extref_item) {
- ret = extref_get_fields(eb, ref_ptr, &name,
+ ret = extref_get_fields(wc->log_leaf, ref_ptr, &name,
&ref_index, &parent_objectid);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -1541,7 +1541,7 @@ static noinline int add_inode_ref(struct walk_control *wc,
}
}
} else {
- ret = ref_get_fields(eb, ref_ptr, &name, &ref_index);
+ ret = ref_get_fields(wc->log_leaf, ref_ptr, &name, &ref_index);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
@@ -1602,12 +1602,12 @@ static noinline int add_inode_ref(struct walk_control *wc,
* dir index entries exist for a name but there is no inode reference
* item with the same name.
*/
- ret = unlink_old_inode_refs(wc, path, inode, eb, slot, key);
+ ret = unlink_old_inode_refs(wc, path, inode);
if (ret)
goto out;
/* finally write the back reference in the inode */
- ret = overwrite_item(wc, path, eb, slot, key);
+ ret = overwrite_item(wc, path);
out:
btrfs_release_path(path);
kfree(name.name);
@@ -1965,9 +1965,7 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,
*/
static noinline int replay_one_name(struct walk_control *wc,
struct btrfs_path *path,
- struct extent_buffer *eb,
- struct btrfs_dir_item *di,
- struct btrfs_key *key)
+ struct btrfs_dir_item *di)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1985,21 +1983,22 @@ static noinline int replay_one_name(struct walk_control *wc,
bool update_size = true;
bool name_added = false;
- dir = btrfs_iget_logging(key->objectid, root);
+ dir = btrfs_iget_logging(wc->log_key.objectid, root);
if (IS_ERR(dir)) {
ret = PTR_ERR(dir);
btrfs_abort_transaction(trans, ret);
return ret;
}
- ret = read_alloc_one_name(eb, di + 1, btrfs_dir_name_len(eb, di), &name);
+ 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);
goto out;
}
- log_flags = btrfs_dir_flags(eb, di);
- btrfs_dir_item_key_to_cpu(eb, di, &log_key);
+ log_flags = btrfs_dir_flags(wc->log_leaf, di);
+ btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &log_key);
ret = btrfs_lookup_inode(trans, root, path, &log_key, 0);
btrfs_release_path(path);
if (ret < 0) {
@@ -2009,7 +2008,7 @@ static noinline int replay_one_name(struct walk_control *wc,
exists = (ret == 0);
ret = 0;
- dir_dst_di = btrfs_lookup_dir_item(trans, root, path, key->objectid,
+ dir_dst_di = btrfs_lookup_dir_item(trans, root, path, wc->log_key.objectid,
&name, 1);
if (IS_ERR(dir_dst_di)) {
ret = PTR_ERR(dir_dst_di);
@@ -2028,8 +2027,8 @@ static noinline int replay_one_name(struct walk_control *wc,
btrfs_release_path(path);
index_dst_di = btrfs_lookup_dir_index_item(trans, root, path,
- key->objectid, key->offset,
- &name, 1);
+ wc->log_key.objectid,
+ wc->log_key.offset, &name, 1);
if (IS_ERR(index_dst_di)) {
ret = PTR_ERR(index_dst_di);
btrfs_abort_transaction(trans, ret);
@@ -2058,7 +2057,7 @@ static noinline int replay_one_name(struct walk_control *wc,
*/
search_key.objectid = log_key.objectid;
search_key.type = BTRFS_INODE_REF_KEY;
- search_key.offset = key->objectid;
+ 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);
@@ -2072,8 +2071,8 @@ static noinline int replay_one_name(struct walk_control *wc,
search_key.objectid = log_key.objectid;
search_key.type = BTRFS_INODE_EXTREF_KEY;
- search_key.offset = btrfs_extref_hash(key->objectid, name.name, name.len);
- ret = backref_in_log(root->log_root, &search_key, key->objectid, &name);
+ 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);
goto out;
@@ -2084,7 +2083,7 @@ static noinline int replay_one_name(struct walk_control *wc,
goto out;
}
btrfs_release_path(path);
- ret = insert_one_name(trans, root, key->objectid, key->offset,
+ 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);
@@ -2111,18 +2110,16 @@ static noinline int replay_one_name(struct walk_control *wc,
/* Replay one dir item from a BTRFS_DIR_INDEX_KEY key. */
static noinline int replay_one_dir_item(struct walk_control *wc,
- struct btrfs_path *path,
- struct extent_buffer *eb, int slot,
- struct btrfs_key *key)
+ struct btrfs_path *path)
{
int ret;
struct btrfs_dir_item *di;
/* We only log dir index keys, which only contain a single dir item. */
- ASSERT(key->type == BTRFS_DIR_INDEX_KEY);
+ ASSERT(wc->log_key.type == BTRFS_DIR_INDEX_KEY);
- di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
- ret = replay_one_name(wc, path, eb, di, key);
+ di = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_dir_item);
+ ret = replay_one_name(wc, path, di);
if (ret < 0)
return ret;
@@ -2152,7 +2149,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
* to ever delete the parent directory has it would result in stale
* dentries that can never be deleted.
*/
- if (ret == 1 && btrfs_dir_ftype(eb, di) != BTRFS_FT_DIR) {
+ if (ret == 1 && btrfs_dir_ftype(wc->log_leaf, di) != BTRFS_FT_DIR) {
struct btrfs_path *fixup_path;
struct btrfs_key di_key;
@@ -2162,7 +2159,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
return -ENOMEM;
}
- btrfs_dir_item_key_to_cpu(eb, di, &di_key);
+ btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &di_key);
ret = link_to_fixup_dir(wc, fixup_path, di_key.objectid);
btrfs_free_path(fixup_path);
}
@@ -2339,15 +2336,14 @@ static noinline int check_item_in_log(struct walk_control *wc,
return ret;
}
-static int replay_xattr_deletes(struct walk_control *wc,
- struct btrfs_path *path,
- const u64 ino)
+static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
struct btrfs_root *log = wc->log;
struct btrfs_key search_key;
struct btrfs_path *log_path;
+ const u64 ino = wc->log_key.objectid;
int i;
int nritems;
int ret;
@@ -2587,8 +2583,6 @@ static int replay_one_buffer(struct extent_buffer *eb,
struct btrfs_path *path;
struct btrfs_root *root = wc->root;
struct btrfs_trans_handle *trans = wc->trans;
- struct btrfs_key key;
- int i;
int ret;
if (level != 0)
@@ -2606,14 +2600,17 @@ static int replay_one_buffer(struct extent_buffer *eb,
return -ENOMEM;
}
+ wc->log_leaf = eb;
+
nritems = btrfs_header_nritems(eb);
- for (i = 0; i < nritems; i++) {
+ for (wc->log_slot = 0; wc->log_slot < nritems; wc->log_slot++) {
struct btrfs_inode_item *inode_item;
- btrfs_item_key_to_cpu(eb, &key, i);
+ btrfs_item_key_to_cpu(eb, &wc->log_key, wc->log_slot);
- if (key.type == BTRFS_INODE_ITEM_KEY) {
- inode_item = btrfs_item_ptr(eb, i, struct btrfs_inode_item);
+ if (wc->log_key.type == BTRFS_INODE_ITEM_KEY) {
+ inode_item = btrfs_item_ptr(eb, wc->log_slot,
+ struct btrfs_inode_item);
/*
* An inode with no links is either:
*
@@ -2642,20 +2639,20 @@ static int replay_one_buffer(struct extent_buffer *eb,
}
/* Inode keys are done during the first stage. */
- if (key.type == BTRFS_INODE_ITEM_KEY &&
+ if (wc->log_key.type == BTRFS_INODE_ITEM_KEY &&
wc->stage == LOG_WALK_REPLAY_INODES) {
u32 mode;
- ret = replay_xattr_deletes(wc, path, key.objectid);
+ ret = replay_xattr_deletes(wc, path);
if (ret)
break;
mode = btrfs_inode_mode(eb, inode_item);
if (S_ISDIR(mode)) {
- ret = replay_dir_deletes(wc, path, key.objectid, false);
+ ret = replay_dir_deletes(wc, path, wc->log_key.objectid, false);
if (ret)
break;
}
- ret = overwrite_item(wc, path, eb, i, &key);
+ ret = overwrite_item(wc, path);
if (ret)
break;
@@ -2672,7 +2669,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
struct btrfs_inode *inode;
u64 from;
- inode = btrfs_iget_logging(key.objectid, root);
+ inode = btrfs_iget_logging(wc->log_key.objectid, root);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
btrfs_abort_transaction(trans, ret);
@@ -2699,7 +2696,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
}
- ret = link_to_fixup_dir(wc, path, key.objectid);
+ ret = link_to_fixup_dir(wc, path, wc->log_key.objectid);
if (ret)
break;
}
@@ -2707,9 +2704,9 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (wc->ignore_cur_inode)
continue;
- if (key.type == BTRFS_DIR_INDEX_KEY &&
+ if (wc->log_key.type == BTRFS_DIR_INDEX_KEY &&
wc->stage == LOG_WALK_REPLAY_DIR_INDEX) {
- ret = replay_one_dir_item(wc, path, eb, i, &key);
+ ret = replay_one_dir_item(wc, path);
if (ret)
break;
}
@@ -2718,17 +2715,17 @@ static int replay_one_buffer(struct extent_buffer *eb,
continue;
/* these keys are simply copied */
- if (key.type == BTRFS_XATTR_ITEM_KEY) {
- ret = overwrite_item(wc, path, eb, i, &key);
+ if (wc->log_key.type == BTRFS_XATTR_ITEM_KEY) {
+ ret = overwrite_item(wc, path);
if (ret)
break;
- } else if (key.type == BTRFS_INODE_REF_KEY ||
- key.type == BTRFS_INODE_EXTREF_KEY) {
- ret = add_inode_ref(wc, path, eb, i, &key);
+ } else if (wc->log_key.type == BTRFS_INODE_REF_KEY ||
+ wc->log_key.type == BTRFS_INODE_EXTREF_KEY) {
+ ret = add_inode_ref(wc, path);
if (ret)
break;
- } else if (key.type == BTRFS_EXTENT_DATA_KEY) {
- ret = replay_one_extent(wc, path, eb, i, &key);
+ } else if (wc->log_key.type == BTRFS_EXTENT_DATA_KEY) {
+ ret = replay_one_extent(wc, path);
if (ret)
break;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 23/33] btrfs: avoid unnecessary path allocation at fixup_inode_link_count()
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (21 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 22/33] btrfs: add current log leaf, key and slot to struct walk_control fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 24/33] btrfs: avoid path allocations when dropping extents during log replay fdmanana
` (10 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
There's no need to allocate a path as our single caller already has a
path that we can use. So pass the caller's path and use it.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index b4e901da9e8b..5754333ae732 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1728,19 +1728,15 @@ static int count_inode_refs(struct btrfs_inode *inode, struct btrfs_path *path)
* will free the inode.
*/
static noinline int fixup_inode_link_count(struct walk_control *wc,
+ struct btrfs_path *path,
struct btrfs_inode *inode)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = inode->root;
- struct btrfs_path *path;
int ret;
u64 nlink = 0;
const u64 ino = btrfs_ino(inode);
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
ret = count_inode_refs(inode, path);
if (ret < 0)
goto out;
@@ -1776,7 +1772,7 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(path);
return ret;
}
@@ -1821,7 +1817,7 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
break;
}
- ret = fixup_inode_link_count(wc, inode);
+ ret = fixup_inode_link_count(wc, path, inode);
iput(&inode->vfs_inode);
if (ret)
break;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 24/33] btrfs: avoid path allocations when dropping extents during log replay
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (22 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 23/33] btrfs: avoid unnecessary path allocation at fixup_inode_link_count() fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 25/33] btrfs: avoid unnecessary path allocation when replaying a dir item fdmanana
` (9 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We can avoid a path allocation in the btrfs_drop_extents() calls we have
at replay_one_extent() and replay_one_buffer() by passing the path we
already have in those contextes as it's unused by the time they call
btrfs_drop_extents().
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 5754333ae732..a912ccdf1485 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -738,6 +738,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
drop_args.start = start;
drop_args.end = extent_end;
drop_args.drop_cache = true;
+ drop_args.path = path;
ret = btrfs_drop_extents(trans, root, inode, &drop_args);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -2676,6 +2677,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
drop_args.start = from;
drop_args.end = (u64)-1;
drop_args.drop_cache = true;
+ drop_args.path = path;
ret = btrfs_drop_extents(trans, root, inode, &drop_args);
if (ret) {
btrfs_abort_transaction(trans, ret);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 25/33] btrfs: avoid unnecessary path allocation when replaying a dir item
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (23 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 24/33] btrfs: avoid path allocations when dropping extents during log replay fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 26/33] btrfs: remove redundant path release when processing dentry during log replay fdmanana
` (8 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
There's no need to allocate 'fixup_path' at replay_one_dir_item(), as the
path passed as an argument is unused by the time link_to_fixup_dir() is
called (replay_one_name() releases the path before it returns).
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index a912ccdf1485..de1f1c024dc0 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2147,18 +2147,10 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
* dentries that can never be deleted.
*/
if (ret == 1 && btrfs_dir_ftype(wc->log_leaf, di) != BTRFS_FT_DIR) {
- struct btrfs_path *fixup_path;
struct btrfs_key di_key;
- fixup_path = btrfs_alloc_path();
- if (!fixup_path) {
- btrfs_abort_transaction(wc->trans, -ENOMEM);
- return -ENOMEM;
- }
-
btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &di_key);
- ret = link_to_fixup_dir(wc, fixup_path, di_key.objectid);
- btrfs_free_path(fixup_path);
+ ret = link_to_fixup_dir(wc, path, di_key.objectid);
}
return ret;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 26/33] btrfs: remove redundant path release when processing dentry during log replay
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (24 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 25/33] btrfs: avoid unnecessary path allocation when replaying a dir item fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 27/33] btrfs: remove redundant path release when overwriting item " fdmanana
` (7 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
At replay_one_one() we have a redundant btrfs_release_path() just before
calling insert_one_name(), as some lines above we have already released
the path with another btrfs_release_path() call. So remove it.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index de1f1c024dc0..65b8858e82d1 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2079,7 +2079,6 @@ static noinline int replay_one_name(struct walk_control *wc,
update_size = false;
goto out;
}
- btrfs_release_path(path);
ret = insert_one_name(trans, root, wc->log_key.objectid, wc->log_key.offset,
&name, &log_key);
if (ret && ret != -ENOENT && ret != -EEXIST) {
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 27/33] btrfs: remove redundant path release when overwriting item during log replay
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (25 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 26/33] btrfs: remove redundant path release when processing dentry during log replay fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 28/33] btrfs: add path for subvolume tree changes to struct walk_control fdmanana
` (6 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
At overwrite_item() we have a redundant btrfs_release_path() just before
failing with -ENOMEM, as the caller who passed in the path will free it
and therefore also release any refcounts and locks on the extent buffers
of the path. So remove it.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 65b8858e82d1..66500ebdd35c 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -471,7 +471,6 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
}
src_copy = kmalloc(item_size, GFP_NOFS);
if (!src_copy) {
- btrfs_release_path(path);
btrfs_abort_transaction(trans, -ENOMEM);
return -ENOMEM;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 28/33] btrfs: add path for subvolume tree changes to struct walk_control
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (26 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 27/33] btrfs: remove redundant path release when overwriting item " fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 29/33] btrfs: stop passing inode object IDs to __add_inode_ref() in log replay fdmanana
` (5 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
While replaying log trees we need to do searches and updates to subvolume
trees and for that we use a path that we allocate in replay_one_buffer()
and then pass it as a parameter to other functions deeper in the log
replay call chain. Instead of passing it as parameter, add it to struct
walk_control since we pass a pointer to that structure for every log
replay function.
This reduces the number of arguments passed to the functions and it will
be needed and important for an upcoming changes that improves error
reporting for log replay. Also name the new filed in struct walk_control
to 'subvol_path' - while that is longer to type, the naming makes it clear
it's used for subvolume tree operations since many of the log replay
functions operate both on subvolume and log trees, and for the log tree
searches we have struct walk_control::log_leaf to also make it obvious
it's an extent buffer for a log tree extent buffer.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 323 +++++++++++++++++++++-----------------------
1 file changed, 156 insertions(+), 167 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 66500ebdd35c..747daaaaf332 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -162,16 +162,17 @@ struct walk_control {
struct btrfs_key log_key;
/* The slot being processed of the current log leaf. */
int log_slot;
+
+ /* A path used for searches and modifications to subvolume trees. */
+ struct btrfs_path *subvol_path;
};
static int btrfs_log_inode(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode,
int inode_only,
struct btrfs_log_ctx *ctx);
-static int link_to_fixup_dir(struct walk_control *wc,
- struct btrfs_path *path, u64 objectid);
+static int link_to_fixup_dir(struct walk_control *wc, u64 objectid);
static noinline int replay_dir_deletes(struct walk_control *wc,
- struct btrfs_path *path,
u64 dirid, bool del_all);
static void wait_log_commit(struct btrfs_root *root, int transid);
@@ -422,7 +423,7 @@ static int process_one_buffer(struct extent_buffer *eb,
*
* If the key isn't in the destination yet, a new item is inserted.
*/
-static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
+static int overwrite_item(struct walk_control *wc)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -449,14 +450,14 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
src_ptr = btrfs_item_ptr_offset(wc->log_leaf, wc->log_slot);
/* Look for the key in the destination tree. */
- ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
return ret;
}
- dst_eb = path->nodes[0];
- dst_slot = path->slots[0];
+ dst_eb = wc->subvol_path->nodes[0];
+ dst_slot = wc->subvol_path->slots[0];
if (ret == 0) {
char *src_copy;
@@ -466,7 +467,7 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
goto insert;
if (item_size == 0) {
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return 0;
}
src_copy = kmalloc(item_size, GFP_NOFS);
@@ -487,7 +488,7 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
* sync
*/
if (ret == 0) {
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return 0;
}
@@ -537,23 +538,23 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
btrfs_set_inode_size(wc->log_leaf, item, 0);
}
insert:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* try to insert the key into the destination tree */
- path->skip_release_on_error = 1;
- ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, item_size);
- path->skip_release_on_error = 0;
+ wc->subvol_path->skip_release_on_error = 1;
+ ret = btrfs_insert_empty_item(trans, root, wc->subvol_path, &wc->log_key, item_size);
+ wc->subvol_path->skip_release_on_error = 0;
- dst_eb = path->nodes[0];
- dst_slot = path->slots[0];
+ dst_eb = wc->subvol_path->nodes[0];
+ dst_slot = wc->subvol_path->slots[0];
/* make sure any existing item is the correct size */
if (ret == -EEXIST || ret == -EOVERFLOW) {
const u32 found_size = btrfs_item_size(dst_eb, dst_slot);
if (found_size > item_size)
- btrfs_truncate_item(trans, path, item_size, 1);
+ btrfs_truncate_item(trans, wc->subvol_path, item_size, 1);
else if (found_size < item_size)
- btrfs_extend_item(trans, path, item_size - found_size);
+ btrfs_extend_item(trans, wc->subvol_path, item_size - found_size);
} else if (ret) {
btrfs_abort_transaction(trans, ret);
return ret;
@@ -618,7 +619,7 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
btrfs_set_inode_generation(dst_eb, dst_item, trans->transid);
}
no_copy:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return 0;
}
@@ -649,7 +650,7 @@ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len,
* The extent is inserted into the file, dropping any existing extents
* from the file that overlap the new one.
*/
-static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path *path)
+static noinline int replay_one_extent(struct walk_control *wc)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -710,16 +711,18 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
* file. This must be done before the btrfs_drop_extents run
* so we don't try to drop this extent.
*/
- ret = btrfs_lookup_file_extent(trans, root, path, btrfs_ino(inode), start, 0);
+ ret = btrfs_lookup_file_extent(trans, root, wc->subvol_path,
+ btrfs_ino(inode), start, 0);
if (ret == 0 &&
(found_type == BTRFS_FILE_EXTENT_REG ||
found_type == BTRFS_FILE_EXTENT_PREALLOC)) {
+ struct extent_buffer *leaf = wc->subvol_path->nodes[0];
struct btrfs_file_extent_item existing;
unsigned long ptr;
- ptr = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]);
- read_extent_buffer(path->nodes[0], &existing, ptr, sizeof(existing));
+ ptr = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]);
+ read_extent_buffer(leaf, &existing, ptr, sizeof(existing));
/*
* we already have a pointer to this exact extent,
@@ -727,17 +730,17 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
*/
if (memcmp_extent_buffer(wc->log_leaf, &existing, (unsigned long)item,
sizeof(existing)) == 0) {
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
goto out;
}
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* drop any overlapping extents */
drop_args.start = start;
drop_args.end = extent_end;
drop_args.drop_cache = true;
- drop_args.path = path;
+ drop_args.path = wc->subvol_path;
ret = btrfs_drop_extents(trans, root, inode, &drop_args);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -746,7 +749,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
if (found_type == BTRFS_FILE_EXTENT_INLINE) {
/* inline extents are easy, we just overwrite them */
- ret = overwrite_item(wc, path);
+ ret = overwrite_item(wc);
if (ret)
goto out;
goto update_inode;
@@ -762,13 +765,15 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
btrfs_fs_incompat(fs_info, NO_HOLES))
goto update_inode;
- ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, sizeof(*item));
+ ret = btrfs_insert_empty_item(trans, root, wc->subvol_path,
+ &wc->log_key, sizeof(*item));
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
}
- dest_offset = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]);
- copy_extent_buffer(path->nodes[0], wc->log_leaf, dest_offset,
+ dest_offset = btrfs_item_ptr_offset(wc->subvol_path->nodes[0],
+ wc->subvol_path->slots[0]);
+ copy_extent_buffer(wc->subvol_path->nodes[0], wc->log_leaf, dest_offset,
(unsigned long)item, sizeof(*item));
/*
@@ -778,7 +783,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
* update the inode item.
*/
if (btrfs_file_extent_disk_bytenr(wc->log_leaf, item) == 0) {
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
goto update_inode;
}
@@ -833,7 +838,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
}
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
if (btrfs_file_extent_compression(wc->log_leaf, item)) {
csum_start = ins.objectid;
@@ -967,7 +972,6 @@ static int unlink_inode_for_log_replay(struct walk_control *wc,
* item
*/
static noinline int drop_one_dir_item(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_inode *dir,
struct btrfs_dir_item *di)
{
@@ -975,12 +979,10 @@ static noinline int drop_one_dir_item(struct walk_control *wc,
struct btrfs_root *root = dir->root;
struct btrfs_inode *inode;
struct fscrypt_str name;
- struct extent_buffer *leaf;
+ struct extent_buffer *leaf = wc->subvol_path->nodes[0];
struct btrfs_key location;
int ret;
- leaf = path->nodes[0];
-
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) {
@@ -988,7 +990,7 @@ static noinline int drop_one_dir_item(struct walk_control *wc,
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
inode = btrfs_iget_logging(location.objectid, root);
if (IS_ERR(inode)) {
@@ -998,7 +1000,7 @@ static noinline int drop_one_dir_item(struct walk_control *wc,
goto out;
}
- ret = link_to_fixup_dir(wc, path, location.objectid);
+ ret = link_to_fixup_dir(wc, location.objectid);
if (ret)
goto out;
@@ -1097,13 +1099,12 @@ static noinline int backref_in_log(struct btrfs_root *log,
}
static int unlink_refs_not_in_log(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_key *search_key,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
u64 parent_objectid)
{
- struct extent_buffer *leaf = path->nodes[0];
+ struct extent_buffer *leaf = wc->subvol_path->nodes[0];
unsigned long ptr;
unsigned long ptr_end;
@@ -1112,8 +1113,8 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
* log. If so, we allow them to stay otherwise they must be unlinked as
* a conflict.
*/
- ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
- ptr_end = ptr + btrfs_item_size(leaf, path->slots[0]);
+ 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;
@@ -1141,7 +1142,7 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
}
inc_nlink(&inode->vfs_inode);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
ret = unlink_inode_for_log_replay(wc, dir, inode, &victim_name);
kfree(victim_name.name);
@@ -1154,15 +1155,14 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
}
static int unlink_extrefs_not_in_log(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_key *search_key,
struct btrfs_inode *inode,
u64 inode_objectid,
u64 parent_objectid)
{
- struct extent_buffer *leaf = path->nodes[0];
- const unsigned long base = btrfs_item_ptr_offset(leaf, path->slots[0]);
- const u32 item_size = btrfs_item_size(leaf, path->slots[0]);
+ struct extent_buffer *leaf = wc->subvol_path->nodes[0];
+ const unsigned long base = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]);
+ const u32 item_size = btrfs_item_size(leaf, wc->subvol_path->slots[0]);
u32 cur_offset = 0;
while (cur_offset < item_size) {
@@ -1213,7 +1213,7 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
}
inc_nlink(&inode->vfs_inode);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
ret = unlink_inode_for_log_replay(wc, victim_parent, inode,
&victim_name);
@@ -1228,7 +1228,6 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
}
static inline int __add_inode_ref(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
u64 inode_objectid, u64 parent_objectid,
@@ -1246,7 +1245,7 @@ static inline int __add_inode_ref(struct walk_control *wc,
search_key.objectid = inode_objectid;
search_key.type = BTRFS_INODE_REF_KEY;
search_key.offset = parent_objectid;
- ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
return ret;
@@ -1258,53 +1257,54 @@ static inline int __add_inode_ref(struct walk_control *wc,
if (search_key.objectid == search_key.offset)
return 1;
- ret = unlink_refs_not_in_log(wc, path, &search_key, dir, inode,
+ ret = unlink_refs_not_in_log(wc, &search_key, dir, inode,
parent_objectid);
if (ret == -EAGAIN)
goto again;
else if (ret)
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* Same search but for extended refs */
- extref = btrfs_lookup_inode_extref(root, path, name, inode_objectid, parent_objectid);
+ extref = btrfs_lookup_inode_extref(root, wc->subvol_path, name,
+ inode_objectid, parent_objectid);
if (IS_ERR(extref)) {
return PTR_ERR(extref);
} else if (extref) {
- ret = unlink_extrefs_not_in_log(wc, path, &search_key, inode,
+ ret = unlink_extrefs_not_in_log(wc, &search_key, inode,
inode_objectid, parent_objectid);
if (ret == -EAGAIN)
goto again;
else if (ret)
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* look for a conflicting sequence number */
- di = btrfs_lookup_dir_index_item(trans, root, path, btrfs_ino(dir),
+ di = btrfs_lookup_dir_index_item(trans, root, wc->subvol_path, btrfs_ino(dir),
ref_index, name, 0);
if (IS_ERR(di)) {
ret = PTR_ERR(di);
btrfs_abort_transaction(trans, ret);
return ret;
} else if (di) {
- ret = drop_one_dir_item(wc, path, dir, di);
+ ret = drop_one_dir_item(wc, dir, di);
if (ret)
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* look for a conflicting name */
- di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir), name, 0);
+ di = btrfs_lookup_dir_item(trans, root, wc->subvol_path, btrfs_ino(dir), name, 0);
if (IS_ERR(di)) {
return PTR_ERR(di);
} else if (di) {
- ret = drop_one_dir_item(wc, path, dir, di);
+ ret = drop_one_dir_item(wc, dir, di);
if (ret)
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return 0;
}
@@ -1357,9 +1357,7 @@ static int ref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
* proper unlink of that name (that is, remove its entry from the inode
* reference item and both dir index keys).
*/
-static int unlink_old_inode_refs(struct walk_control *wc,
- struct btrfs_path *path,
- struct btrfs_inode *inode)
+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;
@@ -1369,8 +1367,8 @@ static int unlink_old_inode_refs(struct walk_control *wc,
struct extent_buffer *eb;
again:
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0);
+ btrfs_release_path(wc->subvol_path);
+ ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0);
if (ret > 0) {
ret = 0;
goto out;
@@ -1380,9 +1378,9 @@ static int unlink_old_inode_refs(struct walk_control *wc,
goto out;
}
- eb = path->nodes[0];
- ref_ptr = btrfs_item_ptr_offset(eb, path->slots[0]);
- ref_end = ref_ptr + btrfs_item_size(eb, path->slots[0]);
+ eb = wc->subvol_path->nodes[0];
+ ref_ptr = btrfs_item_ptr_offset(eb, wc->subvol_path->slots[0]);
+ ref_end = ref_ptr + btrfs_item_size(eb, wc->subvol_path->slots[0]);
while (ref_ptr < ref_end) {
struct fscrypt_str name;
u64 parent_id;
@@ -1413,7 +1411,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
if (!ret) {
struct btrfs_inode *dir;
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
dir = btrfs_iget_logging(parent_id, root);
if (IS_ERR(dir)) {
ret = PTR_ERR(dir);
@@ -1438,7 +1436,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
}
ret = 0;
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return ret;
}
@@ -1446,7 +1444,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
* Replay one inode back reference item found in the log tree.
* Path is for temporary use by this function (it should be released on return).
*/
-static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *path)
+static noinline int add_inode_ref(struct walk_control *wc)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1548,8 +1546,8 @@ static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *pa
}
}
- ret = inode_in_dir(root, path, btrfs_ino(dir), btrfs_ino(inode),
- ref_index, &name);
+ 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);
goto out;
@@ -1561,7 +1559,7 @@ static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *pa
* overwrite any existing back reference, and we don't
* want to create dangling pointers in the directory.
*/
- ret = __add_inode_ref(wc, path, dir, inode, inode_objectid,
+ ret = __add_inode_ref(wc, dir, inode, inode_objectid,
parent_objectid, ref_index, &name);
if (ret) {
if (ret == 1)
@@ -1602,14 +1600,14 @@ static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *pa
* dir index entries exist for a name but there is no inode reference
* item with the same name.
*/
- ret = unlink_old_inode_refs(wc, path, inode);
+ ret = unlink_old_inode_refs(wc, inode);
if (ret)
goto out;
/* finally write the back reference in the inode */
- ret = overwrite_item(wc, path);
+ ret = overwrite_item(wc);
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
kfree(name.name);
if (dir)
iput(&dir->vfs_inode);
@@ -1728,7 +1726,6 @@ static int count_inode_refs(struct btrfs_inode *inode, struct btrfs_path *path)
* will free the inode.
*/
static noinline int fixup_inode_link_count(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_inode *inode)
{
struct btrfs_trans_handle *trans = wc->trans;
@@ -1737,13 +1734,13 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,
u64 nlink = 0;
const u64 ino = btrfs_ino(inode);
- ret = count_inode_refs(inode, path);
+ ret = count_inode_refs(inode, wc->subvol_path);
if (ret < 0)
goto out;
nlink = ret;
- ret = count_inode_extrefs(inode, path);
+ ret = count_inode_extrefs(inode, wc->subvol_path);
if (ret < 0)
goto out;
@@ -1762,7 +1759,7 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,
if (inode->vfs_inode.i_nlink == 0) {
if (S_ISDIR(inode->vfs_inode.i_mode)) {
- ret = replay_dir_deletes(wc, path, ino, true);
+ ret = replay_dir_deletes(wc, ino, true);
if (ret)
goto out;
}
@@ -1772,12 +1769,11 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,
}
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return ret;
}
-static noinline int fixup_inode_link_counts(struct walk_control *wc,
- struct btrfs_path *path)
+static noinline int fixup_inode_link_counts(struct walk_control *wc)
{
int ret;
struct btrfs_key key;
@@ -1790,34 +1786,34 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
struct btrfs_root *root = wc->root;
struct btrfs_inode *inode;
- ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ ret = btrfs_search_slot(trans, root, &key, wc->subvol_path, -1, 1);
if (ret < 0)
break;
if (ret == 1) {
ret = 0;
- if (path->slots[0] == 0)
+ if (wc->subvol_path->slots[0] == 0)
break;
- path->slots[0]--;
+ wc->subvol_path->slots[0]--;
}
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &key, wc->subvol_path->slots[0]);
if (key.objectid != BTRFS_TREE_LOG_FIXUP_OBJECTID ||
key.type != BTRFS_ORPHAN_ITEM_KEY)
break;
- ret = btrfs_del_item(trans, root, path);
+ ret = btrfs_del_item(trans, root, wc->subvol_path);
if (ret)
break;
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
inode = btrfs_iget_logging(key.offset, root);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
break;
}
- ret = fixup_inode_link_count(wc, path, inode);
+ ret = fixup_inode_link_count(wc, inode);
iput(&inode->vfs_inode);
if (ret)
break;
@@ -1829,7 +1825,7 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
*/
key.offset = (u64)-1;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return ret;
}
@@ -1839,9 +1835,7 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
* count when replay is done. The link count is incremented here
* so the inode won't go away until we check it
*/
-static noinline int link_to_fixup_dir(struct walk_control *wc,
- struct btrfs_path *path,
- u64 objectid)
+static noinline int link_to_fixup_dir(struct walk_control *wc, u64 objectid)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1862,9 +1856,9 @@ static noinline int link_to_fixup_dir(struct walk_control *wc,
key.type = BTRFS_ORPHAN_ITEM_KEY;
key.offset = objectid;
- ret = btrfs_insert_empty_item(trans, root, path, &key, 0);
+ ret = btrfs_insert_empty_item(trans, root, wc->subvol_path, &key, 0);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
if (ret == 0) {
if (!vfs_inode->i_nlink)
set_nlink(vfs_inode, 1);
@@ -1917,7 +1911,6 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans,
static int delete_conflicting_dir_entry(struct walk_control *wc,
struct btrfs_inode *dir,
- struct btrfs_path *path,
struct btrfs_dir_item *dst_di,
const struct btrfs_key *log_key,
u8 log_flags,
@@ -1925,12 +1918,12 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,
{
struct btrfs_key found_key;
- btrfs_dir_item_key_to_cpu(path->nodes[0], dst_di, &found_key);
+ btrfs_dir_item_key_to_cpu(wc->subvol_path->nodes[0], dst_di, &found_key);
/* The existing dentry points to the same inode, don't delete it. */
if (found_key.objectid == log_key->objectid &&
found_key.type == log_key->type &&
found_key.offset == log_key->offset &&
- btrfs_dir_flags(path->nodes[0], dst_di) == log_flags)
+ btrfs_dir_flags(wc->subvol_path->nodes[0], dst_di) == log_flags)
return 1;
/*
@@ -1940,7 +1933,7 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,
if (!exists)
return 0;
- return drop_one_dir_item(wc, path, dir, dst_di);
+ return drop_one_dir_item(wc, dir, dst_di);
}
/*
@@ -1959,9 +1952,7 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,
* Returns < 0 on error, 0 if the name wasn't replayed (dentry points to a
* non-existing inode) and 1 if the name was replayed.
*/
-static noinline int replay_one_name(struct walk_control *wc,
- struct btrfs_path *path,
- struct btrfs_dir_item *di)
+static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_item *di)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1995,8 +1986,8 @@ static noinline int replay_one_name(struct walk_control *wc,
log_flags = btrfs_dir_flags(wc->log_leaf, di);
btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &log_key);
- ret = btrfs_lookup_inode(trans, root, path, &log_key, 0);
- btrfs_release_path(path);
+ 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);
goto out;
@@ -2004,14 +1995,14 @@ static noinline int replay_one_name(struct walk_control *wc,
exists = (ret == 0);
ret = 0;
- dir_dst_di = btrfs_lookup_dir_item(trans, root, path, wc->log_key.objectid,
- &name, 1);
+ dir_dst_di = btrfs_lookup_dir_item(trans, root, wc->subvol_path,
+ wc->log_key.objectid, &name, 1);
if (IS_ERR(dir_dst_di)) {
ret = PTR_ERR(dir_dst_di);
btrfs_abort_transaction(trans, ret);
goto out;
} else if (dir_dst_di) {
- ret = delete_conflicting_dir_entry(wc, dir, path, 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);
@@ -2020,9 +2011,9 @@ static noinline int replay_one_name(struct walk_control *wc,
dir_dst_matches = (ret == 1);
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
- index_dst_di = btrfs_lookup_dir_index_item(trans, root, path,
+ index_dst_di = btrfs_lookup_dir_index_item(trans, root, wc->subvol_path,
wc->log_key.objectid,
wc->log_key.offset, &name, 1);
if (IS_ERR(index_dst_di)) {
@@ -2030,7 +2021,7 @@ static noinline int replay_one_name(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
goto out;
} else if (index_dst_di) {
- ret = delete_conflicting_dir_entry(wc, dir, path, 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);
@@ -2039,7 +2030,7 @@ static noinline int replay_one_name(struct walk_control *wc,
index_dst_matches = (ret == 1);
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
if (dir_dst_matches && index_dst_matches) {
ret = 0;
@@ -2104,8 +2095,7 @@ static noinline int replay_one_name(struct walk_control *wc,
}
/* Replay one dir item from a BTRFS_DIR_INDEX_KEY key. */
-static noinline int replay_one_dir_item(struct walk_control *wc,
- struct btrfs_path *path)
+static noinline int replay_one_dir_item(struct walk_control *wc)
{
int ret;
struct btrfs_dir_item *di;
@@ -2114,7 +2104,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
ASSERT(wc->log_key.type == BTRFS_DIR_INDEX_KEY);
di = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_dir_item);
- ret = replay_one_name(wc, path, di);
+ ret = replay_one_name(wc, di);
if (ret < 0)
return ret;
@@ -2148,7 +2138,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
struct btrfs_key di_key;
btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &di_key);
- ret = link_to_fixup_dir(wc, path, di_key.objectid);
+ ret = link_to_fixup_dir(wc, di_key.objectid);
}
return ret;
@@ -2242,7 +2232,6 @@ static noinline int find_dir_range(struct btrfs_root *root,
* to is unlinked
*/
static noinline int check_item_in_log(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_path *log_path,
struct btrfs_inode *dir,
struct btrfs_key *dir_key,
@@ -2266,8 +2255,8 @@ static noinline int check_item_in_log(struct walk_control *wc,
*/
ASSERT(dir_key->type == BTRFS_DIR_INDEX_KEY);
- eb = path->nodes[0];
- slot = path->slots[0];
+ eb = wc->subvol_path->nodes[0];
+ slot = wc->subvol_path->slots[0];
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) {
@@ -2293,7 +2282,7 @@ static noinline int check_item_in_log(struct walk_control *wc,
}
btrfs_dir_item_key_to_cpu(eb, di, &location);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
btrfs_release_path(log_path);
inode = btrfs_iget_logging(location.objectid, root);
if (IS_ERR(inode)) {
@@ -2303,7 +2292,7 @@ static noinline int check_item_in_log(struct walk_control *wc,
goto out;
}
- ret = link_to_fixup_dir(wc, path, location.objectid);
+ ret = link_to_fixup_dir(wc, location.objectid);
if (ret)
goto out;
@@ -2315,7 +2304,7 @@ static noinline int check_item_in_log(struct walk_control *wc,
* (an index number), so we're done.
*/
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
btrfs_release_path(log_path);
kfree(name.name);
if (inode)
@@ -2323,7 +2312,7 @@ static noinline int check_item_in_log(struct walk_control *wc,
return ret;
}
-static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path)
+static int replay_xattr_deletes(struct walk_control *wc)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -2331,7 +2320,6 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
struct btrfs_key search_key;
struct btrfs_path *log_path;
const u64 ino = wc->log_key.objectid;
- int i;
int nritems;
int ret;
@@ -2345,32 +2333,32 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
search_key.type = BTRFS_XATTR_ITEM_KEY;
search_key.offset = 0;
again:
- ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
goto out;
}
process_leaf:
- nritems = btrfs_header_nritems(path->nodes[0]);
- for (i = path->slots[0]; i < nritems; i++) {
+ nritems = btrfs_header_nritems(wc->subvol_path->nodes[0]);
+ for (int i = wc->subvol_path->slots[0]; i < nritems; i++) {
struct btrfs_key key;
struct btrfs_dir_item *di;
struct btrfs_dir_item *log_di;
u32 total_size;
u32 cur;
- btrfs_item_key_to_cpu(path->nodes[0], &key, i);
+ btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &key, i);
if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY) {
ret = 0;
goto out;
}
- di = btrfs_item_ptr(path->nodes[0], i, struct btrfs_dir_item);
- total_size = btrfs_item_size(path->nodes[0], i);
+ di = btrfs_item_ptr(wc->subvol_path->nodes[0], i, struct btrfs_dir_item);
+ total_size = btrfs_item_size(wc->subvol_path->nodes[0], i);
cur = 0;
while (cur < total_size) {
- u16 name_len = btrfs_dir_name_len(path->nodes[0], di);
- u16 data_len = btrfs_dir_data_len(path->nodes[0], di);
+ u16 name_len = btrfs_dir_name_len(wc->subvol_path->nodes[0], di);
+ u16 data_len = btrfs_dir_data_len(wc->subvol_path->nodes[0], di);
u32 this_len = sizeof(*di) + name_len + data_len;
char *name;
@@ -2380,7 +2368,7 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
btrfs_abort_transaction(trans, ret);
goto out;
}
- read_extent_buffer(path->nodes[0], name,
+ read_extent_buffer(wc->subvol_path->nodes[0], name,
(unsigned long)(di + 1), name_len);
log_di = btrfs_lookup_xattr(NULL, log, log_path, ino,
@@ -2388,8 +2376,8 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
btrfs_release_path(log_path);
if (!log_di) {
/* Doesn't exist in log tree, so delete it. */
- btrfs_release_path(path);
- di = btrfs_lookup_xattr(trans, root, path, ino,
+ 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)) {
@@ -2399,12 +2387,12 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
}
ASSERT(di);
ret = btrfs_delete_one_dir_name(trans, root,
- path, di);
+ wc->subvol_path, di);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
search_key = key;
goto again;
}
@@ -2418,7 +2406,7 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
di = (struct btrfs_dir_item *)((char *)di + this_len);
}
}
- ret = btrfs_next_leaf(root, path);
+ ret = btrfs_next_leaf(root, wc->subvol_path);
if (ret > 0)
ret = 0;
else if (ret == 0)
@@ -2427,7 +2415,7 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
btrfs_abort_transaction(trans, ret);
out:
btrfs_free_path(log_path);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return ret;
}
@@ -2443,7 +2431,6 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
* directory.
*/
static noinline int replay_dir_deletes(struct walk_control *wc,
- struct btrfs_path *path,
u64 dirid, bool del_all)
{
struct btrfs_trans_handle *trans = wc->trans;
@@ -2486,7 +2473,7 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
if (del_all)
range_end = (u64)-1;
else {
- ret = find_dir_range(log, path, dirid,
+ ret = find_dir_range(log, wc->subvol_path, dirid,
&range_start, &range_end);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
@@ -2499,16 +2486,16 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
dir_key.offset = range_start;
while (1) {
int nritems;
- ret = btrfs_search_slot(NULL, root, &dir_key, path,
- 0, 0);
+ ret = btrfs_search_slot(NULL, root, &dir_key,
+ wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
goto out;
}
- nritems = btrfs_header_nritems(path->nodes[0]);
- if (path->slots[0] >= nritems) {
- ret = btrfs_next_leaf(root, path);
+ nritems = btrfs_header_nritems(wc->subvol_path->nodes[0]);
+ if (wc->subvol_path->slots[0] >= nritems) {
+ ret = btrfs_next_leaf(root, wc->subvol_path);
if (ret == 1) {
break;
} else if (ret < 0) {
@@ -2516,8 +2503,8 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
goto out;
}
}
- btrfs_item_key_to_cpu(path->nodes[0], &found_key,
- path->slots[0]);
+ btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &found_key,
+ wc->subvol_path->slots[0]);
if (found_key.objectid != dirid ||
found_key.type != dir_key.type) {
ret = 0;
@@ -2527,22 +2514,21 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
if (found_key.offset > range_end)
break;
- ret = check_item_in_log(wc, path, log_path, dir,
- &found_key, del_all);
+ ret = check_item_in_log(wc, log_path, dir, &found_key, del_all);
if (ret)
goto out;
if (found_key.offset == (u64)-1)
break;
dir_key.offset = found_key.offset + 1;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
if (range_end == (u64)-1)
break;
range_start = range_end + 1;
}
ret = 0;
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
btrfs_free_path(log_path);
iput(&dir->vfs_inode);
return ret;
@@ -2567,7 +2553,6 @@ static int replay_one_buffer(struct extent_buffer *eb,
.transid = gen,
.level = level
};
- struct btrfs_path *path;
struct btrfs_root *root = wc->root;
struct btrfs_trans_handle *trans = wc->trans;
int ret;
@@ -2581,8 +2566,9 @@ static int replay_one_buffer(struct extent_buffer *eb,
return ret;
}
- path = btrfs_alloc_path();
- if (!path) {
+ ASSERT(wc->subvol_path == NULL);
+ wc->subvol_path = btrfs_alloc_path();
+ if (!wc->subvol_path) {
btrfs_abort_transaction(trans, -ENOMEM);
return -ENOMEM;
}
@@ -2630,16 +2616,16 @@ static int replay_one_buffer(struct extent_buffer *eb,
wc->stage == LOG_WALK_REPLAY_INODES) {
u32 mode;
- ret = replay_xattr_deletes(wc, path);
+ ret = replay_xattr_deletes(wc);
if (ret)
break;
mode = btrfs_inode_mode(eb, inode_item);
if (S_ISDIR(mode)) {
- ret = replay_dir_deletes(wc, path, wc->log_key.objectid, false);
+ ret = replay_dir_deletes(wc, wc->log_key.objectid, false);
if (ret)
break;
}
- ret = overwrite_item(wc, path);
+ ret = overwrite_item(wc);
if (ret)
break;
@@ -2667,7 +2653,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
drop_args.start = from;
drop_args.end = (u64)-1;
drop_args.drop_cache = true;
- drop_args.path = path;
+ drop_args.path = wc->subvol_path;
ret = btrfs_drop_extents(trans, root, inode, &drop_args);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -2684,7 +2670,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
}
- ret = link_to_fixup_dir(wc, path, wc->log_key.objectid);
+ ret = link_to_fixup_dir(wc, wc->log_key.objectid);
if (ret)
break;
}
@@ -2694,7 +2680,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (wc->log_key.type == BTRFS_DIR_INDEX_KEY &&
wc->stage == LOG_WALK_REPLAY_DIR_INDEX) {
- ret = replay_one_dir_item(wc, path);
+ ret = replay_one_dir_item(wc);
if (ret)
break;
}
@@ -2704,16 +2690,16 @@ static int replay_one_buffer(struct extent_buffer *eb,
/* these keys are simply copied */
if (wc->log_key.type == BTRFS_XATTR_ITEM_KEY) {
- ret = overwrite_item(wc, path);
+ ret = overwrite_item(wc);
if (ret)
break;
} else if (wc->log_key.type == BTRFS_INODE_REF_KEY ||
wc->log_key.type == BTRFS_INODE_EXTREF_KEY) {
- ret = add_inode_ref(wc, path);
+ ret = add_inode_ref(wc);
if (ret)
break;
} else if (wc->log_key.type == BTRFS_EXTENT_DATA_KEY) {
- ret = replay_one_extent(wc, path);
+ ret = replay_one_extent(wc);
if (ret)
break;
}
@@ -2724,7 +2710,8 @@ static int replay_one_buffer(struct extent_buffer *eb,
* older kernel with such keys, ignore them.
*/
}
- btrfs_free_path(path);
+ btrfs_free_path(wc->subvol_path);
+ wc->subvol_path = NULL;
return ret;
}
@@ -7515,7 +7502,9 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
if (wc.stage == LOG_WALK_REPLAY_ALL) {
struct btrfs_root *root = wc.root;
- ret = fixup_inode_link_counts(&wc, path);
+ wc.subvol_path = path;
+ ret = fixup_inode_link_counts(&wc);
+ wc.subvol_path = NULL;
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 29/33] btrfs: stop passing inode object IDs to __add_inode_ref() in log replay
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (27 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 28/33] btrfs: add path for subvolume tree changes to struct walk_control fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 30/33] btrfs: remove pointless inode lookup when processing extrefs during " fdmanana
` (4 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
There's no point in passing the inode and parent inode object IDs to
__add_inode_ref() and its helpers because we can get them by calling
btrfs_ino() against the inode and the directory inode, and we pass both
inodes to __add_inode_ref() and its helpers. So remove the object IDs
parameters to reduce arguments passed and to make things less confusing.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 36 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 21 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 747daaaaf332..6186300923b7 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1101,8 +1101,7 @@ static noinline int backref_in_log(struct btrfs_root *log,
static int unlink_refs_not_in_log(struct walk_control *wc,
struct btrfs_key *search_key,
struct btrfs_inode *dir,
- struct btrfs_inode *inode,
- u64 parent_objectid)
+ struct btrfs_inode *inode)
{
struct extent_buffer *leaf = wc->subvol_path->nodes[0];
unsigned long ptr;
@@ -1130,7 +1129,7 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
return ret;
}
- ret = backref_in_log(wc->log, search_key, parent_objectid, &victim_name);
+ ret = backref_in_log(wc->log, search_key, btrfs_ino(dir), &victim_name);
if (ret) {
kfree(victim_name.name);
if (ret < 0) {
@@ -1156,9 +1155,8 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
static int unlink_extrefs_not_in_log(struct walk_control *wc,
struct btrfs_key *search_key,
- struct btrfs_inode *inode,
- u64 inode_objectid,
- u64 parent_objectid)
+ struct btrfs_inode *dir,
+ struct btrfs_inode *inode)
{
struct extent_buffer *leaf = wc->subvol_path->nodes[0];
const unsigned long base = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]);
@@ -1177,7 +1175,7 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
extref = (struct btrfs_inode_extref *)(base + cur_offset);
victim_name.len = btrfs_inode_extref_name_len(leaf, extref);
- if (btrfs_inode_extref_parent(leaf, extref) != parent_objectid)
+ if (btrfs_inode_extref_parent(leaf, extref) != btrfs_ino(dir))
goto next;
ret = read_alloc_one_name(leaf, &extref->name, victim_name.len,
@@ -1187,12 +1185,12 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
return ret;
}
- search_key->objectid = inode_objectid;
+ search_key->objectid = btrfs_ino(inode);
search_key->type = BTRFS_INODE_EXTREF_KEY;
- search_key->offset = btrfs_extref_hash(parent_objectid,
+ search_key->offset = btrfs_extref_hash(btrfs_ino(dir),
victim_name.name,
victim_name.len);
- ret = backref_in_log(log_root, search_key, parent_objectid, &victim_name);
+ ret = backref_in_log(log_root, search_key, btrfs_ino(dir), &victim_name);
if (ret) {
kfree(victim_name.name);
if (ret < 0) {
@@ -1204,7 +1202,7 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
continue;
}
- victim_parent = btrfs_iget_logging(parent_objectid, root);
+ victim_parent = btrfs_iget_logging(btrfs_ino(dir), root);
if (IS_ERR(victim_parent)) {
kfree(victim_name.name);
ret = PTR_ERR(victim_parent);
@@ -1230,7 +1228,6 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
static inline int __add_inode_ref(struct walk_control *wc,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
- u64 inode_objectid, u64 parent_objectid,
u64 ref_index, struct fscrypt_str *name)
{
int ret;
@@ -1242,9 +1239,9 @@ static inline int __add_inode_ref(struct walk_control *wc,
again:
/* Search old style refs */
- search_key.objectid = inode_objectid;
+ search_key.objectid = btrfs_ino(inode);
search_key.type = BTRFS_INODE_REF_KEY;
- search_key.offset = parent_objectid;
+ 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);
@@ -1257,8 +1254,7 @@ static inline int __add_inode_ref(struct walk_control *wc,
if (search_key.objectid == search_key.offset)
return 1;
- ret = unlink_refs_not_in_log(wc, &search_key, dir, inode,
- parent_objectid);
+ ret = unlink_refs_not_in_log(wc, &search_key, dir, inode);
if (ret == -EAGAIN)
goto again;
else if (ret)
@@ -1268,12 +1264,11 @@ static inline int __add_inode_ref(struct walk_control *wc,
/* Same search but for extended refs */
extref = btrfs_lookup_inode_extref(root, wc->subvol_path, name,
- inode_objectid, parent_objectid);
+ btrfs_ino(inode), btrfs_ino(dir));
if (IS_ERR(extref)) {
return PTR_ERR(extref);
} else if (extref) {
- ret = unlink_extrefs_not_in_log(wc, &search_key, inode,
- inode_objectid, parent_objectid);
+ ret = unlink_extrefs_not_in_log(wc, &search_key, dir, inode);
if (ret == -EAGAIN)
goto again;
else if (ret)
@@ -1559,8 +1554,7 @@ static noinline int add_inode_ref(struct walk_control *wc)
* overwrite any existing back reference, and we don't
* want to create dangling pointers in the directory.
*/
- ret = __add_inode_ref(wc, dir, inode, inode_objectid,
- parent_objectid, ref_index, &name);
+ ret = __add_inode_ref(wc, dir, inode, ref_index, &name);
if (ret) {
if (ret == 1)
ret = 0;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 30/33] btrfs: remove pointless inode lookup when processing extrefs during log replay
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (28 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 29/33] btrfs: stop passing inode object IDs to __add_inode_ref() in log replay fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 31/33] btrfs: abort transaction if we fail to find dir item " fdmanana
` (3 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
At unlink_extrefs_not_in_log() we do an inode lookup of the directory but
we already have the directory inode accessible as a function argument, so
the lookup is redudant. Remove it and use the directory inode passed in as
an argument.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 6186300923b7..86c595ef57f4 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1165,10 +1165,8 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
while (cur_offset < item_size) {
struct btrfs_trans_handle *trans = wc->trans;
- struct btrfs_root *root = wc->root;
struct btrfs_root *log_root = wc->log;
struct btrfs_inode_extref *extref;
- struct btrfs_inode *victim_parent;
struct fscrypt_str victim_name;
int ret;
@@ -1202,20 +1200,10 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
continue;
}
- victim_parent = btrfs_iget_logging(btrfs_ino(dir), root);
- if (IS_ERR(victim_parent)) {
- kfree(victim_name.name);
- ret = PTR_ERR(victim_parent);
- btrfs_abort_transaction(trans, ret);
- return ret;
- }
-
inc_nlink(&inode->vfs_inode);
btrfs_release_path(wc->subvol_path);
- ret = unlink_inode_for_log_replay(wc, victim_parent, inode,
- &victim_name);
- iput(&victim_parent->vfs_inode);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &victim_name);
kfree(victim_name.name);
if (ret)
return ret;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 31/33] btrfs: abort transaction if we fail to find dir item during log replay
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (29 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 30/33] btrfs: remove pointless inode lookup when processing extrefs during " fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 32/33] btrfs: abort transaction if we fail to update inode in log replay dir fixup fdmanana
` (2 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
At __add_inode_ref() if we get an error when trying to lookup a dir item
we don't abort the transaction and propagate the error up the call chain,
so that somewhere else up in the call chain the transaction is aborted.
This however makes it hard to know that the failure comes from looking up
a dir item, so add a transaction abort in case we fail there, so that we
immediately pinpoint where the problem comes from during log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 86c595ef57f4..7b91248b38dc 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1281,7 +1281,9 @@ static inline int __add_inode_ref(struct walk_control *wc,
/* look for a conflicting name */
di = btrfs_lookup_dir_item(trans, root, wc->subvol_path, btrfs_ino(dir), name, 0);
if (IS_ERR(di)) {
- return PTR_ERR(di);
+ ret = PTR_ERR(di);
+ btrfs_abort_transaction(trans, ret);
+ return ret;
} else if (di) {
ret = drop_one_dir_item(wc, dir, di);
if (ret)
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 32/33] btrfs: abort transaction if we fail to update inode in log replay dir fixup
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (30 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 31/33] btrfs: abort transaction if we fail to find dir item " fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-05 16:10 ` [PATCH 33/33] btrfs: dump detailed info and specific messages on log replay failures fdmanana
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
If we fail to update the inode at link_to_fixup_dir(), we don't abort the
transaction and propagate the error up the call chain, which makes it hard
to pinpoint the error to the inode update. So abort the transaction if the
inode update call fails, so that if it happens we known immediately.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 7b91248b38dc..83b79023baae 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1849,6 +1849,8 @@ static noinline int link_to_fixup_dir(struct walk_control *wc, u64 objectid)
else
inc_nlink(vfs_inode);
ret = btrfs_update_inode(trans, inode);
+ if (ret)
+ btrfs_abort_transaction(trans, ret);
} else if (ret == -EEXIST) {
ret = 0;
} else {
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH 33/33] btrfs: dump detailed info and specific messages on log replay failures
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (31 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 32/33] btrfs: abort transaction if we fail to update inode in log replay dir fixup fdmanana
@ 2025-09-05 16:10 ` fdmanana
2025-09-07 11:25 ` Dan Carpenter
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
33 siblings, 1 reply; 72+ messages in thread
From: fdmanana @ 2025-09-05 16:10 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
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>
---
fs/btrfs/fs.h | 2 +
fs/btrfs/tree-log.c | 425 +++++++++++++++++++++++++++++++++++---------
2 files changed, 343 insertions(+), 84 deletions(-)
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 5f0b185a7f21..28bb52bc33f1 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/tree-log.c b/fs/btrfs/tree-log.c
index 83b79023baae..f134f217a755 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 @@ static int overwrite_item(struct walk_control *wc)
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 @@ static inline int __add_inode_ref(struct walk_control *wc,
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 @@ static inline int __add_inode_ref(struct walk_control *wc,
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 @@ static inline int __add_inode_ref(struct walk_control *wc,
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 @@ static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *in
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 @@ static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *in
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 @@ static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *in
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 @@ static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_it
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 @@ static int replay_xattr_deletes(struct walk_control *wc)
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,
@@ -2368,14 +2588,20 @@ static int replay_xattr_deletes(struct walk_control *wc)
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));
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));
goto out;
}
btrfs_release_path(wc->subvol_path);
@@ -2385,7 +2611,10 @@ static int replay_xattr_deletes(struct walk_control *wc)
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));
goto out;
}
cur += this_len;
@@ -2398,7 +2627,9 @@ static int replay_xattr_deletes(struct walk_control *wc)
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 +2650,6 @@ static int replay_xattr_deletes(struct walk_control *wc)
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 +2664,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 +2679,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 +2694,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 +2709,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 +2723,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 +2786,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 +2878,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 +2892,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)
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [PATCH 33/33] btrfs: dump detailed info and specific messages on log replay failures
2025-09-05 16:10 ` [PATCH 33/33] btrfs: dump detailed info and specific messages on log replay failures fdmanana
@ 2025-09-07 11:25 ` Dan Carpenter
0 siblings, 0 replies; 72+ messages in thread
From: Dan Carpenter @ 2025-09-07 11:25 UTC (permalink / raw)
To: oe-kbuild, fdmanana, linux-btrfs; +Cc: lkp, oe-kbuild-all
Hi,
kernel test robot noticed the following build warnings:
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/fdmanana-kernel-org/btrfs-fix-invalid-extref-key-setup-when-replaying-dentry/20250906-001715
base: https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
patch link: https://lore.kernel.org/r/f0623e4f3a69cd61238551c1e5b44fc31077db16.1757075118.git.fdmanana%40suse.com
patch subject: [PATCH 33/33] btrfs: dump detailed info and specific messages on log replay failures
config: i386-randconfig-141-20250906 (https://download.01.org/0day-ci/archive/20250907/202509070931.zy8HHIta-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202509070931.zy8HHIta-lkp@intel.com/
New smatch warnings:
fs/btrfs/tree-log.c:2591 replay_xattr_deletes() warn: passing freed memory 'name' (line 2588)
Old smatch warnings:
fs/btrfs/tree-log.c:2601 replay_xattr_deletes() warn: passing freed memory 'name' (line 2588)
fs/btrfs/tree-log.c:2614 replay_xattr_deletes() warn: passing freed memory 'name' (line 2611)
vim +/name +2591 fs/btrfs/tree-log.c
51abc774a9b1d4 Filipe Manana 2025-09-05 2517 static int replay_xattr_deletes(struct walk_control *wc)
4f764e5153616f Filipe Manana 2015-02-23 2518 {
b75ff3e7b377e8 Filipe Manana 2025-09-05 2519 struct btrfs_trans_handle *trans = wc->trans;
b75ff3e7b377e8 Filipe Manana 2025-09-05 2520 struct btrfs_root *root = wc->root;
b75ff3e7b377e8 Filipe Manana 2025-09-05 2521 struct btrfs_root *log = wc->log;
4f764e5153616f Filipe Manana 2015-02-23 2522 struct btrfs_key search_key;
4f764e5153616f Filipe Manana 2015-02-23 2523 struct btrfs_path *log_path;
b048ef711534b2 Filipe Manana 2025-09-05 2524 const u64 ino = wc->log_key.objectid;
4f764e5153616f Filipe Manana 2015-02-23 2525 int nritems;
4f764e5153616f Filipe Manana 2015-02-23 2526 int ret;
4f764e5153616f Filipe Manana 2015-02-23 2527
4f764e5153616f Filipe Manana 2015-02-23 2528 log_path = btrfs_alloc_path();
fa7c5927bfe412 Filipe Manana 2025-07-18 2529 if (!log_path) {
c2bd309cef030f Filipe Manana 2025-09-05 2530 btrfs_abort_log_replay(wc, -ENOMEM, "failed to allocate path");
4f764e5153616f Filipe Manana 2015-02-23 2531 return -ENOMEM;
fa7c5927bfe412 Filipe Manana 2025-07-18 2532 }
4f764e5153616f Filipe Manana 2015-02-23 2533
4f764e5153616f Filipe Manana 2015-02-23 2534 search_key.objectid = ino;
4f764e5153616f Filipe Manana 2015-02-23 2535 search_key.type = BTRFS_XATTR_ITEM_KEY;
4f764e5153616f Filipe Manana 2015-02-23 2536 search_key.offset = 0;
4f764e5153616f Filipe Manana 2015-02-23 2537 again:
51abc774a9b1d4 Filipe Manana 2025-09-05 2538 ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);
fa7c5927bfe412 Filipe Manana 2025-07-18 2539 if (ret < 0) {
c2bd309cef030f Filipe Manana 2025-09-05 2540 btrfs_abort_log_replay(wc, ret,
c2bd309cef030f Filipe Manana 2025-09-05 2541 "failed to search xattrs for inode %llu root %llu",
c2bd309cef030f Filipe Manana 2025-09-05 2542 ino, btrfs_root_id(root));
4f764e5153616f Filipe Manana 2015-02-23 2543 goto out;
fa7c5927bfe412 Filipe Manana 2025-07-18 2544 }
4f764e5153616f Filipe Manana 2015-02-23 2545 process_leaf:
51abc774a9b1d4 Filipe Manana 2025-09-05 2546 nritems = btrfs_header_nritems(wc->subvol_path->nodes[0]);
51abc774a9b1d4 Filipe Manana 2025-09-05 2547 for (int i = wc->subvol_path->slots[0]; i < nritems; i++) {
4f764e5153616f Filipe Manana 2015-02-23 2548 struct btrfs_key key;
4f764e5153616f Filipe Manana 2015-02-23 2549 struct btrfs_dir_item *di;
4f764e5153616f Filipe Manana 2015-02-23 2550 struct btrfs_dir_item *log_di;
4f764e5153616f Filipe Manana 2015-02-23 2551 u32 total_size;
4f764e5153616f Filipe Manana 2015-02-23 2552 u32 cur;
4f764e5153616f Filipe Manana 2015-02-23 2553
51abc774a9b1d4 Filipe Manana 2025-09-05 2554 btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &key, i);
4f764e5153616f Filipe Manana 2015-02-23 2555 if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY) {
4f764e5153616f Filipe Manana 2015-02-23 2556 ret = 0;
4f764e5153616f Filipe Manana 2015-02-23 2557 goto out;
4f764e5153616f Filipe Manana 2015-02-23 2558 }
4f764e5153616f Filipe Manana 2015-02-23 2559
51abc774a9b1d4 Filipe Manana 2025-09-05 2560 di = btrfs_item_ptr(wc->subvol_path->nodes[0], i, struct btrfs_dir_item);
51abc774a9b1d4 Filipe Manana 2025-09-05 2561 total_size = btrfs_item_size(wc->subvol_path->nodes[0], i);
4f764e5153616f Filipe Manana 2015-02-23 2562 cur = 0;
4f764e5153616f Filipe Manana 2015-02-23 2563 while (cur < total_size) {
51abc774a9b1d4 Filipe Manana 2025-09-05 2564 u16 name_len = btrfs_dir_name_len(wc->subvol_path->nodes[0], di);
51abc774a9b1d4 Filipe Manana 2025-09-05 2565 u16 data_len = btrfs_dir_data_len(wc->subvol_path->nodes[0], di);
4f764e5153616f Filipe Manana 2015-02-23 2566 u32 this_len = sizeof(*di) + name_len + data_len;
4f764e5153616f Filipe Manana 2015-02-23 2567 char *name;
4f764e5153616f Filipe Manana 2015-02-23 2568
4f764e5153616f Filipe Manana 2015-02-23 2569 name = kmalloc(name_len, GFP_NOFS);
4f764e5153616f Filipe Manana 2015-02-23 2570 if (!name) {
4f764e5153616f Filipe Manana 2015-02-23 2571 ret = -ENOMEM;
c2bd309cef030f Filipe Manana 2025-09-05 2572 btrfs_abort_log_replay(wc, ret,
c2bd309cef030f Filipe Manana 2025-09-05 2573 "failed to allocate memory for name of length %u",
c2bd309cef030f Filipe Manana 2025-09-05 2574 name_len);
4f764e5153616f Filipe Manana 2015-02-23 2575 goto out;
4f764e5153616f Filipe Manana 2015-02-23 2576 }
51abc774a9b1d4 Filipe Manana 2025-09-05 2577 read_extent_buffer(wc->subvol_path->nodes[0], name,
4f764e5153616f Filipe Manana 2015-02-23 2578 (unsigned long)(di + 1), name_len);
4f764e5153616f Filipe Manana 2015-02-23 2579
4f764e5153616f Filipe Manana 2015-02-23 2580 log_di = btrfs_lookup_xattr(NULL, log, log_path, ino,
4f764e5153616f Filipe Manana 2015-02-23 2581 name, name_len, 0);
4f764e5153616f Filipe Manana 2015-02-23 2582 btrfs_release_path(log_path);
4f764e5153616f Filipe Manana 2015-02-23 2583 if (!log_di) {
4f764e5153616f Filipe Manana 2015-02-23 2584 /* Doesn't exist in log tree, so delete it. */
51abc774a9b1d4 Filipe Manana 2025-09-05 2585 btrfs_release_path(wc->subvol_path);
51abc774a9b1d4 Filipe Manana 2025-09-05 2586 di = btrfs_lookup_xattr(trans, root, wc->subvol_path, ino,
4f764e5153616f Filipe Manana 2015-02-23 2587 name, name_len, -1);
4f764e5153616f Filipe Manana 2015-02-23 @2588 kfree(name);
^^^^
kfree()
4f764e5153616f Filipe Manana 2015-02-23 2589 if (IS_ERR(di)) {
4f764e5153616f Filipe Manana 2015-02-23 2590 ret = PTR_ERR(di);
c2bd309cef030f Filipe Manana 2025-09-05 @2591 btrfs_abort_log_replay(wc, ret,
c2bd309cef030f Filipe Manana 2025-09-05 2592 "failed to lookup xattr with name %.*s for inode %llu root %llu",
c2bd309cef030f Filipe Manana 2025-09-05 2593 name_len, name, ino,
^^^^
Use after free
c2bd309cef030f Filipe Manana 2025-09-05 2594 btrfs_root_id(root));
4f764e5153616f Filipe Manana 2015-02-23 2595 goto out;
4f764e5153616f Filipe Manana 2015-02-23 2596 }
4f764e5153616f Filipe Manana 2015-02-23 2597 ASSERT(di);
4f764e5153616f Filipe Manana 2015-02-23 2598 ret = btrfs_delete_one_dir_name(trans, root,
51abc774a9b1d4 Filipe Manana 2025-09-05 2599 wc->subvol_path, di);
fa7c5927bfe412 Filipe Manana 2025-07-18 2600 if (ret) {
c2bd309cef030f Filipe Manana 2025-09-05 2601 btrfs_abort_log_replay(wc, ret,
c2bd309cef030f Filipe Manana 2025-09-05 2602 "failed to delete xattr with name %.*s for inode %llu root %llu",
c2bd309cef030f Filipe Manana 2025-09-05 2603 name_len, name, ino,
^^^^
First print the name, then free it.
c2bd309cef030f Filipe Manana 2025-09-05 2604 btrfs_root_id(root));
4f764e5153616f Filipe Manana 2015-02-23 2605 goto out;
fa7c5927bfe412 Filipe Manana 2025-07-18 2606 }
51abc774a9b1d4 Filipe Manana 2025-09-05 2607 btrfs_release_path(wc->subvol_path);
4f764e5153616f Filipe Manana 2015-02-23 2608 search_key = key;
4f764e5153616f Filipe Manana 2015-02-23 2609 goto again;
4f764e5153616f Filipe Manana 2015-02-23 2610 }
4f764e5153616f Filipe Manana 2015-02-23 2611 kfree(name);
^^^^^
4f764e5153616f Filipe Manana 2015-02-23 2612 if (IS_ERR(log_di)) {
4f764e5153616f Filipe Manana 2015-02-23 2613 ret = PTR_ERR(log_di);
c2bd309cef030f Filipe Manana 2025-09-05 2614 btrfs_abort_log_replay(wc, ret,
c2bd309cef030f Filipe Manana 2025-09-05 2615 "failed to lookup xattr in log tree with name %.*s for inode %llu root %llu",
c2bd309cef030f Filipe Manana 2025-09-05 2616 name_len, name, ino,
^^^^
same.
c2bd309cef030f Filipe Manana 2025-09-05 2617 btrfs_root_id(root));
4f764e5153616f Filipe Manana 2015-02-23 2618 goto out;
4f764e5153616f Filipe Manana 2015-02-23 2619 }
4f764e5153616f Filipe Manana 2015-02-23 2620 cur += this_len;
4f764e5153616f Filipe Manana 2015-02-23 2621 di = (struct btrfs_dir_item *)((char *)di + this_len);
4f764e5153616f Filipe Manana 2015-02-23 2622 }
4f764e5153616f Filipe Manana 2015-02-23 2623 }
51abc774a9b1d4 Filipe Manana 2025-09-05 2624 ret = btrfs_next_leaf(root, wc->subvol_path);
4f764e5153616f Filipe Manana 2015-02-23 2625 if (ret > 0)
4f764e5153616f Filipe Manana 2015-02-23 2626 ret = 0;
4f764e5153616f Filipe Manana 2015-02-23 2627 else if (ret == 0)
4f764e5153616f Filipe Manana 2015-02-23 2628 goto process_leaf;
fa7c5927bfe412 Filipe Manana 2025-07-18 2629 else
c2bd309cef030f Filipe Manana 2025-09-05 2630 btrfs_abort_log_replay(wc, ret,
c2bd309cef030f Filipe Manana 2025-09-05 2631 "failed to get next leaf in subvolume root %llu",
c2bd309cef030f Filipe Manana 2025-09-05 2632 btrfs_root_id(root));
4f764e5153616f Filipe Manana 2015-02-23 2633 out:
4f764e5153616f Filipe Manana 2015-02-23 2634 btrfs_free_path(log_path);
51abc774a9b1d4 Filipe Manana 2025-09-05 2635 btrfs_release_path(wc->subvol_path);
4f764e5153616f Filipe Manana 2015-02-23 2636 return ret;
4f764e5153616f Filipe Manana 2015-02-23 2637 }
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 72+ messages in thread
* [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (32 preceding siblings ...)
2025-09-05 16:10 ` [PATCH 33/33] btrfs: dump detailed info and specific messages on log replay failures fdmanana
@ 2025-09-08 9:52 ` fdmanana
2025-09-08 9:52 ` [PATCH v2 01/33] btrfs: fix invalid extref key setup when replaying dentry fdmanana
` (33 more replies)
33 siblings, 34 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:52 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
The following are a bug fix for an extref key offset computation, several
cleanups to eliminate duplicated or not needed code, memory allocations
and preparation work for the final change which is to make log replay
failures dump contextual information useful to help debug failures during
log replay. Details in the change logs.
V2: Update subject of patch 18/33 which was a duplicate of another one
likely due to copy paste. Update patch 33/33 to avoid use-after-free
on a name if we had an error during replay of xattr deletes.
Filipe Manana (33):
btrfs: fix invalid extref key setup when replaying dentry
btrfs: use booleans in walk control structure for log replay
btrfs: rename replay_dest member of struct walk_control to root
btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree()
btrfs: add and use a log root field to struct walk_control
btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees()
btrfs: stop passing transaction parameter to log tree walk functions
btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees()
btrfs: always drop log root tree reference in btrfs_replay_log()
btrfs: pass walk_control structure to replay_xattr_deletes()
btrfs: move up the definition of struct walk_control
btrfs: pass walk_control structure to replay_dir_deletes()
btrfs: pass walk_control structure to check_item_in_log()
btrfs: pass walk_control structure to replay_one_extent()
btrfs: pass walk_control structure to add_inode_ref() and helpers
btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name()
btrfs: pass walk_control structure to drop_one_dir_item() and helpers
btrfs: pass walk_control structure to overwrite_item()
btrfs: use level argument in log tree walk callback process_one_buffer()
btrfs: use level argument in log tree walk callback replay_one_buffer()
btrfs: use the inode item boolean everywhere in overwrite_item()
btrfs: add current log leaf, key and slot to struct walk_control
btrfs: avoid unnecessary path allocation at fixup_inode_link_count()
btrfs: avoid path allocations when dropping extents during log replay
btrfs: avoid unnecessary path allocation when replaying a dir item
btrfs: remove redundant path release when processing dentry during log replay
btrfs: remove redundant path release when overwriting item during log replay
btrfs: add path for subvolume tree changes to struct walk_control
btrfs: stop passing inode object IDs to __add_inode_ref() in log replay
btrfs: remove pointless inode lookup when processing extrefs during log replay
btrfs: abort transaction if we fail to find dir item during log replay
btrfs: abort transaction if we fail to update inode in log replay dir fixup
btrfs: dump detailed info and specific messages on log replay failures
fs/btrfs/disk-io.c | 2 +-
fs/btrfs/fs.h | 2 +
fs/btrfs/tree-log.c | 1299 +++++++++++++++++++++++++------------------
3 files changed, 767 insertions(+), 536 deletions(-)
--
2.47.2
^ permalink raw reply [flat|nested] 72+ messages in thread
* [PATCH v2 01/33] btrfs: fix invalid extref key setup when replaying dentry
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
@ 2025-09-08 9:52 ` fdmanana
2025-09-08 9:52 ` [PATCH v2 02/33] btrfs: use booleans in walk control structure for log replay fdmanana
` (32 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:52 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
The offset for an extref item's key is not the object ID of the parent
dir, otherwise we would not need the extref item and would use plain ref
items. Instead the offset is the result of a hash computation that uses
the object ID of the parent dir and the name associated to the entry.
So fix this by setting the key offset at replay_one_name() to be the
result of calling btrfs_extref_hash().
Fixes: 725af92a6251 ("btrfs: Open-code name_in_log_ref in replay_one_name")
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index b91cc7ac28d8..861f96ef28cf 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2058,7 +2058,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
search_key.objectid = log_key.objectid;
search_key.type = BTRFS_INODE_EXTREF_KEY;
- search_key.offset = key->objectid;
+ search_key.offset = btrfs_extref_hash(key->objectid, name.name, name.len);
ret = backref_in_log(root->log_root, &search_key, key->objectid, &name);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 02/33] btrfs: use booleans in walk control structure for log replay
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
2025-09-08 9:52 ` [PATCH v2 01/33] btrfs: fix invalid extref key setup when replaying dentry fdmanana
@ 2025-09-08 9:52 ` fdmanana
2025-09-08 9:52 ` [PATCH v2 03/33] btrfs: rename replay_dest member of struct walk_control to root fdmanana
` (31 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:52 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
The 'free' and 'pin' member of struct walk_control, used during log replay
and when freeing a log tree, are defined as integers but in practice are
used as booleans. Change their type to bool and while at it update their
comments to be more detailed and comply with the preferred comment style
(first word in a sentence is capitalized, sentences end with punctuation
and the comment opening (/*) is on a line of its own).
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 861f96ef28cf..c5c5fc05eabb 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -306,15 +306,20 @@ void btrfs_end_log_trans(struct btrfs_root *root)
* are state fields used for that specific part
*/
struct walk_control {
- /* should we free the extent on disk when done? This is used
- * at transaction commit time while freeing a log tree
+ /*
+ * Signal that we are freeing the metadata extents of a log tree.
+ * This is used at transaction commit time while freeing a log tree.
*/
- int free;
+ bool free;
- /* pin only walk, we record which extents on disk belong to the
- * log trees
+ /*
+ * Signal that we are pinning the metadata extents of a log tree and the
+ * data extents its leaves point to (if using mixed block groups).
+ * This happens in the first stage of log replay to ensure that during
+ * replay, while we are modifying subvolume trees, we don't overwrite
+ * the metadata extents of log trees.
*/
- int pin;
+ bool pin;
/* what stage of the replay code we're currently in */
int stage;
@@ -3415,7 +3420,7 @@ static void free_log_tree(struct btrfs_trans_handle *trans,
{
int ret;
struct walk_control wc = {
- .free = 1,
+ .free = true,
.process_func = process_one_buffer
};
@@ -7433,7 +7438,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
}
wc.trans = trans;
- wc.pin = 1;
+ wc.pin = true;
ret = walk_log_tree(trans, log_root_tree, &wc);
if (ret) {
@@ -7557,7 +7562,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
/* step one is to pin it all, step two is to replay just inodes */
if (wc.pin) {
- wc.pin = 0;
+ wc.pin = false;
wc.process_func = replay_one_buffer;
wc.stage = LOG_WALK_REPLAY_INODES;
goto again;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 03/33] btrfs: rename replay_dest member of struct walk_control to root
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
2025-09-08 9:52 ` [PATCH v2 01/33] btrfs: fix invalid extref key setup when replaying dentry fdmanana
2025-09-08 9:52 ` [PATCH v2 02/33] btrfs: use booleans in walk control structure for log replay fdmanana
@ 2025-09-08 9:52 ` fdmanana
2025-09-08 9:52 ` [PATCH v2 04/33] btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree() fdmanana
` (30 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:52 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Everywhere else we refer to a subvolume root we are replaying to simply
as 'root', so rename from 'replay_dest' to 'root' for consistency and
having a more meaningful and shorter name. While at it also update the
comment to be more detailed and comply to preferred style (first word in
a sentence is capitalized and sentence ends with punctuation).
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 32 +++++++++++++++++---------------
1 file changed, 17 insertions(+), 15 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index c5c5fc05eabb..c0cc94efbcaa 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -330,8 +330,11 @@ struct walk_control {
*/
bool ignore_cur_inode;
- /* the root we are currently replaying */
- struct btrfs_root *replay_dest;
+ /*
+ * The root we are currently replaying to. This is NULL for the replay
+ * stage LOG_WALK_PIN_ONLY.
+ */
+ struct btrfs_root *root;
/* the trans handle for the current replay */
struct btrfs_trans_handle *trans;
@@ -2575,7 +2578,7 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
.level = level
};
struct btrfs_path *path;
- struct btrfs_root *root = wc->replay_dest;
+ struct btrfs_root *root = wc->root;
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_key key;
int i;
@@ -7479,11 +7482,10 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
goto error;
}
- wc.replay_dest = btrfs_get_fs_root(fs_info, found_key.offset,
- true);
- if (IS_ERR(wc.replay_dest)) {
- ret = PTR_ERR(wc.replay_dest);
- wc.replay_dest = NULL;
+ wc.root = btrfs_get_fs_root(fs_info, found_key.offset, true);
+ if (IS_ERR(wc.root)) {
+ ret = PTR_ERR(wc.root);
+ wc.root = NULL;
if (ret != -ENOENT) {
btrfs_put_root(log);
btrfs_abort_transaction(trans, ret);
@@ -7510,8 +7512,8 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
goto next;
}
- wc.replay_dest->log_root = log;
- ret = btrfs_record_root_in_trans(trans, wc.replay_dest);
+ wc.root->log_root = log;
+ ret = btrfs_record_root_in_trans(trans, wc.root);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
@@ -7524,9 +7526,9 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
}
if (wc.stage == LOG_WALK_REPLAY_ALL) {
- struct btrfs_root *root = wc.replay_dest;
+ struct btrfs_root *root = wc.root;
- ret = fixup_inode_link_counts(trans, wc.replay_dest, path);
+ ret = fixup_inode_link_counts(trans, root, path);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
@@ -7546,9 +7548,9 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
}
}
next:
- if (wc.replay_dest) {
- wc.replay_dest->log_root = NULL;
- btrfs_put_root(wc.replay_dest);
+ if (wc.root) {
+ wc.root->log_root = NULL;
+ btrfs_put_root(wc.root);
}
btrfs_put_root(log);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 04/33] btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (2 preceding siblings ...)
2025-09-08 9:52 ` [PATCH v2 03/33] btrfs: rename replay_dest member of struct walk_control to root fdmanana
@ 2025-09-08 9:52 ` fdmanana
2025-09-08 9:52 ` [PATCH v2 05/33] btrfs: add and use a log root field to struct walk_control fdmanana
` (29 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:52 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Everywhere we have a log root we name it as 'log' or 'log_root' except in
walk_down_log_tree() and walk_up_log_tree() where we name it as 'root',
which not only it's inconsistent, it's also confusing since we typically
use 'root' when naming variables that refer to a subvolume tree. So for
clairty and consistency rename the 'root' argument to 'log'.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index c0cc94efbcaa..321ec0de0733 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2779,11 +2779,11 @@ static int clean_log_buffer(struct btrfs_trans_handle *trans,
}
static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+ struct btrfs_root *log,
struct btrfs_path *path, int *level,
struct walk_control *wc)
{
- struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_fs_info *fs_info = log->fs_info;
u64 bytenr;
u64 ptr_gen;
struct extent_buffer *next;
@@ -2821,8 +2821,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
}
if (*level == 1) {
- ret = wc->process_func(root, next, wc, ptr_gen,
- *level - 1);
+ ret = wc->process_func(log, next, wc, ptr_gen, *level - 1);
if (ret) {
free_extent_buffer(next);
return ret;
@@ -2873,7 +2872,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
}
static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+ struct btrfs_root *log,
struct btrfs_path *path, int *level,
struct walk_control *wc)
{
@@ -2889,7 +2888,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
WARN_ON(*level == 0);
return 0;
} else {
- ret = wc->process_func(root, path->nodes[*level], wc,
+ ret = wc->process_func(log, path->nodes[*level], wc,
btrfs_header_generation(path->nodes[*level]),
*level);
if (ret)
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 05/33] btrfs: add and use a log root field to struct walk_control
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (3 preceding siblings ...)
2025-09-08 9:52 ` [PATCH v2 04/33] btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree() fdmanana
@ 2025-09-08 9:52 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 06/33] btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees() fdmanana
` (28 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:52 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing an extra log root parameter for the log tree walk
functions and callbacks, add the log tree to struct walk_control and
make those functions and callbacks extract the log root from that
structure, reducing the number of parameters. This also simplifies
further upcoming changes to report log tree replay failures.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 62 +++++++++++++++++++++++++--------------------
1 file changed, 35 insertions(+), 27 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 321ec0de0733..7d1b21df698d 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -336,6 +336,9 @@ struct walk_control {
*/
struct btrfs_root *root;
+ /* The log tree we are currently processing (not NULL for any stage). */
+ struct btrfs_root *log;
+
/* the trans handle for the current replay */
struct btrfs_trans_handle *trans;
@@ -344,17 +347,17 @@ struct walk_control {
* passed in, and it must be checked or read if you need the data
* inside it
*/
- int (*process_func)(struct btrfs_root *log, struct extent_buffer *eb,
+ int (*process_func)(struct extent_buffer *eb,
struct walk_control *wc, u64 gen, int level);
};
/*
* process_func used to pin down extents, write them or wait on them
*/
-static int process_one_buffer(struct btrfs_root *log,
- struct extent_buffer *eb,
+static int process_one_buffer(struct extent_buffer *eb,
struct walk_control *wc, u64 gen, int level)
{
+ struct btrfs_root *log = wc->log;
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_fs_info *fs_info = log->fs_info;
int ret = 0;
@@ -2569,7 +2572,7 @@ static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,
* only in the log (references come from either directory items or inode
* back refs).
*/
-static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
+static int replay_one_buffer(struct extent_buffer *eb,
struct walk_control *wc, u64 gen, int level)
{
int nritems;
@@ -2579,6 +2582,7 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
};
struct btrfs_path *path;
struct btrfs_root *root = wc->root;
+ struct btrfs_root *log = wc->log;
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_key key;
int i;
@@ -2779,11 +2783,10 @@ static int clean_log_buffer(struct btrfs_trans_handle *trans,
}
static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *log,
struct btrfs_path *path, int *level,
struct walk_control *wc)
{
- struct btrfs_fs_info *fs_info = log->fs_info;
+ struct btrfs_fs_info *fs_info = wc->log->fs_info;
u64 bytenr;
u64 ptr_gen;
struct extent_buffer *next;
@@ -2821,7 +2824,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
}
if (*level == 1) {
- ret = wc->process_func(log, next, wc, ptr_gen, *level - 1);
+ ret = wc->process_func(next, wc, ptr_gen, *level - 1);
if (ret) {
free_extent_buffer(next);
return ret;
@@ -2872,7 +2875,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
}
static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *log,
struct btrfs_path *path, int *level,
struct walk_control *wc)
{
@@ -2888,7 +2890,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
WARN_ON(*level == 0);
return 0;
} else {
- ret = wc->process_func(log, path->nodes[*level], wc,
+ ret = wc->process_func(path->nodes[*level], wc,
btrfs_header_generation(path->nodes[*level]),
*level);
if (ret)
@@ -2912,9 +2914,9 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
* the tree freeing any blocks that have a ref count of zero after being
* decremented.
*/
-static int walk_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *log, struct walk_control *wc)
+static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *wc)
{
+ struct btrfs_root *log = wc->log;
int ret = 0;
int wret;
int level;
@@ -2932,7 +2934,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
path->slots[level] = 0;
while (1) {
- wret = walk_down_log_tree(trans, log, path, &level, wc);
+ wret = walk_down_log_tree(trans, path, &level, wc);
if (wret > 0)
break;
if (wret < 0) {
@@ -2940,7 +2942,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
goto out;
}
- wret = walk_up_log_tree(trans, log, path, &level, wc);
+ wret = walk_up_log_tree(trans, path, &level, wc);
if (wret > 0)
break;
if (wret < 0) {
@@ -2951,7 +2953,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
/* was the root node processed? if not, catch it here */
if (path->nodes[orig_level]) {
- ret = wc->process_func(log, path->nodes[orig_level], wc,
+ ret = wc->process_func(path->nodes[orig_level], wc,
btrfs_header_generation(path->nodes[orig_level]),
orig_level);
if (ret)
@@ -3423,11 +3425,12 @@ static void free_log_tree(struct btrfs_trans_handle *trans,
int ret;
struct walk_control wc = {
.free = true,
- .process_func = process_one_buffer
+ .process_func = process_one_buffer,
+ .log = log,
};
if (log->node) {
- ret = walk_log_tree(trans, log, &wc);
+ ret = walk_log_tree(trans, &wc);
if (ret) {
/*
* We weren't able to traverse the entire log tree, the
@@ -7441,8 +7444,10 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
wc.trans = trans;
wc.pin = true;
+ wc.log = log_root_tree;
- ret = walk_log_tree(trans, log_root_tree, &wc);
+ ret = walk_log_tree(trans, &wc);
+ wc.log = NULL;
if (ret) {
btrfs_abort_transaction(trans, ret);
goto error;
@@ -7454,7 +7459,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
key.offset = (u64)-1;
while (1) {
- struct btrfs_root *log;
struct btrfs_key found_key;
ret = btrfs_search_slot(NULL, log_root_tree, &key, path, 0, 0);
@@ -7474,9 +7478,10 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
if (found_key.objectid != BTRFS_TREE_LOG_OBJECTID)
break;
- log = btrfs_read_tree_root(log_root_tree, &found_key);
- if (IS_ERR(log)) {
- ret = PTR_ERR(log);
+ wc.log = btrfs_read_tree_root(log_root_tree, &found_key);
+ if (IS_ERR(wc.log)) {
+ ret = PTR_ERR(wc.log);
+ wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
@@ -7486,7 +7491,8 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
ret = PTR_ERR(wc.root);
wc.root = NULL;
if (ret != -ENOENT) {
- btrfs_put_root(log);
+ btrfs_put_root(wc.log);
+ wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
@@ -7502,23 +7508,24 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
* block from being modified, and we'll just bail for
* each subsequent pass.
*/
- ret = btrfs_pin_extent_for_log_replay(trans, log->node);
+ ret = btrfs_pin_extent_for_log_replay(trans, wc.log->node);
if (ret) {
- btrfs_put_root(log);
+ btrfs_put_root(wc.log);
+ wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
goto next;
}
- wc.root->log_root = log;
+ wc.root->log_root = wc.log;
ret = btrfs_record_root_in_trans(trans, wc.root);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
}
- ret = walk_log_tree(trans, log, &wc);
+ ret = walk_log_tree(trans, &wc);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
@@ -7551,7 +7558,8 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
wc.root->log_root = NULL;
btrfs_put_root(wc.root);
}
- btrfs_put_root(log);
+ btrfs_put_root(wc.log);
+ wc.log = NULL;
if (ret)
goto error;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 06/33] btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (4 preceding siblings ...)
2025-09-08 9:52 ` [PATCH v2 05/33] btrfs: add and use a log root field to struct walk_control fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 07/33] btrfs: stop passing transaction parameter to log tree walk functions fdmanana
` (27 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of duplicating the dropping of a log tree in case we jump to the
'error' label, move the dropping under the 'error' label and get rid of the
the unnecessary setting of the log root to NULL since we return immediately
after.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 7d1b21df698d..f039f0613a38 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -7491,8 +7491,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
ret = PTR_ERR(wc.root);
wc.root = NULL;
if (ret != -ENOENT) {
- btrfs_put_root(wc.log);
- wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
@@ -7510,8 +7508,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
*/
ret = btrfs_pin_extent_for_log_replay(trans, wc.log->node);
if (ret) {
- btrfs_put_root(wc.log);
- wc.log = NULL;
btrfs_abort_transaction(trans, ret);
goto error;
}
@@ -7597,6 +7593,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
error:
if (wc.trans)
btrfs_end_transaction(wc.trans);
+ btrfs_put_root(wc.log);
clear_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags);
btrfs_free_path(path);
return ret;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 07/33] btrfs: stop passing transaction parameter to log tree walk functions
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (5 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 06/33] btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 08/33] btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees() fdmanana
` (26 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
It's unncessary to pass a transaction parameter since struct walk_control
already has a member that points to the transaction, so we can make the
functions access the structure.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index f039f0613a38..dee306101d8e 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2782,10 +2782,10 @@ static int clean_log_buffer(struct btrfs_trans_handle *trans,
return 0;
}
-static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_path *path, int *level,
- struct walk_control *wc)
+static noinline int walk_down_log_tree(struct btrfs_path *path, int *level,
+ struct walk_control *wc)
{
+ struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_fs_info *fs_info = wc->log->fs_info;
u64 bytenr;
u64 ptr_gen;
@@ -2874,9 +2874,8 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
return 0;
}
-static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
- struct btrfs_path *path, int *level,
- struct walk_control *wc)
+static noinline int walk_up_log_tree(struct btrfs_path *path, int *level,
+ struct walk_control *wc)
{
int i;
int slot;
@@ -2897,7 +2896,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
return ret;
if (wc->free) {
- ret = clean_log_buffer(trans, path->nodes[*level]);
+ ret = clean_log_buffer(wc->trans, path->nodes[*level]);
if (ret)
return ret;
}
@@ -2914,7 +2913,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
* the tree freeing any blocks that have a ref count of zero after being
* decremented.
*/
-static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *wc)
+static int walk_log_tree(struct walk_control *wc)
{
struct btrfs_root *log = wc->log;
int ret = 0;
@@ -2934,7 +2933,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *
path->slots[level] = 0;
while (1) {
- wret = walk_down_log_tree(trans, path, &level, wc);
+ wret = walk_down_log_tree(path, &level, wc);
if (wret > 0)
break;
if (wret < 0) {
@@ -2942,7 +2941,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *
goto out;
}
- wret = walk_up_log_tree(trans, path, &level, wc);
+ wret = walk_up_log_tree(path, &level, wc);
if (wret > 0)
break;
if (wret < 0) {
@@ -2959,7 +2958,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans, struct walk_control *
if (ret)
goto out;
if (wc->free)
- ret = clean_log_buffer(trans, path->nodes[orig_level]);
+ ret = clean_log_buffer(wc->trans, path->nodes[orig_level]);
}
out:
@@ -3427,10 +3426,11 @@ static void free_log_tree(struct btrfs_trans_handle *trans,
.free = true,
.process_func = process_one_buffer,
.log = log,
+ .trans = trans,
};
if (log->node) {
- ret = walk_log_tree(trans, &wc);
+ ret = walk_log_tree(&wc);
if (ret) {
/*
* We weren't able to traverse the entire log tree, the
@@ -7446,7 +7446,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
wc.pin = true;
wc.log = log_root_tree;
- ret = walk_log_tree(trans, &wc);
+ ret = walk_log_tree(&wc);
wc.log = NULL;
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -7521,7 +7521,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
goto next;
}
- ret = walk_log_tree(trans, &wc);
+ ret = walk_log_tree(&wc);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 08/33] btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (6 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 07/33] btrfs: stop passing transaction parameter to log tree walk functions fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 09/33] btrfs: always drop log root tree reference in btrfs_replay_log() fdmanana
` (25 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
There's no point in setting log_root_tree->log_root to NULL as this is
already NULL, we never assigned anything to it before and it's meaningless
as a log root never has a value other than NULL for the ->log_root field,
that can be not NULL only for non log roots.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index dee306101d8e..ab2f6bab096b 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -7585,7 +7585,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
if (ret)
return ret;
- log_root_tree->log_root = NULL;
clear_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags);
btrfs_put_root(log_root_tree);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 09/33] btrfs: always drop log root tree reference in btrfs_replay_log()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (7 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 08/33] btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 10/33] btrfs: pass walk_control structure to replay_xattr_deletes() fdmanana
` (24 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Currently we have this odd behaviour:
1) At btrfs_replay_log() we drop the reference of the log root tree if
the call to btrfs_recover_log_trees() failed;
2) But if the call to btrfs_recover_log_trees() did not fail, we don't
drop the reference in btrfs_replay_log() - we expect that
btrfs_recover_log_trees() does it in case it returns success.
Let's simplify this and make btrfs_replay_log() always drop the reference
on the log root tree, not only this simplifies code as it's what makes
sense since it's btrfs_replay_log() who grabbed the reference in the first
place.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/disk-io.c | 2 +-
fs/btrfs/tree-log.c | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 7b06bbc40898..8dbb6a12ec24 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -2088,10 +2088,10 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info,
/* returns with log_tree_root freed on success */
ret = btrfs_recover_log_trees(log_tree_root);
+ btrfs_put_root(log_tree_root);
if (ret) {
btrfs_handle_fs_error(fs_info, ret,
"Failed to recover log tree");
- btrfs_put_root(log_tree_root);
return ret;
}
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index ab2f6bab096b..4d34aee0cafa 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -7586,7 +7586,6 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
return ret;
clear_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags);
- btrfs_put_root(log_root_tree);
return 0;
error:
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 10/33] btrfs: pass walk_control structure to replay_xattr_deletes()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (8 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 09/33] btrfs: always drop log root tree reference in btrfs_replay_log() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 11/33] btrfs: move up the definition of struct walk_control fdmanana
` (23 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction, subvolume root and log tree as
arguments to replay_xattr_deletes(), pass the walk_control structure as
we can grab all of those from the structure. This reduces the number of
arguments passed and it's going to be needed by an incoming change that
improves error reporting for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 4d34aee0cafa..cd4c5ae3e0a3 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2336,12 +2336,13 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
return ret;
}
-static int replay_xattr_deletes(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_root *log,
- struct btrfs_path *path,
- const u64 ino)
+static int replay_xattr_deletes(struct walk_control *wc,
+ struct btrfs_path *path,
+ const u64 ino)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
+ struct btrfs_root *log = wc->log;
struct btrfs_key search_key;
struct btrfs_path *log_path;
int i;
@@ -2645,7 +2646,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
wc->stage == LOG_WALK_REPLAY_INODES) {
u32 mode;
- ret = replay_xattr_deletes(trans, root, log, path, key.objectid);
+ ret = replay_xattr_deletes(wc, path, key.objectid);
if (ret)
break;
mode = btrfs_inode_mode(eb, inode_item);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 11/33] btrfs: move up the definition of struct walk_control
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (9 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 10/33] btrfs: pass walk_control structure to replay_xattr_deletes() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 12/33] btrfs: pass walk_control structure to replay_dir_deletes() fdmanana
` (22 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
In upcoming changes we need to pass struct walk_control as an argument to
replay_dir_deletes() and link_to_fixup_dir() so we need to move its
definition above the prototypes of those functions. So move it up right
below the enum that defines log replay stages and before any functions and
function prototypes are declared. Also fixup the comments while moving it
so that they comply with the preferred code style (capitalize the first
word in a sentence, end sentences with punctuation, makes lines wider and
closer to the 80 characters limit).
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 103 ++++++++++++++++++++++----------------------
1 file changed, 51 insertions(+), 52 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index cd4c5ae3e0a3..2780f0e1db01 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -101,6 +101,57 @@ enum {
LOG_WALK_REPLAY_ALL,
};
+/*
+ * The walk control struct is used to pass state down the chain when processing
+ * the log tree. The stage field tells us which part of the log tree processing
+ * we are currently doing.
+ */
+struct walk_control {
+ /*
+ * Signal that we are freeing the metadata extents of a log tree.
+ * This is used at transaction commit time while freeing a log tree.
+ */
+ bool free;
+
+ /*
+ * Signal that we are pinning the metadata extents of a log tree and the
+ * data extents its leaves point to (if using mixed block groups).
+ * This happens in the first stage of log replay to ensure that during
+ * replay, while we are modifying subvolume trees, we don't overwrite
+ * the metadata extents of log trees.
+ */
+ bool pin;
+
+ /* What stage of the replay code we're currently in. */
+ int stage;
+
+ /*
+ * Ignore any items from the inode currently being processed. Needs
+ * to be set every time we find a BTRFS_INODE_ITEM_KEY.
+ */
+ bool ignore_cur_inode;
+
+ /*
+ * The root we are currently replaying to. This is NULL for the replay
+ * stage LOG_WALK_PIN_ONLY.
+ */
+ struct btrfs_root *root;
+
+ /* The log tree we are currently processing (not NULL for any stage). */
+ struct btrfs_root *log;
+
+ /* The transaction handle used for replaying all log trees. */
+ struct btrfs_trans_handle *trans;
+
+ /*
+ * The function that gets used to process blocks we find in the tree.
+ * Note the extent_buffer might not be up to date when it is passed in,
+ * and it must be checked or read if you need the data inside it.
+ */
+ int (*process_func)(struct extent_buffer *eb,
+ struct walk_control *wc, u64 gen, int level);
+};
+
static int btrfs_log_inode(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode,
int inode_only,
@@ -299,58 +350,6 @@ void btrfs_end_log_trans(struct btrfs_root *root)
}
}
-/*
- * the walk control struct is used to pass state down the chain when
- * processing the log tree. The stage field tells us which part
- * of the log tree processing we are currently doing. The others
- * are state fields used for that specific part
- */
-struct walk_control {
- /*
- * Signal that we are freeing the metadata extents of a log tree.
- * This is used at transaction commit time while freeing a log tree.
- */
- bool free;
-
- /*
- * Signal that we are pinning the metadata extents of a log tree and the
- * data extents its leaves point to (if using mixed block groups).
- * This happens in the first stage of log replay to ensure that during
- * replay, while we are modifying subvolume trees, we don't overwrite
- * the metadata extents of log trees.
- */
- bool pin;
-
- /* what stage of the replay code we're currently in */
- int stage;
-
- /*
- * Ignore any items from the inode currently being processed. Needs
- * to be set every time we find a BTRFS_INODE_ITEM_KEY.
- */
- bool ignore_cur_inode;
-
- /*
- * The root we are currently replaying to. This is NULL for the replay
- * stage LOG_WALK_PIN_ONLY.
- */
- struct btrfs_root *root;
-
- /* The log tree we are currently processing (not NULL for any stage). */
- struct btrfs_root *log;
-
- /* the trans handle for the current replay */
- struct btrfs_trans_handle *trans;
-
- /* the function that gets used to process blocks we find in the
- * tree. Note the extent_buffer might not be up to date when it is
- * passed in, and it must be checked or read if you need the data
- * inside it
- */
- int (*process_func)(struct extent_buffer *eb,
- struct walk_control *wc, u64 gen, int level);
-};
-
/*
* process_func used to pin down extents, write them or wait on them
*/
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 12/33] btrfs: pass walk_control structure to replay_dir_deletes()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (10 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 11/33] btrfs: move up the definition of struct walk_control fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 13/33] btrfs: pass walk_control structure to check_item_in_log() fdmanana
` (21 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction, subvolume root and log tree as
arguments to replay_dir_deletes(), pass the walk_control structure as
we can grab all of those from the structure. This reduces the number of
arguments passed and it's going to be needed by an incoming change that
improves error reporting for log replay. This also requires changing
fixup_inode_link_counts() and fixup_inode_link_count() to take that
structure as an argument since fixup_inode_link_count() makes a call
to replay_dir_deletes().
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 2780f0e1db01..460dc51e8c82 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -159,9 +159,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
static int link_to_fixup_dir(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, u64 objectid);
-static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_root *log,
+static noinline int replay_dir_deletes(struct walk_control *wc,
struct btrfs_path *path,
u64 dirid, bool del_all);
static void wait_log_commit(struct btrfs_root *root, int transid);
@@ -1727,9 +1725,10 @@ static int count_inode_refs(struct btrfs_inode *inode, struct btrfs_path *path)
* number of back refs found. If it goes down to zero, the iput
* will free the inode.
*/
-static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans,
+static noinline int fixup_inode_link_count(struct walk_control *wc,
struct btrfs_inode *inode)
{
+ struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = inode->root;
struct btrfs_path *path;
int ret;
@@ -1765,7 +1764,7 @@ static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans,
if (inode->vfs_inode.i_nlink == 0) {
if (S_ISDIR(inode->vfs_inode.i_mode)) {
- ret = replay_dir_deletes(trans, root, NULL, path, ino, true);
+ ret = replay_dir_deletes(wc, path, ino, true);
if (ret)
goto out;
}
@@ -1779,8 +1778,7 @@ static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans,
return ret;
}
-static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int fixup_inode_link_counts(struct walk_control *wc,
struct btrfs_path *path)
{
int ret;
@@ -1790,6 +1788,8 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans,
key.type = BTRFS_ORPHAN_ITEM_KEY;
key.offset = (u64)-1;
while (1) {
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_inode *inode;
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
@@ -1819,7 +1819,7 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans,
break;
}
- ret = fixup_inode_link_count(trans, inode);
+ ret = fixup_inode_link_count(wc, inode);
iput(&inode->vfs_inode);
if (ret)
break;
@@ -2455,12 +2455,13 @@ static int replay_xattr_deletes(struct walk_control *wc,
* Anything we don't find in the log is unlinked and removed from the
* directory.
*/
-static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_root *log,
+static noinline int replay_dir_deletes(struct walk_control *wc,
struct btrfs_path *path,
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;
u64 range_end;
int ret = 0;
@@ -2650,8 +2651,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
mode = btrfs_inode_mode(eb, inode_item);
if (S_ISDIR(mode)) {
- ret = replay_dir_deletes(trans, root, log, path,
- key.objectid, false);
+ ret = replay_dir_deletes(wc, path, key.objectid, false);
if (ret)
break;
}
@@ -7530,7 +7530,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
if (wc.stage == LOG_WALK_REPLAY_ALL) {
struct btrfs_root *root = wc.root;
- ret = fixup_inode_link_counts(trans, root, path);
+ ret = fixup_inode_link_counts(&wc, path);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 13/33] btrfs: pass walk_control structure to check_item_in_log()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (11 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 12/33] btrfs: pass walk_control structure to replay_dir_deletes() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 14/33] btrfs: pass walk_control structure to replay_one_extent() fdmanana
` (20 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction and log tree as arguments to
check_item_in_log(), pass the walk_control structure as we can grab those
from the structure. This reduces the number of arguments passed and it's
going to be needed by an incoming change that improves error reporting for
log replay. Notice that a NULL log root argument to check_item_in_log()
makes it unconditionally delete a directory entry, so since the
walk_control always has a non-NULL log root, we add an extra boolean to
check_item_in_log() to tell it if it should unconditionally delete a
directory entry, preserving the behaviour and also making it a bit more
clear.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 460dc51e8c82..ca0946f947df 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2254,13 +2254,14 @@ static noinline int find_dir_range(struct btrfs_root *root,
* item is not in the log, the item is removed and the inode it points
* to is unlinked
*/
-static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
- struct btrfs_root *log,
+static noinline int check_item_in_log(struct walk_control *wc,
struct btrfs_path *path,
struct btrfs_path *log_path,
struct btrfs_inode *dir,
- struct btrfs_key *dir_key)
+ struct btrfs_key *dir_key,
+ bool force_remove)
{
+ struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = dir->root;
int ret;
struct extent_buffer *eb;
@@ -2287,10 +2288,10 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
goto out;
}
- if (log) {
+ if (!force_remove) {
struct btrfs_dir_item *log_di;
- log_di = btrfs_lookup_dir_index_item(trans, log, log_path,
+ log_di = btrfs_lookup_dir_index_item(trans, wc->log, log_path,
dir_key->objectid,
dir_key->offset, &name, 0);
if (IS_ERR(log_di)) {
@@ -2540,9 +2541,8 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
if (found_key.offset > range_end)
break;
- ret = check_item_in_log(trans, log, path,
- log_path, dir,
- &found_key);
+ ret = check_item_in_log(wc, path, log_path, dir,
+ &found_key, del_all);
if (ret)
goto out;
if (found_key.offset == (u64)-1)
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 14/33] btrfs: pass walk_control structure to replay_one_extent()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (12 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 13/33] btrfs: pass walk_control structure to check_item_in_log() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 15/33] btrfs: pass walk_control structure to add_inode_ref() and helpers fdmanana
` (19 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction and subvolume root as arguments to
replay_one_extent(), pass the walk_control structure as we can grab all
of those from the structure. This reduces the number of arguments passed
and it's going to be needed by an incoming change that improves error
reporting for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index ca0946f947df..aac648ae30fb 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -642,12 +642,13 @@ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len,
* The extent is inserted into the file, dropping any existing extents
* from the file that overlap the new one.
*/
-static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int replay_one_extent(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb, int slot,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_drop_extents_args drop_args = { 0 };
struct btrfs_fs_info *fs_info = root->fs_info;
int found_type;
@@ -2728,7 +2729,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (ret)
break;
} else if (key.type == BTRFS_EXTENT_DATA_KEY) {
- ret = replay_one_extent(trans, root, path, eb, i, &key);
+ ret = replay_one_extent(wc, path, eb, i, &key);
if (ret)
break;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 15/33] btrfs: pass walk_control structure to add_inode_ref() and helpers
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (13 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 14/33] btrfs: pass walk_control structure to replay_one_extent() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 16/33] btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name() fdmanana
` (18 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction, subvolume root and log tree as
arguments to add_inode_ref() and its helpers (__add_inode_ref(),
unlink_refs_not_in_log(), unlink_extrefs_not_in_log() and
unlink_old_inode_refs()), pass the walk_control structure as we can
access all of those from the structure. This reduces the number of
arguments passed and it's going to be needed by an incoming change
that improves error reporting for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 47 ++++++++++++++++++++++-----------------------
1 file changed, 23 insertions(+), 24 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index aac648ae30fb..2ec9252115fd 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1088,9 +1088,8 @@ static noinline int backref_in_log(struct btrfs_root *log,
return ret;
}
-static int unlink_refs_not_in_log(struct btrfs_trans_handle *trans,
+static int unlink_refs_not_in_log(struct walk_control *wc,
struct btrfs_path *path,
- struct btrfs_root *log_root,
struct btrfs_key *search_key,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
@@ -1108,6 +1107,7 @@ static int unlink_refs_not_in_log(struct btrfs_trans_handle *trans,
ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
ptr_end = ptr + btrfs_item_size(leaf, 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;
@@ -1121,7 +1121,7 @@ static int unlink_refs_not_in_log(struct btrfs_trans_handle *trans,
return ret;
}
- ret = backref_in_log(log_root, search_key, parent_objectid, &victim_name);
+ ret = backref_in_log(wc->log, search_key, parent_objectid, &victim_name);
if (ret) {
kfree(victim_name.name);
if (ret < 0) {
@@ -1145,10 +1145,8 @@ static int unlink_refs_not_in_log(struct btrfs_trans_handle *trans,
return 0;
}
-static int unlink_extrefs_not_in_log(struct btrfs_trans_handle *trans,
+static int unlink_extrefs_not_in_log(struct walk_control *wc,
struct btrfs_path *path,
- struct btrfs_root *root,
- struct btrfs_root *log_root,
struct btrfs_key *search_key,
struct btrfs_inode *inode,
u64 inode_objectid,
@@ -1160,6 +1158,9 @@ static int unlink_extrefs_not_in_log(struct btrfs_trans_handle *trans,
u32 cur_offset = 0;
while (cur_offset < item_size) {
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
+ struct btrfs_root *log_root = wc->log;
struct btrfs_inode_extref *extref;
struct btrfs_inode *victim_parent;
struct fscrypt_str victim_name;
@@ -1218,16 +1219,16 @@ static int unlink_extrefs_not_in_log(struct btrfs_trans_handle *trans,
return 0;
}
-static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static inline int __add_inode_ref(struct walk_control *wc,
struct btrfs_path *path,
- struct btrfs_root *log_root,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
u64 inode_objectid, u64 parent_objectid,
u64 ref_index, struct fscrypt_str *name)
{
int ret;
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_dir_item *di;
struct btrfs_key search_key;
struct btrfs_inode_extref *extref;
@@ -1249,8 +1250,8 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
if (search_key.objectid == search_key.offset)
return 1;
- ret = unlink_refs_not_in_log(trans, path, log_root, &search_key,
- dir, inode, parent_objectid);
+ ret = unlink_refs_not_in_log(wc, path, &search_key, dir, inode,
+ parent_objectid);
if (ret == -EAGAIN)
goto again;
else if (ret)
@@ -1263,8 +1264,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
if (IS_ERR(extref)) {
return PTR_ERR(extref);
} else if (extref) {
- ret = unlink_extrefs_not_in_log(trans, path, root, log_root,
- &search_key, inode,
+ ret = unlink_extrefs_not_in_log(wc, path, &search_key, inode,
inode_objectid, parent_objectid);
if (ret == -EAGAIN)
goto again;
@@ -1349,14 +1349,15 @@ static int ref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
* proper unlink of that name (that is, remove its entry from the inode
* reference item and both dir index keys).
*/
-static int unlink_old_inode_refs(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int unlink_old_inode_refs(struct walk_control *wc,
struct btrfs_path *path,
struct btrfs_inode *inode,
struct extent_buffer *log_eb,
int log_slot,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
int ret;
unsigned long ref_ptr;
unsigned long ref_end;
@@ -1441,13 +1442,13 @@ static int unlink_old_inode_refs(struct btrfs_trans_handle *trans,
* root is the destination we are replaying into, and path is for temp
* use by this function. (it should be released on return).
*/
-static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_root *log,
+static noinline int add_inode_ref(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb, int slot,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_inode *dir = NULL;
struct btrfs_inode *inode = NULL;
unsigned long ref_ptr;
@@ -1559,9 +1560,8 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
* overwrite any existing back reference, and we don't
* want to create dangling pointers in the directory.
*/
- ret = __add_inode_ref(trans, root, path, log, dir, inode,
- inode_objectid, parent_objectid,
- ref_index, &name);
+ ret = __add_inode_ref(wc, path, dir, inode, inode_objectid,
+ parent_objectid, ref_index, &name);
if (ret) {
if (ret == 1)
ret = 0;
@@ -1601,7 +1601,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
* dir index entries exist for a name but there is no inode reference
* item with the same name.
*/
- ret = unlink_old_inode_refs(trans, root, path, inode, eb, slot, key);
+ ret = unlink_old_inode_refs(wc, path, inode, eb, slot, key);
if (ret)
goto out;
@@ -2584,7 +2584,6 @@ static int replay_one_buffer(struct extent_buffer *eb,
};
struct btrfs_path *path;
struct btrfs_root *root = wc->root;
- struct btrfs_root *log = wc->log;
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_key key;
int i;
@@ -2725,7 +2724,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
} else if (key.type == BTRFS_INODE_REF_KEY ||
key.type == BTRFS_INODE_EXTREF_KEY) {
- ret = add_inode_ref(trans, root, log, path, eb, i, &key);
+ ret = add_inode_ref(wc, path, eb, i, &key);
if (ret)
break;
} else if (key.type == BTRFS_EXTENT_DATA_KEY) {
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 16/33] btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (14 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 15/33] btrfs: pass walk_control structure to add_inode_ref() and helpers fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 17/33] btrfs: pass walk_control structure to drop_one_dir_item() and helpers fdmanana
` (17 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction and subvolume root and log tree as
arguments, pass the walk_control structure as we can grab all of those
from the structure. This reduces the number of arguments passed and it's
going to be needed by an incoming change that improves error reporting
for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 2ec9252115fd..c4c2fbf291a1 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1961,13 +1961,14 @@ static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans,
* Returns < 0 on error, 0 if the name wasn't replayed (dentry points to a
* non-existing inode) and 1 if the name was replayed.
*/
-static noinline int replay_one_name(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int replay_one_name(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb,
struct btrfs_dir_item *di,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct fscrypt_str name = { 0 };
struct btrfs_dir_item *dir_dst_di;
struct btrfs_dir_item *index_dst_di;
@@ -2107,8 +2108,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
}
/* Replay one dir item from a BTRFS_DIR_INDEX_KEY key. */
-static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int replay_one_dir_item(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb, int slot,
struct btrfs_key *key)
@@ -2120,7 +2120,7 @@ static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
ASSERT(key->type == BTRFS_DIR_INDEX_KEY);
di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
- ret = replay_one_name(trans, root, path, eb, di, key);
+ ret = replay_one_name(wc, path, eb, di, key);
if (ret < 0)
return ret;
@@ -2156,12 +2156,12 @@ static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
fixup_path = btrfs_alloc_path();
if (!fixup_path) {
- btrfs_abort_transaction(trans, -ENOMEM);
+ btrfs_abort_transaction(wc->trans, -ENOMEM);
return -ENOMEM;
}
btrfs_dir_item_key_to_cpu(eb, di, &di_key);
- ret = link_to_fixup_dir(trans, root, fixup_path, di_key.objectid);
+ ret = link_to_fixup_dir(wc->trans, wc->root, fixup_path, di_key.objectid);
btrfs_free_path(fixup_path);
}
@@ -2709,7 +2709,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (key.type == BTRFS_DIR_INDEX_KEY &&
wc->stage == LOG_WALK_REPLAY_DIR_INDEX) {
- ret = replay_one_dir_item(trans, root, path, eb, i, &key);
+ ret = replay_one_dir_item(wc, path, eb, i, &key);
if (ret)
break;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 17/33] btrfs: pass walk_control structure to drop_one_dir_item() and helpers
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (15 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 16/33] btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 18/33] btrfs: pass walk_control structure to overwrite_item() fdmanana
` (16 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction as an argument to drop_one_dir_item()
and its helpers (link_to_fixup_dir() and unlink_inode_for_log_replay()),
pass the walk_control structure as we can access the transaction from it
and the subvolume root. This is going to be needed by an incoming change
that improves error reporting for log replay and also reduces the number
of arguments passed to link_to_fixup_dir().
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 44 +++++++++++++++++++++++---------------------
1 file changed, 23 insertions(+), 21 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index c4c2fbf291a1..01a0f7cbcd4b 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -156,8 +156,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode,
int inode_only,
struct btrfs_log_ctx *ctx);
-static int link_to_fixup_dir(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int link_to_fixup_dir(struct walk_control *wc,
struct btrfs_path *path, u64 objectid);
static noinline int replay_dir_deletes(struct walk_control *wc,
struct btrfs_path *path,
@@ -927,11 +926,12 @@ static noinline int replay_one_extent(struct walk_control *wc,
return ret;
}
-static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans,
+static int unlink_inode_for_log_replay(struct walk_control *wc,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
const struct fscrypt_str *name)
{
+ struct btrfs_trans_handle *trans = wc->trans;
int ret;
ret = btrfs_unlink_inode(trans, dir, inode, name);
@@ -959,11 +959,12 @@ static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans,
* This is a helper function to do the unlink of a specific directory
* item
*/
-static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
+static noinline int drop_one_dir_item(struct walk_control *wc,
struct btrfs_path *path,
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;
@@ -990,11 +991,11 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
goto out;
}
- ret = link_to_fixup_dir(trans, root, path, location.objectid);
+ ret = link_to_fixup_dir(wc, path, location.objectid);
if (ret)
goto out;
- ret = unlink_inode_for_log_replay(trans, dir, inode, &name);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &name);
out:
kfree(name.name);
if (inode)
@@ -1135,7 +1136,7 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
inc_nlink(&inode->vfs_inode);
btrfs_release_path(path);
- ret = unlink_inode_for_log_replay(trans, dir, inode, &victim_name);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &victim_name);
kfree(victim_name.name);
if (ret)
return ret;
@@ -1207,7 +1208,7 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
inc_nlink(&inode->vfs_inode);
btrfs_release_path(path);
- ret = unlink_inode_for_log_replay(trans, victim_parent, inode,
+ ret = unlink_inode_for_log_replay(wc, victim_parent, inode,
&victim_name);
iput(&victim_parent->vfs_inode);
kfree(victim_name.name);
@@ -1281,7 +1282,7 @@ static inline int __add_inode_ref(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
return ret;
} else if (di) {
- ret = drop_one_dir_item(trans, path, dir, di);
+ ret = drop_one_dir_item(wc, path, dir, di);
if (ret)
return ret;
}
@@ -1292,7 +1293,7 @@ static inline int __add_inode_ref(struct walk_control *wc,
if (IS_ERR(di)) {
return PTR_ERR(di);
} else if (di) {
- ret = drop_one_dir_item(trans, path, dir, di);
+ ret = drop_one_dir_item(wc, path, dir, di);
if (ret)
return ret;
}
@@ -1415,7 +1416,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
goto out;
}
- ret = unlink_inode_for_log_replay(trans, dir, inode, &name);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &name);
kfree(name.name);
iput(&dir->vfs_inode);
if (ret)
@@ -1842,11 +1843,12 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
* count when replay is done. The link count is incremented here
* so the inode won't go away until we check it
*/
-static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static noinline int link_to_fixup_dir(struct walk_control *wc,
struct btrfs_path *path,
u64 objectid)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
struct btrfs_key key;
int ret = 0;
struct btrfs_inode *inode;
@@ -1917,7 +1919,7 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans,
return ret;
}
-static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans,
+static int delete_conflicting_dir_entry(struct walk_control *wc,
struct btrfs_inode *dir,
struct btrfs_path *path,
struct btrfs_dir_item *dst_di,
@@ -1942,7 +1944,7 @@ static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans,
if (!exists)
return 0;
- return drop_one_dir_item(trans, path, dir, dst_di);
+ return drop_one_dir_item(wc, path, dir, dst_di);
}
/*
@@ -2014,7 +2016,7 @@ static noinline int replay_one_name(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
goto out;
} else if (dir_dst_di) {
- ret = delete_conflicting_dir_entry(trans, dir, path, dir_dst_di,
+ ret = delete_conflicting_dir_entry(wc, dir, path, dir_dst_di,
&log_key, log_flags, exists);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
@@ -2033,7 +2035,7 @@ static noinline int replay_one_name(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
goto out;
} else if (index_dst_di) {
- ret = delete_conflicting_dir_entry(trans, dir, path, index_dst_di,
+ ret = delete_conflicting_dir_entry(wc, dir, path, index_dst_di,
&log_key, log_flags, exists);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
@@ -2161,7 +2163,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
}
btrfs_dir_item_key_to_cpu(eb, di, &di_key);
- ret = link_to_fixup_dir(wc->trans, wc->root, fixup_path, di_key.objectid);
+ ret = link_to_fixup_dir(wc, fixup_path, di_key.objectid);
btrfs_free_path(fixup_path);
}
@@ -2317,12 +2319,12 @@ static noinline int check_item_in_log(struct walk_control *wc,
goto out;
}
- ret = link_to_fixup_dir(trans, root, path, location.objectid);
+ ret = link_to_fixup_dir(wc, path, location.objectid);
if (ret)
goto out;
inc_nlink(&inode->vfs_inode);
- ret = unlink_inode_for_log_replay(trans, dir, inode, &name);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &name);
/*
* Unlike dir item keys, dir index keys can only have one name (entry) in
* them, as there are no key collisions since each key has a unique offset
@@ -2699,7 +2701,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
}
- ret = link_to_fixup_dir(trans, root, path, key.objectid);
+ ret = link_to_fixup_dir(wc, path, key.objectid);
if (ret)
break;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 18/33] btrfs: pass walk_control structure to overwrite_item()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (16 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 17/33] btrfs: pass walk_control structure to drop_one_dir_item() and helpers fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 19/33] btrfs: use level argument in log tree walk callback process_one_buffer() fdmanana
` (15 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
Instead of passing the transaction and subvolume root as arguments to
overwrite_item(), pass the walk_control structure as we can grab them
from the structure. This reduces the number of arguments passed and it's
going to be needed by an incoming change that improves error reporting
for log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 01a0f7cbcd4b..2060f0d99f6e 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -410,12 +410,13 @@ static int process_one_buffer(struct extent_buffer *eb,
*
* If the key isn't in the destination yet, a new item is inserted.
*/
-static int overwrite_item(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int overwrite_item(struct walk_control *wc,
struct btrfs_path *path,
struct extent_buffer *eb, int slot,
struct btrfs_key *key)
{
+ struct btrfs_trans_handle *trans = wc->trans;
+ struct btrfs_root *root = wc->root;
int ret;
u32 item_size;
u64 saved_i_size = 0;
@@ -739,7 +740,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
if (found_type == BTRFS_FILE_EXTENT_INLINE) {
/* inline extents are easy, we just overwrite them */
- ret = overwrite_item(trans, root, path, eb, slot, key);
+ ret = overwrite_item(wc, path, eb, slot, key);
if (ret)
goto out;
goto update_inode;
@@ -1607,7 +1608,7 @@ static noinline int add_inode_ref(struct walk_control *wc,
goto out;
/* finally write the back reference in the inode */
- ret = overwrite_item(trans, root, path, eb, slot, key);
+ ret = overwrite_item(wc, path, eb, slot, key);
out:
btrfs_release_path(path);
kfree(name.name);
@@ -2657,7 +2658,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (ret)
break;
}
- ret = overwrite_item(trans, root, path, eb, i, &key);
+ ret = overwrite_item(wc, path, eb, i, &key);
if (ret)
break;
@@ -2721,7 +2722,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
/* these keys are simply copied */
if (key.type == BTRFS_XATTR_ITEM_KEY) {
- ret = overwrite_item(trans, root, path, eb, i, &key);
+ ret = overwrite_item(wc, path, eb, i, &key);
if (ret)
break;
} else if (key.type == BTRFS_INODE_REF_KEY ||
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 19/33] btrfs: use level argument in log tree walk callback process_one_buffer()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (17 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 18/33] btrfs: pass walk_control structure to overwrite_item() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 20/33] btrfs: use level argument in log tree walk callback replay_one_buffer() fdmanana
` (14 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We already have the extent buffer's level in an argument, there's no need
to call btrfs_header_level(). So use the level argument and make the code
shorter.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 2060f0d99f6e..166ceb003a1e 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -386,8 +386,7 @@ static int process_one_buffer(struct extent_buffer *eb,
return ret;
}
- if (btrfs_buffer_uptodate(eb, gen, false) &&
- btrfs_header_level(eb) == 0) {
+ if (btrfs_buffer_uptodate(eb, gen, false) && level == 0) {
ret = btrfs_exclude_logged_extents(eb);
if (ret)
btrfs_abort_transaction(trans, ret);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 20/33] btrfs: use level argument in log tree walk callback replay_one_buffer()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (18 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 19/33] btrfs: use level argument in log tree walk callback process_one_buffer() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 21/33] btrfs: use the inode item boolean everywhere in overwrite_item() fdmanana
` (13 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We already have the extent buffer's level in an argument, there's no need
to first ensure the extent buffer's data is loaded (by calling
btrfs_read_extent_buffer()) and then call btrfs_header_level() to check
the level. So use the level argument and do the check before calling
btrfs_read_extent_buffer().
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 166ceb003a1e..88e813bb28d8 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2591,17 +2591,15 @@ static int replay_one_buffer(struct extent_buffer *eb,
int i;
int ret;
+ if (level != 0)
+ return 0;
+
ret = btrfs_read_extent_buffer(eb, &check);
if (ret) {
btrfs_abort_transaction(trans, ret);
return ret;
}
- level = btrfs_header_level(eb);
-
- if (level != 0)
- return 0;
-
path = btrfs_alloc_path();
if (!path) {
btrfs_abort_transaction(trans, -ENOMEM);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 21/33] btrfs: use the inode item boolean everywhere in overwrite_item()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (19 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 20/33] btrfs: use level argument in log tree walk callback replay_one_buffer() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 22/33] btrfs: add current log leaf, key and slot to struct walk_control fdmanana
` (12 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We have this boolean 'inode_item' to tell if we are processing an inode
item key and we use it in a couple of places while in another two places
we open code by checking if the key type matches the inode item type.
Make this consistent and use the boolean everywhere. Also rename it from
'inode_item' to 'is_inode_item', which makes it more clear that it's a
boolean and not an instance of struct btrfs_inode_item, and make it const
too.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 88e813bb28d8..d830c33be7c6 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -424,7 +424,7 @@ static int overwrite_item(struct walk_control *wc,
unsigned long dst_ptr;
struct extent_buffer *dst_eb;
int dst_slot;
- bool inode_item = key->type == BTRFS_INODE_ITEM_KEY;
+ const bool is_inode_item = (key->type == BTRFS_INODE_ITEM_KEY);
/*
* This is only used during log replay, so the root is always from a
@@ -486,7 +486,7 @@ static int overwrite_item(struct walk_control *wc,
* We need to load the old nbytes into the inode so when we
* replay the extents we've logged we get the right nbytes.
*/
- if (inode_item) {
+ if (is_inode_item) {
struct btrfs_inode_item *item;
u64 nbytes;
u32 mode;
@@ -507,7 +507,7 @@ static int overwrite_item(struct walk_control *wc,
if (S_ISDIR(mode))
btrfs_set_inode_size(eb, item, 0);
}
- } else if (inode_item) {
+ } else if (is_inode_item) {
struct btrfs_inode_item *item;
u32 mode;
@@ -561,7 +561,7 @@ static int overwrite_item(struct walk_control *wc,
* state of the tree found in the subvolume, and i_size is modified
* as it goes
*/
- if (key->type == BTRFS_INODE_ITEM_KEY && ret == -EEXIST) {
+ if (is_inode_item && ret == -EEXIST) {
struct btrfs_inode_item *src_item;
struct btrfs_inode_item *dst_item;
@@ -602,7 +602,7 @@ static int overwrite_item(struct walk_control *wc,
}
/* make sure the generation is filled in */
- if (key->type == BTRFS_INODE_ITEM_KEY) {
+ if (is_inode_item) {
struct btrfs_inode_item *dst_item;
dst_item = (struct btrfs_inode_item *)dst_ptr;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 22/33] btrfs: add current log leaf, key and slot to struct walk_control
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (20 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 21/33] btrfs: use the inode item boolean everywhere in overwrite_item() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 23/33] btrfs: avoid unnecessary path allocation at fixup_inode_link_count() fdmanana
` (11 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
A lot of the log replay functions get passed the current log leaf being
processed as well as the current slot and the key at that slot. Instead
of passing them as parameters, add them to struct walk_control so that
we reduce the numbers of parameters. This is also going to be needed to
further changes that improve error reporting during log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 255 ++++++++++++++++++++++----------------------
1 file changed, 126 insertions(+), 129 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index d830c33be7c6..b4e901da9e8b 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -150,6 +150,18 @@ struct walk_control {
*/
int (*process_func)(struct extent_buffer *eb,
struct walk_control *wc, u64 gen, int level);
+
+ /*
+ * The following are used only when stage is >= LOG_WALK_REPLAY_INODES
+ * and by the replay_one_buffer() callback.
+ */
+
+ /* The current log leaf being processed. */
+ struct extent_buffer *log_leaf;
+ /* The key being processed of the current log leaf. */
+ struct btrfs_key log_key;
+ /* The slot being processed of the current log leaf. */
+ int log_slot;
};
static int btrfs_log_inode(struct btrfs_trans_handle *trans,
@@ -396,8 +408,9 @@ static int process_one_buffer(struct extent_buffer *eb,
}
/*
- * Item overwrite used by log replay. The given eb, slot and key all refer to
- * the source data we are copying out.
+ * Item overwrite used by log replay. The given log tree leaf, slot and key
+ * from the walk_control structure all refer to the source data we are copying
+ * out.
*
* The given root is for the tree we are copying into, and path is a scratch
* path for use in this function (it should be released on entry and will be
@@ -409,10 +422,7 @@ static int process_one_buffer(struct extent_buffer *eb,
*
* If the key isn't in the destination yet, a new item is inserted.
*/
-static int overwrite_item(struct walk_control *wc,
- struct btrfs_path *path,
- struct extent_buffer *eb, int slot,
- struct btrfs_key *key)
+static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -424,7 +434,7 @@ static int overwrite_item(struct walk_control *wc,
unsigned long dst_ptr;
struct extent_buffer *dst_eb;
int dst_slot;
- const bool is_inode_item = (key->type == BTRFS_INODE_ITEM_KEY);
+ const bool is_inode_item = (wc->log_key.type == BTRFS_INODE_ITEM_KEY);
/*
* This is only used during log replay, so the root is always from a
@@ -435,11 +445,11 @@ static int overwrite_item(struct walk_control *wc,
*/
ASSERT(btrfs_root_id(root) != BTRFS_TREE_LOG_OBJECTID);
- item_size = btrfs_item_size(eb, slot);
- src_ptr = btrfs_item_ptr_offset(eb, slot);
+ item_size = btrfs_item_size(wc->log_leaf, wc->log_slot);
+ src_ptr = btrfs_item_ptr_offset(wc->log_leaf, wc->log_slot);
/* Look for the key in the destination tree. */
- ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
return ret;
@@ -466,7 +476,7 @@ static int overwrite_item(struct walk_control *wc,
return -ENOMEM;
}
- read_extent_buffer(eb, src_copy, src_ptr, item_size);
+ read_extent_buffer(wc->log_leaf, src_copy, src_ptr, item_size);
dst_ptr = btrfs_item_ptr_offset(dst_eb, dst_slot);
ret = memcmp_extent_buffer(dst_eb, src_copy, dst_ptr, item_size);
@@ -494,18 +504,18 @@ static int overwrite_item(struct walk_control *wc,
item = btrfs_item_ptr(dst_eb, dst_slot,
struct btrfs_inode_item);
nbytes = btrfs_inode_nbytes(dst_eb, item);
- item = btrfs_item_ptr(eb, slot,
+ item = btrfs_item_ptr(wc->log_leaf, wc->log_slot,
struct btrfs_inode_item);
- btrfs_set_inode_nbytes(eb, item, nbytes);
+ btrfs_set_inode_nbytes(wc->log_leaf, item, nbytes);
/*
* If this is a directory we need to reset the i_size to
* 0 so that we can set it up properly when replaying
* the rest of the items in this log.
*/
- mode = btrfs_inode_mode(eb, item);
+ mode = btrfs_inode_mode(wc->log_leaf, item);
if (S_ISDIR(mode))
- btrfs_set_inode_size(eb, item, 0);
+ btrfs_set_inode_size(wc->log_leaf, item, 0);
}
} else if (is_inode_item) {
struct btrfs_inode_item *item;
@@ -515,24 +525,23 @@ static int overwrite_item(struct walk_control *wc,
* New inode, set nbytes to 0 so that the nbytes comes out
* properly when we replay the extents.
*/
- item = btrfs_item_ptr(eb, slot, struct btrfs_inode_item);
- btrfs_set_inode_nbytes(eb, item, 0);
+ item = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_inode_item);
+ btrfs_set_inode_nbytes(wc->log_leaf, item, 0);
/*
* If this is a directory we need to reset the i_size to 0 so
* that we can set it up properly when replaying the rest of
* the items in this log.
*/
- mode = btrfs_inode_mode(eb, item);
+ mode = btrfs_inode_mode(wc->log_leaf, item);
if (S_ISDIR(mode))
- btrfs_set_inode_size(eb, item, 0);
+ btrfs_set_inode_size(wc->log_leaf, item, 0);
}
insert:
btrfs_release_path(path);
/* try to insert the key into the destination tree */
path->skip_release_on_error = 1;
- ret = btrfs_insert_empty_item(trans, root, path,
- key, item_size);
+ ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, item_size);
path->skip_release_on_error = 0;
dst_eb = path->nodes[0];
@@ -568,8 +577,8 @@ static int overwrite_item(struct walk_control *wc,
src_item = (struct btrfs_inode_item *)src_ptr;
dst_item = (struct btrfs_inode_item *)dst_ptr;
- if (btrfs_inode_generation(eb, src_item) == 0) {
- const u64 ino_size = btrfs_inode_size(eb, src_item);
+ if (btrfs_inode_generation(wc->log_leaf, src_item) == 0) {
+ const u64 ino_size = btrfs_inode_size(wc->log_leaf, src_item);
/*
* For regular files an ino_size == 0 is used only when
@@ -578,21 +587,21 @@ static int overwrite_item(struct walk_control *wc,
* case don't set the size of the inode in the fs/subvol
* tree, otherwise we would be throwing valid data away.
*/
- if (S_ISREG(btrfs_inode_mode(eb, src_item)) &&
+ if (S_ISREG(btrfs_inode_mode(wc->log_leaf, src_item)) &&
S_ISREG(btrfs_inode_mode(dst_eb, dst_item)) &&
ino_size != 0)
btrfs_set_inode_size(dst_eb, dst_item, ino_size);
goto no_copy;
}
- if (S_ISDIR(btrfs_inode_mode(eb, src_item)) &&
+ if (S_ISDIR(btrfs_inode_mode(wc->log_leaf, src_item)) &&
S_ISDIR(btrfs_inode_mode(dst_eb, dst_item))) {
save_old_i_size = 1;
saved_i_size = btrfs_inode_size(dst_eb, dst_item);
}
}
- copy_extent_buffer(dst_eb, eb, dst_ptr, src_ptr, item_size);
+ copy_extent_buffer(dst_eb, wc->log_leaf, dst_ptr, src_ptr, item_size);
if (save_old_i_size) {
struct btrfs_inode_item *dst_item;
@@ -641,10 +650,7 @@ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len,
* The extent is inserted into the file, dropping any existing extents
* from the file that overlap the new one.
*/
-static noinline int replay_one_extent(struct walk_control *wc,
- struct btrfs_path *path,
- struct extent_buffer *eb, int slot,
- struct btrfs_key *key)
+static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path *path)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -652,7 +658,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
struct btrfs_fs_info *fs_info = root->fs_info;
int found_type;
u64 extent_end;
- u64 start = key->offset;
+ const u64 start = wc->log_key.offset;
u64 nbytes = 0;
u64 csum_start;
u64 csum_end;
@@ -665,34 +671,35 @@ static noinline int replay_one_extent(struct walk_control *wc,
unsigned long size;
int ret = 0;
- item = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
- found_type = btrfs_file_extent_type(eb, item);
+ item = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_file_extent_item);
+ found_type = btrfs_file_extent_type(wc->log_leaf, item);
if (found_type == BTRFS_FILE_EXTENT_REG ||
found_type == BTRFS_FILE_EXTENT_PREALLOC) {
- nbytes = btrfs_file_extent_num_bytes(eb, item);
+ nbytes = btrfs_file_extent_num_bytes(wc->log_leaf, item);
extent_end = start + nbytes;
/*
* We don't add to the inodes nbytes if we are prealloc or a
* hole.
*/
- if (btrfs_file_extent_disk_bytenr(eb, item) == 0)
+ if (btrfs_file_extent_disk_bytenr(wc->log_leaf, item) == 0)
nbytes = 0;
} else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
- size = btrfs_file_extent_ram_bytes(eb, item);
- nbytes = btrfs_file_extent_ram_bytes(eb, item);
+ size = btrfs_file_extent_ram_bytes(wc->log_leaf, item);
+ nbytes = btrfs_file_extent_ram_bytes(wc->log_leaf, item);
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), key->objectid, key->offset);
+ found_type, btrfs_root_id(root), wc->log_key.objectid,
+ wc->log_key.offset);
return -EUCLEAN;
}
- inode = btrfs_iget_logging(key->objectid, root);
+ inode = btrfs_iget_logging(wc->log_key.objectid, root);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
btrfs_abort_transaction(trans, ret);
@@ -719,7 +726,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
* we already have a pointer to this exact extent,
* we don't have to do anything
*/
- if (memcmp_extent_buffer(eb, &existing, (unsigned long)item,
+ if (memcmp_extent_buffer(wc->log_leaf, &existing, (unsigned long)item,
sizeof(existing)) == 0) {
btrfs_release_path(path);
goto out;
@@ -739,7 +746,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
if (found_type == BTRFS_FILE_EXTENT_INLINE) {
/* inline extents are easy, we just overwrite them */
- ret = overwrite_item(wc, path, eb, slot, key);
+ ret = overwrite_item(wc, path);
if (ret)
goto out;
goto update_inode;
@@ -751,18 +758,18 @@ static noinline int replay_one_extent(struct walk_control *wc,
*/
/* A hole and NO_HOLES feature enabled, nothing else to do. */
- if (btrfs_file_extent_disk_bytenr(eb, item) == 0 &&
+ if (btrfs_file_extent_disk_bytenr(wc->log_leaf, item) == 0 &&
btrfs_fs_incompat(fs_info, NO_HOLES))
goto update_inode;
- ret = btrfs_insert_empty_item(trans, root, path, key, sizeof(*item));
+ ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, sizeof(*item));
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
}
dest_offset = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]);
- copy_extent_buffer(path->nodes[0], eb, dest_offset, (unsigned long)item,
- sizeof(*item));
+ copy_extent_buffer(path->nodes[0], wc->log_leaf, dest_offset,
+ (unsigned long)item, sizeof(*item));
/*
* We have an explicit hole and NO_HOLES is not enabled. We have added
@@ -770,15 +777,15 @@ static noinline int replay_one_extent(struct walk_control *wc,
* anything else to do other than update the file extent item range and
* update the inode item.
*/
- if (btrfs_file_extent_disk_bytenr(eb, item) == 0) {
+ if (btrfs_file_extent_disk_bytenr(wc->log_leaf, item) == 0) {
btrfs_release_path(path);
goto update_inode;
}
- ins.objectid = btrfs_file_extent_disk_bytenr(eb, item);
+ ins.objectid = btrfs_file_extent_disk_bytenr(wc->log_leaf, item);
ins.type = BTRFS_EXTENT_ITEM_KEY;
- ins.offset = btrfs_file_extent_disk_num_bytes(eb, item);
- offset = key->offset - btrfs_file_extent_offset(eb, item);
+ ins.offset = btrfs_file_extent_disk_num_bytes(wc->log_leaf, item);
+ offset = wc->log_key.offset - btrfs_file_extent_offset(wc->log_leaf, item);
/*
* Manually record dirty extent, as here we did a shallow file extent
@@ -810,7 +817,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
.ref_root = btrfs_root_id(root),
};
- btrfs_init_data_ref(&ref, key->objectid, offset, 0, false);
+ 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);
@@ -819,7 +826,7 @@ static noinline int replay_one_extent(struct walk_control *wc,
} else {
/* Insert the extent pointer in the extent tree. */
ret = btrfs_alloc_logged_file_extent(trans, btrfs_root_id(root),
- key->objectid, offset, &ins);
+ wc->log_key.objectid, offset, &ins);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
@@ -828,12 +835,12 @@ static noinline int replay_one_extent(struct walk_control *wc,
btrfs_release_path(path);
- if (btrfs_file_extent_compression(eb, item)) {
+ if (btrfs_file_extent_compression(wc->log_leaf, item)) {
csum_start = ins.objectid;
csum_end = csum_start + ins.offset;
} else {
- csum_start = ins.objectid + btrfs_file_extent_offset(eb, item);
- csum_end = csum_start + btrfs_file_extent_num_bytes(eb, item);
+ csum_start = ins.objectid + btrfs_file_extent_offset(wc->log_leaf, item);
+ csum_end = csum_start + btrfs_file_extent_num_bytes(wc->log_leaf, item);
}
ret = btrfs_lookup_csums_list(root->log_root, csum_start, csum_end - 1,
@@ -1352,10 +1359,7 @@ 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_path *path,
- struct btrfs_inode *inode,
- struct extent_buffer *log_eb,
- int log_slot,
- struct btrfs_key *key)
+ struct btrfs_inode *inode)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1366,7 +1370,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
again:
btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0);
if (ret > 0) {
ret = 0;
goto out;
@@ -1383,7 +1387,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
struct fscrypt_str name;
u64 parent_id;
- if (key->type == BTRFS_INODE_EXTREF_KEY) {
+ if (wc->log_key.type == BTRFS_INODE_EXTREF_KEY) {
ret = extref_get_fields(eb, ref_ptr, &name,
NULL, &parent_id);
if (ret) {
@@ -1391,7 +1395,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
goto out;
}
} else {
- parent_id = key->offset;
+ parent_id = wc->log_key.offset;
ret = ref_get_fields(eb, ref_ptr, &name, NULL);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -1399,11 +1403,12 @@ static int unlink_old_inode_refs(struct walk_control *wc,
}
}
- if (key->type == BTRFS_INODE_EXTREF_KEY)
- ret = !!btrfs_find_name_in_ext_backref(log_eb, log_slot,
+ if (wc->log_key.type == BTRFS_INODE_EXTREF_KEY)
+ ret = !!btrfs_find_name_in_ext_backref(wc->log_leaf, wc->log_slot,
parent_id, &name);
else
- ret = !!btrfs_find_name_in_backref(log_eb, log_slot, &name);
+ ret = !!btrfs_find_name_in_backref(wc->log_leaf, wc->log_slot,
+ &name);
if (!ret) {
struct btrfs_inode *dir;
@@ -1426,7 +1431,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
kfree(name.name);
ref_ptr += name.len;
- if (key->type == BTRFS_INODE_EXTREF_KEY)
+ if (wc->log_key.type == BTRFS_INODE_EXTREF_KEY)
ref_ptr += sizeof(struct btrfs_inode_extref);
else
ref_ptr += sizeof(struct btrfs_inode_ref);
@@ -1438,15 +1443,10 @@ static int unlink_old_inode_refs(struct walk_control *wc,
}
/*
- * replay one inode back reference item found in the log tree.
- * eb, slot and key refer to the buffer and key found in the log tree.
- * root is the destination we are replaying into, and path is for temp
- * use by this function. (it should be released on return).
+ * Replay one inode back reference item found in the log tree.
+ * Path is for temporary use by this function (it should be released on return).
*/
-static noinline int add_inode_ref(struct walk_control *wc,
- struct btrfs_path *path,
- struct extent_buffer *eb, int slot,
- struct btrfs_key *key)
+static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *path)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1456,26 +1456,26 @@ static noinline int add_inode_ref(struct walk_control *wc,
unsigned long ref_end;
struct fscrypt_str name = { 0 };
int ret;
- const bool is_extref_item = (key->type == BTRFS_INODE_EXTREF_KEY);
+ const bool is_extref_item = (wc->log_key.type == BTRFS_INODE_EXTREF_KEY);
u64 parent_objectid;
u64 inode_objectid;
u64 ref_index = 0;
int ref_struct_size;
- ref_ptr = btrfs_item_ptr_offset(eb, slot);
- ref_end = ref_ptr + btrfs_item_size(eb, slot);
+ ref_ptr = btrfs_item_ptr_offset(wc->log_leaf, wc->log_slot);
+ ref_end = ref_ptr + btrfs_item_size(wc->log_leaf, wc->log_slot);
if (is_extref_item) {
struct btrfs_inode_extref *r;
ref_struct_size = sizeof(struct btrfs_inode_extref);
r = (struct btrfs_inode_extref *)ref_ptr;
- parent_objectid = btrfs_inode_extref_parent(eb, r);
+ parent_objectid = btrfs_inode_extref_parent(wc->log_leaf, r);
} else {
ref_struct_size = sizeof(struct btrfs_inode_ref);
- parent_objectid = key->offset;
+ parent_objectid = wc->log_key.offset;
}
- inode_objectid = key->objectid;
+ inode_objectid = wc->log_key.objectid;
/*
* it is possible that we didn't log all the parent directories
@@ -1504,7 +1504,7 @@ static noinline int add_inode_ref(struct walk_control *wc,
while (ref_ptr < ref_end) {
if (is_extref_item) {
- ret = extref_get_fields(eb, ref_ptr, &name,
+ ret = extref_get_fields(wc->log_leaf, ref_ptr, &name,
&ref_index, &parent_objectid);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -1541,7 +1541,7 @@ static noinline int add_inode_ref(struct walk_control *wc,
}
}
} else {
- ret = ref_get_fields(eb, ref_ptr, &name, &ref_index);
+ ret = ref_get_fields(wc->log_leaf, ref_ptr, &name, &ref_index);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
@@ -1602,12 +1602,12 @@ static noinline int add_inode_ref(struct walk_control *wc,
* dir index entries exist for a name but there is no inode reference
* item with the same name.
*/
- ret = unlink_old_inode_refs(wc, path, inode, eb, slot, key);
+ ret = unlink_old_inode_refs(wc, path, inode);
if (ret)
goto out;
/* finally write the back reference in the inode */
- ret = overwrite_item(wc, path, eb, slot, key);
+ ret = overwrite_item(wc, path);
out:
btrfs_release_path(path);
kfree(name.name);
@@ -1965,9 +1965,7 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,
*/
static noinline int replay_one_name(struct walk_control *wc,
struct btrfs_path *path,
- struct extent_buffer *eb,
- struct btrfs_dir_item *di,
- struct btrfs_key *key)
+ struct btrfs_dir_item *di)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1985,21 +1983,22 @@ static noinline int replay_one_name(struct walk_control *wc,
bool update_size = true;
bool name_added = false;
- dir = btrfs_iget_logging(key->objectid, root);
+ dir = btrfs_iget_logging(wc->log_key.objectid, root);
if (IS_ERR(dir)) {
ret = PTR_ERR(dir);
btrfs_abort_transaction(trans, ret);
return ret;
}
- ret = read_alloc_one_name(eb, di + 1, btrfs_dir_name_len(eb, di), &name);
+ 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);
goto out;
}
- log_flags = btrfs_dir_flags(eb, di);
- btrfs_dir_item_key_to_cpu(eb, di, &log_key);
+ log_flags = btrfs_dir_flags(wc->log_leaf, di);
+ btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &log_key);
ret = btrfs_lookup_inode(trans, root, path, &log_key, 0);
btrfs_release_path(path);
if (ret < 0) {
@@ -2009,7 +2008,7 @@ static noinline int replay_one_name(struct walk_control *wc,
exists = (ret == 0);
ret = 0;
- dir_dst_di = btrfs_lookup_dir_item(trans, root, path, key->objectid,
+ dir_dst_di = btrfs_lookup_dir_item(trans, root, path, wc->log_key.objectid,
&name, 1);
if (IS_ERR(dir_dst_di)) {
ret = PTR_ERR(dir_dst_di);
@@ -2028,8 +2027,8 @@ static noinline int replay_one_name(struct walk_control *wc,
btrfs_release_path(path);
index_dst_di = btrfs_lookup_dir_index_item(trans, root, path,
- key->objectid, key->offset,
- &name, 1);
+ wc->log_key.objectid,
+ wc->log_key.offset, &name, 1);
if (IS_ERR(index_dst_di)) {
ret = PTR_ERR(index_dst_di);
btrfs_abort_transaction(trans, ret);
@@ -2058,7 +2057,7 @@ static noinline int replay_one_name(struct walk_control *wc,
*/
search_key.objectid = log_key.objectid;
search_key.type = BTRFS_INODE_REF_KEY;
- search_key.offset = key->objectid;
+ 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);
@@ -2072,8 +2071,8 @@ static noinline int replay_one_name(struct walk_control *wc,
search_key.objectid = log_key.objectid;
search_key.type = BTRFS_INODE_EXTREF_KEY;
- search_key.offset = btrfs_extref_hash(key->objectid, name.name, name.len);
- ret = backref_in_log(root->log_root, &search_key, key->objectid, &name);
+ 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);
goto out;
@@ -2084,7 +2083,7 @@ static noinline int replay_one_name(struct walk_control *wc,
goto out;
}
btrfs_release_path(path);
- ret = insert_one_name(trans, root, key->objectid, key->offset,
+ 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);
@@ -2111,18 +2110,16 @@ static noinline int replay_one_name(struct walk_control *wc,
/* Replay one dir item from a BTRFS_DIR_INDEX_KEY key. */
static noinline int replay_one_dir_item(struct walk_control *wc,
- struct btrfs_path *path,
- struct extent_buffer *eb, int slot,
- struct btrfs_key *key)
+ struct btrfs_path *path)
{
int ret;
struct btrfs_dir_item *di;
/* We only log dir index keys, which only contain a single dir item. */
- ASSERT(key->type == BTRFS_DIR_INDEX_KEY);
+ ASSERT(wc->log_key.type == BTRFS_DIR_INDEX_KEY);
- di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
- ret = replay_one_name(wc, path, eb, di, key);
+ di = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_dir_item);
+ ret = replay_one_name(wc, path, di);
if (ret < 0)
return ret;
@@ -2152,7 +2149,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
* to ever delete the parent directory has it would result in stale
* dentries that can never be deleted.
*/
- if (ret == 1 && btrfs_dir_ftype(eb, di) != BTRFS_FT_DIR) {
+ if (ret == 1 && btrfs_dir_ftype(wc->log_leaf, di) != BTRFS_FT_DIR) {
struct btrfs_path *fixup_path;
struct btrfs_key di_key;
@@ -2162,7 +2159,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
return -ENOMEM;
}
- btrfs_dir_item_key_to_cpu(eb, di, &di_key);
+ btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &di_key);
ret = link_to_fixup_dir(wc, fixup_path, di_key.objectid);
btrfs_free_path(fixup_path);
}
@@ -2339,15 +2336,14 @@ static noinline int check_item_in_log(struct walk_control *wc,
return ret;
}
-static int replay_xattr_deletes(struct walk_control *wc,
- struct btrfs_path *path,
- const u64 ino)
+static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
struct btrfs_root *log = wc->log;
struct btrfs_key search_key;
struct btrfs_path *log_path;
+ const u64 ino = wc->log_key.objectid;
int i;
int nritems;
int ret;
@@ -2587,8 +2583,6 @@ static int replay_one_buffer(struct extent_buffer *eb,
struct btrfs_path *path;
struct btrfs_root *root = wc->root;
struct btrfs_trans_handle *trans = wc->trans;
- struct btrfs_key key;
- int i;
int ret;
if (level != 0)
@@ -2606,14 +2600,17 @@ static int replay_one_buffer(struct extent_buffer *eb,
return -ENOMEM;
}
+ wc->log_leaf = eb;
+
nritems = btrfs_header_nritems(eb);
- for (i = 0; i < nritems; i++) {
+ for (wc->log_slot = 0; wc->log_slot < nritems; wc->log_slot++) {
struct btrfs_inode_item *inode_item;
- btrfs_item_key_to_cpu(eb, &key, i);
+ btrfs_item_key_to_cpu(eb, &wc->log_key, wc->log_slot);
- if (key.type == BTRFS_INODE_ITEM_KEY) {
- inode_item = btrfs_item_ptr(eb, i, struct btrfs_inode_item);
+ if (wc->log_key.type == BTRFS_INODE_ITEM_KEY) {
+ inode_item = btrfs_item_ptr(eb, wc->log_slot,
+ struct btrfs_inode_item);
/*
* An inode with no links is either:
*
@@ -2642,20 +2639,20 @@ static int replay_one_buffer(struct extent_buffer *eb,
}
/* Inode keys are done during the first stage. */
- if (key.type == BTRFS_INODE_ITEM_KEY &&
+ if (wc->log_key.type == BTRFS_INODE_ITEM_KEY &&
wc->stage == LOG_WALK_REPLAY_INODES) {
u32 mode;
- ret = replay_xattr_deletes(wc, path, key.objectid);
+ ret = replay_xattr_deletes(wc, path);
if (ret)
break;
mode = btrfs_inode_mode(eb, inode_item);
if (S_ISDIR(mode)) {
- ret = replay_dir_deletes(wc, path, key.objectid, false);
+ ret = replay_dir_deletes(wc, path, wc->log_key.objectid, false);
if (ret)
break;
}
- ret = overwrite_item(wc, path, eb, i, &key);
+ ret = overwrite_item(wc, path);
if (ret)
break;
@@ -2672,7 +2669,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
struct btrfs_inode *inode;
u64 from;
- inode = btrfs_iget_logging(key.objectid, root);
+ inode = btrfs_iget_logging(wc->log_key.objectid, root);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
btrfs_abort_transaction(trans, ret);
@@ -2699,7 +2696,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
}
- ret = link_to_fixup_dir(wc, path, key.objectid);
+ ret = link_to_fixup_dir(wc, path, wc->log_key.objectid);
if (ret)
break;
}
@@ -2707,9 +2704,9 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (wc->ignore_cur_inode)
continue;
- if (key.type == BTRFS_DIR_INDEX_KEY &&
+ if (wc->log_key.type == BTRFS_DIR_INDEX_KEY &&
wc->stage == LOG_WALK_REPLAY_DIR_INDEX) {
- ret = replay_one_dir_item(wc, path, eb, i, &key);
+ ret = replay_one_dir_item(wc, path);
if (ret)
break;
}
@@ -2718,17 +2715,17 @@ static int replay_one_buffer(struct extent_buffer *eb,
continue;
/* these keys are simply copied */
- if (key.type == BTRFS_XATTR_ITEM_KEY) {
- ret = overwrite_item(wc, path, eb, i, &key);
+ if (wc->log_key.type == BTRFS_XATTR_ITEM_KEY) {
+ ret = overwrite_item(wc, path);
if (ret)
break;
- } else if (key.type == BTRFS_INODE_REF_KEY ||
- key.type == BTRFS_INODE_EXTREF_KEY) {
- ret = add_inode_ref(wc, path, eb, i, &key);
+ } else if (wc->log_key.type == BTRFS_INODE_REF_KEY ||
+ wc->log_key.type == BTRFS_INODE_EXTREF_KEY) {
+ ret = add_inode_ref(wc, path);
if (ret)
break;
- } else if (key.type == BTRFS_EXTENT_DATA_KEY) {
- ret = replay_one_extent(wc, path, eb, i, &key);
+ } else if (wc->log_key.type == BTRFS_EXTENT_DATA_KEY) {
+ ret = replay_one_extent(wc, path);
if (ret)
break;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 23/33] btrfs: avoid unnecessary path allocation at fixup_inode_link_count()
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (21 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 22/33] btrfs: add current log leaf, key and slot to struct walk_control fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 24/33] btrfs: avoid path allocations when dropping extents during log replay fdmanana
` (10 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
There's no need to allocate a path as our single caller already has a
path that we can use. So pass the caller's path and use it.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index b4e901da9e8b..5754333ae732 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1728,19 +1728,15 @@ static int count_inode_refs(struct btrfs_inode *inode, struct btrfs_path *path)
* will free the inode.
*/
static noinline int fixup_inode_link_count(struct walk_control *wc,
+ struct btrfs_path *path,
struct btrfs_inode *inode)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = inode->root;
- struct btrfs_path *path;
int ret;
u64 nlink = 0;
const u64 ino = btrfs_ino(inode);
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
-
ret = count_inode_refs(inode, path);
if (ret < 0)
goto out;
@@ -1776,7 +1772,7 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(path);
return ret;
}
@@ -1821,7 +1817,7 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
break;
}
- ret = fixup_inode_link_count(wc, inode);
+ ret = fixup_inode_link_count(wc, path, inode);
iput(&inode->vfs_inode);
if (ret)
break;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 24/33] btrfs: avoid path allocations when dropping extents during log replay
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (22 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 23/33] btrfs: avoid unnecessary path allocation at fixup_inode_link_count() fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 25/33] btrfs: avoid unnecessary path allocation when replaying a dir item fdmanana
` (9 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We can avoid a path allocation in the btrfs_drop_extents() calls we have
at replay_one_extent() and replay_one_buffer() by passing the path we
already have in those contextes as it's unused by the time they call
btrfs_drop_extents().
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 5754333ae732..a912ccdf1485 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -738,6 +738,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
drop_args.start = start;
drop_args.end = extent_end;
drop_args.drop_cache = true;
+ drop_args.path = path;
ret = btrfs_drop_extents(trans, root, inode, &drop_args);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -2676,6 +2677,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
drop_args.start = from;
drop_args.end = (u64)-1;
drop_args.drop_cache = true;
+ drop_args.path = path;
ret = btrfs_drop_extents(trans, root, inode, &drop_args);
if (ret) {
btrfs_abort_transaction(trans, ret);
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 25/33] btrfs: avoid unnecessary path allocation when replaying a dir item
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (23 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 24/33] btrfs: avoid path allocations when dropping extents during log replay fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 26/33] btrfs: remove redundant path release when processing dentry during log replay fdmanana
` (8 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
There's no need to allocate 'fixup_path' at replay_one_dir_item(), as the
path passed as an argument is unused by the time link_to_fixup_dir() is
called (replay_one_name() releases the path before it returns).
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index a912ccdf1485..de1f1c024dc0 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2147,18 +2147,10 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
* dentries that can never be deleted.
*/
if (ret == 1 && btrfs_dir_ftype(wc->log_leaf, di) != BTRFS_FT_DIR) {
- struct btrfs_path *fixup_path;
struct btrfs_key di_key;
- fixup_path = btrfs_alloc_path();
- if (!fixup_path) {
- btrfs_abort_transaction(wc->trans, -ENOMEM);
- return -ENOMEM;
- }
-
btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &di_key);
- ret = link_to_fixup_dir(wc, fixup_path, di_key.objectid);
- btrfs_free_path(fixup_path);
+ ret = link_to_fixup_dir(wc, path, di_key.objectid);
}
return ret;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 26/33] btrfs: remove redundant path release when processing dentry during log replay
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (24 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 25/33] btrfs: avoid unnecessary path allocation when replaying a dir item fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 27/33] btrfs: remove redundant path release when overwriting item " fdmanana
` (7 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
At replay_one_one() we have a redundant btrfs_release_path() just before
calling insert_one_name(), as some lines above we have already released
the path with another btrfs_release_path() call. So remove it.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index de1f1c024dc0..65b8858e82d1 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2079,7 +2079,6 @@ static noinline int replay_one_name(struct walk_control *wc,
update_size = false;
goto out;
}
- btrfs_release_path(path);
ret = insert_one_name(trans, root, wc->log_key.objectid, wc->log_key.offset,
&name, &log_key);
if (ret && ret != -ENOENT && ret != -EEXIST) {
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 27/33] btrfs: remove redundant path release when overwriting item during log replay
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (25 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 26/33] btrfs: remove redundant path release when processing dentry during log replay fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 28/33] btrfs: add path for subvolume tree changes to struct walk_control fdmanana
` (6 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
At overwrite_item() we have a redundant btrfs_release_path() just before
failing with -ENOMEM, as the caller who passed in the path will free it
and therefore also release any refcounts and locks on the extent buffers
of the path. So remove it.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 65b8858e82d1..66500ebdd35c 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -471,7 +471,6 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
}
src_copy = kmalloc(item_size, GFP_NOFS);
if (!src_copy) {
- btrfs_release_path(path);
btrfs_abort_transaction(trans, -ENOMEM);
return -ENOMEM;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 28/33] btrfs: add path for subvolume tree changes to struct walk_control
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (26 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 27/33] btrfs: remove redundant path release when overwriting item " fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 29/33] btrfs: stop passing inode object IDs to __add_inode_ref() in log replay fdmanana
` (5 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
While replaying log trees we need to do searches and updates to subvolume
trees and for that we use a path that we allocate in replay_one_buffer()
and then pass it as a parameter to other functions deeper in the log
replay call chain. Instead of passing it as parameter, add it to struct
walk_control since we pass a pointer to that structure for every log
replay function.
This reduces the number of arguments passed to the functions and it will
be needed and important for an upcoming changes that improves error
reporting for log replay. Also name the new filed in struct walk_control
to 'subvol_path' - while that is longer to type, the naming makes it clear
it's used for subvolume tree operations since many of the log replay
functions operate both on subvolume and log trees, and for the log tree
searches we have struct walk_control::log_leaf to also make it obvious
it's an extent buffer for a log tree extent buffer.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 323 +++++++++++++++++++++-----------------------
1 file changed, 156 insertions(+), 167 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 66500ebdd35c..747daaaaf332 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -162,16 +162,17 @@ struct walk_control {
struct btrfs_key log_key;
/* The slot being processed of the current log leaf. */
int log_slot;
+
+ /* A path used for searches and modifications to subvolume trees. */
+ struct btrfs_path *subvol_path;
};
static int btrfs_log_inode(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode,
int inode_only,
struct btrfs_log_ctx *ctx);
-static int link_to_fixup_dir(struct walk_control *wc,
- struct btrfs_path *path, u64 objectid);
+static int link_to_fixup_dir(struct walk_control *wc, u64 objectid);
static noinline int replay_dir_deletes(struct walk_control *wc,
- struct btrfs_path *path,
u64 dirid, bool del_all);
static void wait_log_commit(struct btrfs_root *root, int transid);
@@ -422,7 +423,7 @@ static int process_one_buffer(struct extent_buffer *eb,
*
* If the key isn't in the destination yet, a new item is inserted.
*/
-static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
+static int overwrite_item(struct walk_control *wc)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -449,14 +450,14 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
src_ptr = btrfs_item_ptr_offset(wc->log_leaf, wc->log_slot);
/* Look for the key in the destination tree. */
- ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
return ret;
}
- dst_eb = path->nodes[0];
- dst_slot = path->slots[0];
+ dst_eb = wc->subvol_path->nodes[0];
+ dst_slot = wc->subvol_path->slots[0];
if (ret == 0) {
char *src_copy;
@@ -466,7 +467,7 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
goto insert;
if (item_size == 0) {
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return 0;
}
src_copy = kmalloc(item_size, GFP_NOFS);
@@ -487,7 +488,7 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
* sync
*/
if (ret == 0) {
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return 0;
}
@@ -537,23 +538,23 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
btrfs_set_inode_size(wc->log_leaf, item, 0);
}
insert:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* try to insert the key into the destination tree */
- path->skip_release_on_error = 1;
- ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, item_size);
- path->skip_release_on_error = 0;
+ wc->subvol_path->skip_release_on_error = 1;
+ ret = btrfs_insert_empty_item(trans, root, wc->subvol_path, &wc->log_key, item_size);
+ wc->subvol_path->skip_release_on_error = 0;
- dst_eb = path->nodes[0];
- dst_slot = path->slots[0];
+ dst_eb = wc->subvol_path->nodes[0];
+ dst_slot = wc->subvol_path->slots[0];
/* make sure any existing item is the correct size */
if (ret == -EEXIST || ret == -EOVERFLOW) {
const u32 found_size = btrfs_item_size(dst_eb, dst_slot);
if (found_size > item_size)
- btrfs_truncate_item(trans, path, item_size, 1);
+ btrfs_truncate_item(trans, wc->subvol_path, item_size, 1);
else if (found_size < item_size)
- btrfs_extend_item(trans, path, item_size - found_size);
+ btrfs_extend_item(trans, wc->subvol_path, item_size - found_size);
} else if (ret) {
btrfs_abort_transaction(trans, ret);
return ret;
@@ -618,7 +619,7 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)
btrfs_set_inode_generation(dst_eb, dst_item, trans->transid);
}
no_copy:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return 0;
}
@@ -649,7 +650,7 @@ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len,
* The extent is inserted into the file, dropping any existing extents
* from the file that overlap the new one.
*/
-static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path *path)
+static noinline int replay_one_extent(struct walk_control *wc)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -710,16 +711,18 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
* file. This must be done before the btrfs_drop_extents run
* so we don't try to drop this extent.
*/
- ret = btrfs_lookup_file_extent(trans, root, path, btrfs_ino(inode), start, 0);
+ ret = btrfs_lookup_file_extent(trans, root, wc->subvol_path,
+ btrfs_ino(inode), start, 0);
if (ret == 0 &&
(found_type == BTRFS_FILE_EXTENT_REG ||
found_type == BTRFS_FILE_EXTENT_PREALLOC)) {
+ struct extent_buffer *leaf = wc->subvol_path->nodes[0];
struct btrfs_file_extent_item existing;
unsigned long ptr;
- ptr = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]);
- read_extent_buffer(path->nodes[0], &existing, ptr, sizeof(existing));
+ ptr = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]);
+ read_extent_buffer(leaf, &existing, ptr, sizeof(existing));
/*
* we already have a pointer to this exact extent,
@@ -727,17 +730,17 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
*/
if (memcmp_extent_buffer(wc->log_leaf, &existing, (unsigned long)item,
sizeof(existing)) == 0) {
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
goto out;
}
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* drop any overlapping extents */
drop_args.start = start;
drop_args.end = extent_end;
drop_args.drop_cache = true;
- drop_args.path = path;
+ drop_args.path = wc->subvol_path;
ret = btrfs_drop_extents(trans, root, inode, &drop_args);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -746,7 +749,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
if (found_type == BTRFS_FILE_EXTENT_INLINE) {
/* inline extents are easy, we just overwrite them */
- ret = overwrite_item(wc, path);
+ ret = overwrite_item(wc);
if (ret)
goto out;
goto update_inode;
@@ -762,13 +765,15 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
btrfs_fs_incompat(fs_info, NO_HOLES))
goto update_inode;
- ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, sizeof(*item));
+ ret = btrfs_insert_empty_item(trans, root, wc->subvol_path,
+ &wc->log_key, sizeof(*item));
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
}
- dest_offset = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]);
- copy_extent_buffer(path->nodes[0], wc->log_leaf, dest_offset,
+ dest_offset = btrfs_item_ptr_offset(wc->subvol_path->nodes[0],
+ wc->subvol_path->slots[0]);
+ copy_extent_buffer(wc->subvol_path->nodes[0], wc->log_leaf, dest_offset,
(unsigned long)item, sizeof(*item));
/*
@@ -778,7 +783,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
* update the inode item.
*/
if (btrfs_file_extent_disk_bytenr(wc->log_leaf, item) == 0) {
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
goto update_inode;
}
@@ -833,7 +838,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path
}
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
if (btrfs_file_extent_compression(wc->log_leaf, item)) {
csum_start = ins.objectid;
@@ -967,7 +972,6 @@ static int unlink_inode_for_log_replay(struct walk_control *wc,
* item
*/
static noinline int drop_one_dir_item(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_inode *dir,
struct btrfs_dir_item *di)
{
@@ -975,12 +979,10 @@ static noinline int drop_one_dir_item(struct walk_control *wc,
struct btrfs_root *root = dir->root;
struct btrfs_inode *inode;
struct fscrypt_str name;
- struct extent_buffer *leaf;
+ struct extent_buffer *leaf = wc->subvol_path->nodes[0];
struct btrfs_key location;
int ret;
- leaf = path->nodes[0];
-
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) {
@@ -988,7 +990,7 @@ static noinline int drop_one_dir_item(struct walk_control *wc,
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
inode = btrfs_iget_logging(location.objectid, root);
if (IS_ERR(inode)) {
@@ -998,7 +1000,7 @@ static noinline int drop_one_dir_item(struct walk_control *wc,
goto out;
}
- ret = link_to_fixup_dir(wc, path, location.objectid);
+ ret = link_to_fixup_dir(wc, location.objectid);
if (ret)
goto out;
@@ -1097,13 +1099,12 @@ static noinline int backref_in_log(struct btrfs_root *log,
}
static int unlink_refs_not_in_log(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_key *search_key,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
u64 parent_objectid)
{
- struct extent_buffer *leaf = path->nodes[0];
+ struct extent_buffer *leaf = wc->subvol_path->nodes[0];
unsigned long ptr;
unsigned long ptr_end;
@@ -1112,8 +1113,8 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
* log. If so, we allow them to stay otherwise they must be unlinked as
* a conflict.
*/
- ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
- ptr_end = ptr + btrfs_item_size(leaf, path->slots[0]);
+ 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;
@@ -1141,7 +1142,7 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
}
inc_nlink(&inode->vfs_inode);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
ret = unlink_inode_for_log_replay(wc, dir, inode, &victim_name);
kfree(victim_name.name);
@@ -1154,15 +1155,14 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
}
static int unlink_extrefs_not_in_log(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_key *search_key,
struct btrfs_inode *inode,
u64 inode_objectid,
u64 parent_objectid)
{
- struct extent_buffer *leaf = path->nodes[0];
- const unsigned long base = btrfs_item_ptr_offset(leaf, path->slots[0]);
- const u32 item_size = btrfs_item_size(leaf, path->slots[0]);
+ struct extent_buffer *leaf = wc->subvol_path->nodes[0];
+ const unsigned long base = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]);
+ const u32 item_size = btrfs_item_size(leaf, wc->subvol_path->slots[0]);
u32 cur_offset = 0;
while (cur_offset < item_size) {
@@ -1213,7 +1213,7 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
}
inc_nlink(&inode->vfs_inode);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
ret = unlink_inode_for_log_replay(wc, victim_parent, inode,
&victim_name);
@@ -1228,7 +1228,6 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
}
static inline int __add_inode_ref(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
u64 inode_objectid, u64 parent_objectid,
@@ -1246,7 +1245,7 @@ static inline int __add_inode_ref(struct walk_control *wc,
search_key.objectid = inode_objectid;
search_key.type = BTRFS_INODE_REF_KEY;
search_key.offset = parent_objectid;
- ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
return ret;
@@ -1258,53 +1257,54 @@ static inline int __add_inode_ref(struct walk_control *wc,
if (search_key.objectid == search_key.offset)
return 1;
- ret = unlink_refs_not_in_log(wc, path, &search_key, dir, inode,
+ ret = unlink_refs_not_in_log(wc, &search_key, dir, inode,
parent_objectid);
if (ret == -EAGAIN)
goto again;
else if (ret)
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* Same search but for extended refs */
- extref = btrfs_lookup_inode_extref(root, path, name, inode_objectid, parent_objectid);
+ extref = btrfs_lookup_inode_extref(root, wc->subvol_path, name,
+ inode_objectid, parent_objectid);
if (IS_ERR(extref)) {
return PTR_ERR(extref);
} else if (extref) {
- ret = unlink_extrefs_not_in_log(wc, path, &search_key, inode,
+ ret = unlink_extrefs_not_in_log(wc, &search_key, inode,
inode_objectid, parent_objectid);
if (ret == -EAGAIN)
goto again;
else if (ret)
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* look for a conflicting sequence number */
- di = btrfs_lookup_dir_index_item(trans, root, path, btrfs_ino(dir),
+ di = btrfs_lookup_dir_index_item(trans, root, wc->subvol_path, btrfs_ino(dir),
ref_index, name, 0);
if (IS_ERR(di)) {
ret = PTR_ERR(di);
btrfs_abort_transaction(trans, ret);
return ret;
} else if (di) {
- ret = drop_one_dir_item(wc, path, dir, di);
+ ret = drop_one_dir_item(wc, dir, di);
if (ret)
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
/* look for a conflicting name */
- di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir), name, 0);
+ di = btrfs_lookup_dir_item(trans, root, wc->subvol_path, btrfs_ino(dir), name, 0);
if (IS_ERR(di)) {
return PTR_ERR(di);
} else if (di) {
- ret = drop_one_dir_item(wc, path, dir, di);
+ ret = drop_one_dir_item(wc, dir, di);
if (ret)
return ret;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return 0;
}
@@ -1357,9 +1357,7 @@ static int ref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
* proper unlink of that name (that is, remove its entry from the inode
* reference item and both dir index keys).
*/
-static int unlink_old_inode_refs(struct walk_control *wc,
- struct btrfs_path *path,
- struct btrfs_inode *inode)
+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;
@@ -1369,8 +1367,8 @@ static int unlink_old_inode_refs(struct walk_control *wc,
struct extent_buffer *eb;
again:
- btrfs_release_path(path);
- ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0);
+ btrfs_release_path(wc->subvol_path);
+ ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0);
if (ret > 0) {
ret = 0;
goto out;
@@ -1380,9 +1378,9 @@ static int unlink_old_inode_refs(struct walk_control *wc,
goto out;
}
- eb = path->nodes[0];
- ref_ptr = btrfs_item_ptr_offset(eb, path->slots[0]);
- ref_end = ref_ptr + btrfs_item_size(eb, path->slots[0]);
+ eb = wc->subvol_path->nodes[0];
+ ref_ptr = btrfs_item_ptr_offset(eb, wc->subvol_path->slots[0]);
+ ref_end = ref_ptr + btrfs_item_size(eb, wc->subvol_path->slots[0]);
while (ref_ptr < ref_end) {
struct fscrypt_str name;
u64 parent_id;
@@ -1413,7 +1411,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
if (!ret) {
struct btrfs_inode *dir;
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
dir = btrfs_iget_logging(parent_id, root);
if (IS_ERR(dir)) {
ret = PTR_ERR(dir);
@@ -1438,7 +1436,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
}
ret = 0;
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return ret;
}
@@ -1446,7 +1444,7 @@ static int unlink_old_inode_refs(struct walk_control *wc,
* Replay one inode back reference item found in the log tree.
* Path is for temporary use by this function (it should be released on return).
*/
-static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *path)
+static noinline int add_inode_ref(struct walk_control *wc)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1548,8 +1546,8 @@ static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *pa
}
}
- ret = inode_in_dir(root, path, btrfs_ino(dir), btrfs_ino(inode),
- ref_index, &name);
+ 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);
goto out;
@@ -1561,7 +1559,7 @@ static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *pa
* overwrite any existing back reference, and we don't
* want to create dangling pointers in the directory.
*/
- ret = __add_inode_ref(wc, path, dir, inode, inode_objectid,
+ ret = __add_inode_ref(wc, dir, inode, inode_objectid,
parent_objectid, ref_index, &name);
if (ret) {
if (ret == 1)
@@ -1602,14 +1600,14 @@ static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *pa
* dir index entries exist for a name but there is no inode reference
* item with the same name.
*/
- ret = unlink_old_inode_refs(wc, path, inode);
+ ret = unlink_old_inode_refs(wc, inode);
if (ret)
goto out;
/* finally write the back reference in the inode */
- ret = overwrite_item(wc, path);
+ ret = overwrite_item(wc);
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
kfree(name.name);
if (dir)
iput(&dir->vfs_inode);
@@ -1728,7 +1726,6 @@ static int count_inode_refs(struct btrfs_inode *inode, struct btrfs_path *path)
* will free the inode.
*/
static noinline int fixup_inode_link_count(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_inode *inode)
{
struct btrfs_trans_handle *trans = wc->trans;
@@ -1737,13 +1734,13 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,
u64 nlink = 0;
const u64 ino = btrfs_ino(inode);
- ret = count_inode_refs(inode, path);
+ ret = count_inode_refs(inode, wc->subvol_path);
if (ret < 0)
goto out;
nlink = ret;
- ret = count_inode_extrefs(inode, path);
+ ret = count_inode_extrefs(inode, wc->subvol_path);
if (ret < 0)
goto out;
@@ -1762,7 +1759,7 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,
if (inode->vfs_inode.i_nlink == 0) {
if (S_ISDIR(inode->vfs_inode.i_mode)) {
- ret = replay_dir_deletes(wc, path, ino, true);
+ ret = replay_dir_deletes(wc, ino, true);
if (ret)
goto out;
}
@@ -1772,12 +1769,11 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,
}
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return ret;
}
-static noinline int fixup_inode_link_counts(struct walk_control *wc,
- struct btrfs_path *path)
+static noinline int fixup_inode_link_counts(struct walk_control *wc)
{
int ret;
struct btrfs_key key;
@@ -1790,34 +1786,34 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
struct btrfs_root *root = wc->root;
struct btrfs_inode *inode;
- ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ ret = btrfs_search_slot(trans, root, &key, wc->subvol_path, -1, 1);
if (ret < 0)
break;
if (ret == 1) {
ret = 0;
- if (path->slots[0] == 0)
+ if (wc->subvol_path->slots[0] == 0)
break;
- path->slots[0]--;
+ wc->subvol_path->slots[0]--;
}
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &key, wc->subvol_path->slots[0]);
if (key.objectid != BTRFS_TREE_LOG_FIXUP_OBJECTID ||
key.type != BTRFS_ORPHAN_ITEM_KEY)
break;
- ret = btrfs_del_item(trans, root, path);
+ ret = btrfs_del_item(trans, root, wc->subvol_path);
if (ret)
break;
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
inode = btrfs_iget_logging(key.offset, root);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
break;
}
- ret = fixup_inode_link_count(wc, path, inode);
+ ret = fixup_inode_link_count(wc, inode);
iput(&inode->vfs_inode);
if (ret)
break;
@@ -1829,7 +1825,7 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
*/
key.offset = (u64)-1;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return ret;
}
@@ -1839,9 +1835,7 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,
* count when replay is done. The link count is incremented here
* so the inode won't go away until we check it
*/
-static noinline int link_to_fixup_dir(struct walk_control *wc,
- struct btrfs_path *path,
- u64 objectid)
+static noinline int link_to_fixup_dir(struct walk_control *wc, u64 objectid)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1862,9 +1856,9 @@ static noinline int link_to_fixup_dir(struct walk_control *wc,
key.type = BTRFS_ORPHAN_ITEM_KEY;
key.offset = objectid;
- ret = btrfs_insert_empty_item(trans, root, path, &key, 0);
+ ret = btrfs_insert_empty_item(trans, root, wc->subvol_path, &key, 0);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
if (ret == 0) {
if (!vfs_inode->i_nlink)
set_nlink(vfs_inode, 1);
@@ -1917,7 +1911,6 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans,
static int delete_conflicting_dir_entry(struct walk_control *wc,
struct btrfs_inode *dir,
- struct btrfs_path *path,
struct btrfs_dir_item *dst_di,
const struct btrfs_key *log_key,
u8 log_flags,
@@ -1925,12 +1918,12 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,
{
struct btrfs_key found_key;
- btrfs_dir_item_key_to_cpu(path->nodes[0], dst_di, &found_key);
+ btrfs_dir_item_key_to_cpu(wc->subvol_path->nodes[0], dst_di, &found_key);
/* The existing dentry points to the same inode, don't delete it. */
if (found_key.objectid == log_key->objectid &&
found_key.type == log_key->type &&
found_key.offset == log_key->offset &&
- btrfs_dir_flags(path->nodes[0], dst_di) == log_flags)
+ btrfs_dir_flags(wc->subvol_path->nodes[0], dst_di) == log_flags)
return 1;
/*
@@ -1940,7 +1933,7 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,
if (!exists)
return 0;
- return drop_one_dir_item(wc, path, dir, dst_di);
+ return drop_one_dir_item(wc, dir, dst_di);
}
/*
@@ -1959,9 +1952,7 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,
* Returns < 0 on error, 0 if the name wasn't replayed (dentry points to a
* non-existing inode) and 1 if the name was replayed.
*/
-static noinline int replay_one_name(struct walk_control *wc,
- struct btrfs_path *path,
- struct btrfs_dir_item *di)
+static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_item *di)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -1995,8 +1986,8 @@ static noinline int replay_one_name(struct walk_control *wc,
log_flags = btrfs_dir_flags(wc->log_leaf, di);
btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &log_key);
- ret = btrfs_lookup_inode(trans, root, path, &log_key, 0);
- btrfs_release_path(path);
+ 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);
goto out;
@@ -2004,14 +1995,14 @@ static noinline int replay_one_name(struct walk_control *wc,
exists = (ret == 0);
ret = 0;
- dir_dst_di = btrfs_lookup_dir_item(trans, root, path, wc->log_key.objectid,
- &name, 1);
+ dir_dst_di = btrfs_lookup_dir_item(trans, root, wc->subvol_path,
+ wc->log_key.objectid, &name, 1);
if (IS_ERR(dir_dst_di)) {
ret = PTR_ERR(dir_dst_di);
btrfs_abort_transaction(trans, ret);
goto out;
} else if (dir_dst_di) {
- ret = delete_conflicting_dir_entry(wc, dir, path, 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);
@@ -2020,9 +2011,9 @@ static noinline int replay_one_name(struct walk_control *wc,
dir_dst_matches = (ret == 1);
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
- index_dst_di = btrfs_lookup_dir_index_item(trans, root, path,
+ index_dst_di = btrfs_lookup_dir_index_item(trans, root, wc->subvol_path,
wc->log_key.objectid,
wc->log_key.offset, &name, 1);
if (IS_ERR(index_dst_di)) {
@@ -2030,7 +2021,7 @@ static noinline int replay_one_name(struct walk_control *wc,
btrfs_abort_transaction(trans, ret);
goto out;
} else if (index_dst_di) {
- ret = delete_conflicting_dir_entry(wc, dir, path, 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);
@@ -2039,7 +2030,7 @@ static noinline int replay_one_name(struct walk_control *wc,
index_dst_matches = (ret == 1);
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
if (dir_dst_matches && index_dst_matches) {
ret = 0;
@@ -2104,8 +2095,7 @@ static noinline int replay_one_name(struct walk_control *wc,
}
/* Replay one dir item from a BTRFS_DIR_INDEX_KEY key. */
-static noinline int replay_one_dir_item(struct walk_control *wc,
- struct btrfs_path *path)
+static noinline int replay_one_dir_item(struct walk_control *wc)
{
int ret;
struct btrfs_dir_item *di;
@@ -2114,7 +2104,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
ASSERT(wc->log_key.type == BTRFS_DIR_INDEX_KEY);
di = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_dir_item);
- ret = replay_one_name(wc, path, di);
+ ret = replay_one_name(wc, di);
if (ret < 0)
return ret;
@@ -2148,7 +2138,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,
struct btrfs_key di_key;
btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &di_key);
- ret = link_to_fixup_dir(wc, path, di_key.objectid);
+ ret = link_to_fixup_dir(wc, di_key.objectid);
}
return ret;
@@ -2242,7 +2232,6 @@ static noinline int find_dir_range(struct btrfs_root *root,
* to is unlinked
*/
static noinline int check_item_in_log(struct walk_control *wc,
- struct btrfs_path *path,
struct btrfs_path *log_path,
struct btrfs_inode *dir,
struct btrfs_key *dir_key,
@@ -2266,8 +2255,8 @@ static noinline int check_item_in_log(struct walk_control *wc,
*/
ASSERT(dir_key->type == BTRFS_DIR_INDEX_KEY);
- eb = path->nodes[0];
- slot = path->slots[0];
+ eb = wc->subvol_path->nodes[0];
+ slot = wc->subvol_path->slots[0];
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) {
@@ -2293,7 +2282,7 @@ static noinline int check_item_in_log(struct walk_control *wc,
}
btrfs_dir_item_key_to_cpu(eb, di, &location);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
btrfs_release_path(log_path);
inode = btrfs_iget_logging(location.objectid, root);
if (IS_ERR(inode)) {
@@ -2303,7 +2292,7 @@ static noinline int check_item_in_log(struct walk_control *wc,
goto out;
}
- ret = link_to_fixup_dir(wc, path, location.objectid);
+ ret = link_to_fixup_dir(wc, location.objectid);
if (ret)
goto out;
@@ -2315,7 +2304,7 @@ static noinline int check_item_in_log(struct walk_control *wc,
* (an index number), so we're done.
*/
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
btrfs_release_path(log_path);
kfree(name.name);
if (inode)
@@ -2323,7 +2312,7 @@ static noinline int check_item_in_log(struct walk_control *wc,
return ret;
}
-static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path)
+static int replay_xattr_deletes(struct walk_control *wc)
{
struct btrfs_trans_handle *trans = wc->trans;
struct btrfs_root *root = wc->root;
@@ -2331,7 +2320,6 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
struct btrfs_key search_key;
struct btrfs_path *log_path;
const u64 ino = wc->log_key.objectid;
- int i;
int nritems;
int ret;
@@ -2345,32 +2333,32 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
search_key.type = BTRFS_XATTR_ITEM_KEY;
search_key.offset = 0;
again:
- ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
goto out;
}
process_leaf:
- nritems = btrfs_header_nritems(path->nodes[0]);
- for (i = path->slots[0]; i < nritems; i++) {
+ nritems = btrfs_header_nritems(wc->subvol_path->nodes[0]);
+ for (int i = wc->subvol_path->slots[0]; i < nritems; i++) {
struct btrfs_key key;
struct btrfs_dir_item *di;
struct btrfs_dir_item *log_di;
u32 total_size;
u32 cur;
- btrfs_item_key_to_cpu(path->nodes[0], &key, i);
+ btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &key, i);
if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY) {
ret = 0;
goto out;
}
- di = btrfs_item_ptr(path->nodes[0], i, struct btrfs_dir_item);
- total_size = btrfs_item_size(path->nodes[0], i);
+ di = btrfs_item_ptr(wc->subvol_path->nodes[0], i, struct btrfs_dir_item);
+ total_size = btrfs_item_size(wc->subvol_path->nodes[0], i);
cur = 0;
while (cur < total_size) {
- u16 name_len = btrfs_dir_name_len(path->nodes[0], di);
- u16 data_len = btrfs_dir_data_len(path->nodes[0], di);
+ u16 name_len = btrfs_dir_name_len(wc->subvol_path->nodes[0], di);
+ u16 data_len = btrfs_dir_data_len(wc->subvol_path->nodes[0], di);
u32 this_len = sizeof(*di) + name_len + data_len;
char *name;
@@ -2380,7 +2368,7 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
btrfs_abort_transaction(trans, ret);
goto out;
}
- read_extent_buffer(path->nodes[0], name,
+ read_extent_buffer(wc->subvol_path->nodes[0], name,
(unsigned long)(di + 1), name_len);
log_di = btrfs_lookup_xattr(NULL, log, log_path, ino,
@@ -2388,8 +2376,8 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
btrfs_release_path(log_path);
if (!log_di) {
/* Doesn't exist in log tree, so delete it. */
- btrfs_release_path(path);
- di = btrfs_lookup_xattr(trans, root, path, ino,
+ 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)) {
@@ -2399,12 +2387,12 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
}
ASSERT(di);
ret = btrfs_delete_one_dir_name(trans, root,
- path, di);
+ wc->subvol_path, di);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
search_key = key;
goto again;
}
@@ -2418,7 +2406,7 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
di = (struct btrfs_dir_item *)((char *)di + this_len);
}
}
- ret = btrfs_next_leaf(root, path);
+ ret = btrfs_next_leaf(root, wc->subvol_path);
if (ret > 0)
ret = 0;
else if (ret == 0)
@@ -2427,7 +2415,7 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
btrfs_abort_transaction(trans, ret);
out:
btrfs_free_path(log_path);
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
return ret;
}
@@ -2443,7 +2431,6 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path
* directory.
*/
static noinline int replay_dir_deletes(struct walk_control *wc,
- struct btrfs_path *path,
u64 dirid, bool del_all)
{
struct btrfs_trans_handle *trans = wc->trans;
@@ -2486,7 +2473,7 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
if (del_all)
range_end = (u64)-1;
else {
- ret = find_dir_range(log, path, dirid,
+ ret = find_dir_range(log, wc->subvol_path, dirid,
&range_start, &range_end);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
@@ -2499,16 +2486,16 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
dir_key.offset = range_start;
while (1) {
int nritems;
- ret = btrfs_search_slot(NULL, root, &dir_key, path,
- 0, 0);
+ ret = btrfs_search_slot(NULL, root, &dir_key,
+ wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_transaction(trans, ret);
goto out;
}
- nritems = btrfs_header_nritems(path->nodes[0]);
- if (path->slots[0] >= nritems) {
- ret = btrfs_next_leaf(root, path);
+ nritems = btrfs_header_nritems(wc->subvol_path->nodes[0]);
+ if (wc->subvol_path->slots[0] >= nritems) {
+ ret = btrfs_next_leaf(root, wc->subvol_path);
if (ret == 1) {
break;
} else if (ret < 0) {
@@ -2516,8 +2503,8 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
goto out;
}
}
- btrfs_item_key_to_cpu(path->nodes[0], &found_key,
- path->slots[0]);
+ btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &found_key,
+ wc->subvol_path->slots[0]);
if (found_key.objectid != dirid ||
found_key.type != dir_key.type) {
ret = 0;
@@ -2527,22 +2514,21 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
if (found_key.offset > range_end)
break;
- ret = check_item_in_log(wc, path, log_path, dir,
- &found_key, del_all);
+ ret = check_item_in_log(wc, log_path, dir, &found_key, del_all);
if (ret)
goto out;
if (found_key.offset == (u64)-1)
break;
dir_key.offset = found_key.offset + 1;
}
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
if (range_end == (u64)-1)
break;
range_start = range_end + 1;
}
ret = 0;
out:
- btrfs_release_path(path);
+ btrfs_release_path(wc->subvol_path);
btrfs_free_path(log_path);
iput(&dir->vfs_inode);
return ret;
@@ -2567,7 +2553,6 @@ static int replay_one_buffer(struct extent_buffer *eb,
.transid = gen,
.level = level
};
- struct btrfs_path *path;
struct btrfs_root *root = wc->root;
struct btrfs_trans_handle *trans = wc->trans;
int ret;
@@ -2581,8 +2566,9 @@ static int replay_one_buffer(struct extent_buffer *eb,
return ret;
}
- path = btrfs_alloc_path();
- if (!path) {
+ ASSERT(wc->subvol_path == NULL);
+ wc->subvol_path = btrfs_alloc_path();
+ if (!wc->subvol_path) {
btrfs_abort_transaction(trans, -ENOMEM);
return -ENOMEM;
}
@@ -2630,16 +2616,16 @@ static int replay_one_buffer(struct extent_buffer *eb,
wc->stage == LOG_WALK_REPLAY_INODES) {
u32 mode;
- ret = replay_xattr_deletes(wc, path);
+ ret = replay_xattr_deletes(wc);
if (ret)
break;
mode = btrfs_inode_mode(eb, inode_item);
if (S_ISDIR(mode)) {
- ret = replay_dir_deletes(wc, path, wc->log_key.objectid, false);
+ ret = replay_dir_deletes(wc, wc->log_key.objectid, false);
if (ret)
break;
}
- ret = overwrite_item(wc, path);
+ ret = overwrite_item(wc);
if (ret)
break;
@@ -2667,7 +2653,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
drop_args.start = from;
drop_args.end = (u64)-1;
drop_args.drop_cache = true;
- drop_args.path = path;
+ drop_args.path = wc->subvol_path;
ret = btrfs_drop_extents(trans, root, inode, &drop_args);
if (ret) {
btrfs_abort_transaction(trans, ret);
@@ -2684,7 +2670,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
break;
}
- ret = link_to_fixup_dir(wc, path, wc->log_key.objectid);
+ ret = link_to_fixup_dir(wc, wc->log_key.objectid);
if (ret)
break;
}
@@ -2694,7 +2680,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
if (wc->log_key.type == BTRFS_DIR_INDEX_KEY &&
wc->stage == LOG_WALK_REPLAY_DIR_INDEX) {
- ret = replay_one_dir_item(wc, path);
+ ret = replay_one_dir_item(wc);
if (ret)
break;
}
@@ -2704,16 +2690,16 @@ static int replay_one_buffer(struct extent_buffer *eb,
/* these keys are simply copied */
if (wc->log_key.type == BTRFS_XATTR_ITEM_KEY) {
- ret = overwrite_item(wc, path);
+ ret = overwrite_item(wc);
if (ret)
break;
} else if (wc->log_key.type == BTRFS_INODE_REF_KEY ||
wc->log_key.type == BTRFS_INODE_EXTREF_KEY) {
- ret = add_inode_ref(wc, path);
+ ret = add_inode_ref(wc);
if (ret)
break;
} else if (wc->log_key.type == BTRFS_EXTENT_DATA_KEY) {
- ret = replay_one_extent(wc, path);
+ ret = replay_one_extent(wc);
if (ret)
break;
}
@@ -2724,7 +2710,8 @@ static int replay_one_buffer(struct extent_buffer *eb,
* older kernel with such keys, ignore them.
*/
}
- btrfs_free_path(path);
+ btrfs_free_path(wc->subvol_path);
+ wc->subvol_path = NULL;
return ret;
}
@@ -7515,7 +7502,9 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
if (wc.stage == LOG_WALK_REPLAY_ALL) {
struct btrfs_root *root = wc.root;
- ret = fixup_inode_link_counts(&wc, path);
+ wc.subvol_path = path;
+ ret = fixup_inode_link_counts(&wc);
+ wc.subvol_path = NULL;
if (ret) {
btrfs_abort_transaction(trans, ret);
goto next;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 29/33] btrfs: stop passing inode object IDs to __add_inode_ref() in log replay
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (27 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 28/33] btrfs: add path for subvolume tree changes to struct walk_control fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 30/33] btrfs: remove pointless inode lookup when processing extrefs during " fdmanana
` (4 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
There's no point in passing the inode and parent inode object IDs to
__add_inode_ref() and its helpers because we can get them by calling
btrfs_ino() against the inode and the directory inode, and we pass both
inodes to __add_inode_ref() and its helpers. So remove the object IDs
parameters to reduce arguments passed and to make things less confusing.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 36 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 21 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 747daaaaf332..6186300923b7 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1101,8 +1101,7 @@ static noinline int backref_in_log(struct btrfs_root *log,
static int unlink_refs_not_in_log(struct walk_control *wc,
struct btrfs_key *search_key,
struct btrfs_inode *dir,
- struct btrfs_inode *inode,
- u64 parent_objectid)
+ struct btrfs_inode *inode)
{
struct extent_buffer *leaf = wc->subvol_path->nodes[0];
unsigned long ptr;
@@ -1130,7 +1129,7 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
return ret;
}
- ret = backref_in_log(wc->log, search_key, parent_objectid, &victim_name);
+ ret = backref_in_log(wc->log, search_key, btrfs_ino(dir), &victim_name);
if (ret) {
kfree(victim_name.name);
if (ret < 0) {
@@ -1156,9 +1155,8 @@ static int unlink_refs_not_in_log(struct walk_control *wc,
static int unlink_extrefs_not_in_log(struct walk_control *wc,
struct btrfs_key *search_key,
- struct btrfs_inode *inode,
- u64 inode_objectid,
- u64 parent_objectid)
+ struct btrfs_inode *dir,
+ struct btrfs_inode *inode)
{
struct extent_buffer *leaf = wc->subvol_path->nodes[0];
const unsigned long base = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]);
@@ -1177,7 +1175,7 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
extref = (struct btrfs_inode_extref *)(base + cur_offset);
victim_name.len = btrfs_inode_extref_name_len(leaf, extref);
- if (btrfs_inode_extref_parent(leaf, extref) != parent_objectid)
+ if (btrfs_inode_extref_parent(leaf, extref) != btrfs_ino(dir))
goto next;
ret = read_alloc_one_name(leaf, &extref->name, victim_name.len,
@@ -1187,12 +1185,12 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
return ret;
}
- search_key->objectid = inode_objectid;
+ search_key->objectid = btrfs_ino(inode);
search_key->type = BTRFS_INODE_EXTREF_KEY;
- search_key->offset = btrfs_extref_hash(parent_objectid,
+ search_key->offset = btrfs_extref_hash(btrfs_ino(dir),
victim_name.name,
victim_name.len);
- ret = backref_in_log(log_root, search_key, parent_objectid, &victim_name);
+ ret = backref_in_log(log_root, search_key, btrfs_ino(dir), &victim_name);
if (ret) {
kfree(victim_name.name);
if (ret < 0) {
@@ -1204,7 +1202,7 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
continue;
}
- victim_parent = btrfs_iget_logging(parent_objectid, root);
+ victim_parent = btrfs_iget_logging(btrfs_ino(dir), root);
if (IS_ERR(victim_parent)) {
kfree(victim_name.name);
ret = PTR_ERR(victim_parent);
@@ -1230,7 +1228,6 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
static inline int __add_inode_ref(struct walk_control *wc,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
- u64 inode_objectid, u64 parent_objectid,
u64 ref_index, struct fscrypt_str *name)
{
int ret;
@@ -1242,9 +1239,9 @@ static inline int __add_inode_ref(struct walk_control *wc,
again:
/* Search old style refs */
- search_key.objectid = inode_objectid;
+ search_key.objectid = btrfs_ino(inode);
search_key.type = BTRFS_INODE_REF_KEY;
- search_key.offset = parent_objectid;
+ 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);
@@ -1257,8 +1254,7 @@ static inline int __add_inode_ref(struct walk_control *wc,
if (search_key.objectid == search_key.offset)
return 1;
- ret = unlink_refs_not_in_log(wc, &search_key, dir, inode,
- parent_objectid);
+ ret = unlink_refs_not_in_log(wc, &search_key, dir, inode);
if (ret == -EAGAIN)
goto again;
else if (ret)
@@ -1268,12 +1264,11 @@ static inline int __add_inode_ref(struct walk_control *wc,
/* Same search but for extended refs */
extref = btrfs_lookup_inode_extref(root, wc->subvol_path, name,
- inode_objectid, parent_objectid);
+ btrfs_ino(inode), btrfs_ino(dir));
if (IS_ERR(extref)) {
return PTR_ERR(extref);
} else if (extref) {
- ret = unlink_extrefs_not_in_log(wc, &search_key, inode,
- inode_objectid, parent_objectid);
+ ret = unlink_extrefs_not_in_log(wc, &search_key, dir, inode);
if (ret == -EAGAIN)
goto again;
else if (ret)
@@ -1559,8 +1554,7 @@ static noinline int add_inode_ref(struct walk_control *wc)
* overwrite any existing back reference, and we don't
* want to create dangling pointers in the directory.
*/
- ret = __add_inode_ref(wc, dir, inode, inode_objectid,
- parent_objectid, ref_index, &name);
+ ret = __add_inode_ref(wc, dir, inode, ref_index, &name);
if (ret) {
if (ret == 1)
ret = 0;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 30/33] btrfs: remove pointless inode lookup when processing extrefs during log replay
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (28 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 29/33] btrfs: stop passing inode object IDs to __add_inode_ref() in log replay fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 31/33] btrfs: abort transaction if we fail to find dir item " fdmanana
` (3 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
At unlink_extrefs_not_in_log() we do an inode lookup of the directory but
we already have the directory inode accessible as a function argument, so
the lookup is redudant. Remove it and use the directory inode passed in as
an argument.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 6186300923b7..86c595ef57f4 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1165,10 +1165,8 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
while (cur_offset < item_size) {
struct btrfs_trans_handle *trans = wc->trans;
- struct btrfs_root *root = wc->root;
struct btrfs_root *log_root = wc->log;
struct btrfs_inode_extref *extref;
- struct btrfs_inode *victim_parent;
struct fscrypt_str victim_name;
int ret;
@@ -1202,20 +1200,10 @@ static int unlink_extrefs_not_in_log(struct walk_control *wc,
continue;
}
- victim_parent = btrfs_iget_logging(btrfs_ino(dir), root);
- if (IS_ERR(victim_parent)) {
- kfree(victim_name.name);
- ret = PTR_ERR(victim_parent);
- btrfs_abort_transaction(trans, ret);
- return ret;
- }
-
inc_nlink(&inode->vfs_inode);
btrfs_release_path(wc->subvol_path);
- ret = unlink_inode_for_log_replay(wc, victim_parent, inode,
- &victim_name);
- iput(&victim_parent->vfs_inode);
+ ret = unlink_inode_for_log_replay(wc, dir, inode, &victim_name);
kfree(victim_name.name);
if (ret)
return ret;
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 31/33] btrfs: abort transaction if we fail to find dir item during log replay
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (29 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 30/33] btrfs: remove pointless inode lookup when processing extrefs during " fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 32/33] btrfs: abort transaction if we fail to update inode in log replay dir fixup fdmanana
` (2 subsequent siblings)
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
At __add_inode_ref() if we get an error when trying to lookup a dir item
we don't abort the transaction and propagate the error up the call chain,
so that somewhere else up in the call chain the transaction is aborted.
This however makes it hard to know that the failure comes from looking up
a dir item, so add a transaction abort in case we fail there, so that we
immediately pinpoint where the problem comes from during log replay.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 86c595ef57f4..7b91248b38dc 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1281,7 +1281,9 @@ static inline int __add_inode_ref(struct walk_control *wc,
/* look for a conflicting name */
di = btrfs_lookup_dir_item(trans, root, wc->subvol_path, btrfs_ino(dir), name, 0);
if (IS_ERR(di)) {
- return PTR_ERR(di);
+ ret = PTR_ERR(di);
+ btrfs_abort_transaction(trans, ret);
+ return ret;
} else if (di) {
ret = drop_one_dir_item(wc, dir, di);
if (ret)
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 32/33] btrfs: abort transaction if we fail to update inode in log replay dir fixup
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (30 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 31/33] btrfs: abort transaction if we fail to find dir item " fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-08 9:53 ` [PATCH v2 33/33] btrfs: dump detailed info and specific messages on log replay failures fdmanana
2025-09-09 0:55 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes David Sterba
33 siblings, 0 replies; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
If we fail to update the inode at link_to_fixup_dir(), we don't abort the
transaction and propagate the error up the call chain, which makes it hard
to pinpoint the error to the inode update. So abort the transaction if the
inode update call fails, so that if it happens we known immediately.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/tree-log.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 7b91248b38dc..83b79023baae 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1849,6 +1849,8 @@ static noinline int link_to_fixup_dir(struct walk_control *wc, u64 objectid)
else
inc_nlink(vfs_inode);
ret = btrfs_update_inode(trans, inode);
+ if (ret)
+ btrfs_abort_transaction(trans, ret);
} else if (ret == -EEXIST) {
ret = 0;
} else {
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 33/33] btrfs: dump detailed info and specific messages on log replay failures
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (31 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 32/33] btrfs: abort transaction if we fail to update inode in log replay dir fixup fdmanana
@ 2025-09-08 9:53 ` fdmanana
2025-09-09 0:53 ` David Sterba
2025-09-09 0:55 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes David Sterba
33 siblings, 1 reply; 72+ messages in thread
From: fdmanana @ 2025-09-08 9:53 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
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>
---
fs/btrfs/fs.h | 2 +
fs/btrfs/tree-log.c | 432 +++++++++++++++++++++++++++++++++++---------
2 files changed, 348 insertions(+), 86 deletions(-)
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 5f0b185a7f21..28bb52bc33f1 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/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 @@ static int overwrite_item(struct walk_control *wc)
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 @@ static inline int __add_inode_ref(struct walk_control *wc,
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 @@ static inline int __add_inode_ref(struct walk_control *wc,
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 @@ static inline int __add_inode_ref(struct walk_control *wc,
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 @@ static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *in
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 @@ static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *in
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 @@ static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *in
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 @@ static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_it
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 @@ static int replay_xattr_deletes(struct walk_control *wc)
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 @@ static int replay_xattr_deletes(struct walk_control *wc)
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 @@ static int replay_xattr_deletes(struct walk_control *wc)
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 @@ static int replay_xattr_deletes(struct walk_control *wc)
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)
--
2.47.2
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [PATCH v2 33/33] btrfs: dump detailed info and specific messages on log replay failures
2025-09-08 9:53 ` [PATCH v2 33/33] btrfs: dump detailed info and specific messages on log replay failures fdmanana
@ 2025-09-09 0:53 ` David Sterba
2025-09-09 14:42 ` Filipe Manana
0 siblings, 1 reply; 72+ messages in thread
From: David Sterba @ 2025-09-09 0:53 UTC (permalink / raw)
To: fdmanana; +Cc: linux-btrfs
On Mon, Sep 08, 2025 at 10:53:27AM +0100, fdmanana@kernel.org wrote:
> From: Filipe Manana <fdmanana@suse.com>
>
> 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>
> ---
> fs/btrfs/fs.h | 2 +
> fs/btrfs/tree-log.c | 432 +++++++++++++++++++++++++++++++++++---------
> 2 files changed, 348 insertions(+), 86 deletions(-)
>
> diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
> index 5f0b185a7f21..28bb52bc33f1 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,
As the series improves error handling and debugging output, it may be
also useful to add the state BTRFS_FS_STATE_LOG_REPLAY_ABORTED to the
table messages.c:fs_state_chars. The log cleanup error is there (L).
When the log replay is aborted the normal transaction abort won't set
the bit so the reason wouldn't be visible in the message headers.
(This is on top of the patchset, no need to update it or reset.)
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
` (32 preceding siblings ...)
2025-09-08 9:53 ` [PATCH v2 33/33] btrfs: dump detailed info and specific messages on log replay failures fdmanana
@ 2025-09-09 0:55 ` David Sterba
33 siblings, 0 replies; 72+ messages in thread
From: David Sterba @ 2025-09-09 0:55 UTC (permalink / raw)
To: fdmanana; +Cc: linux-btrfs
On Mon, Sep 08, 2025 at 10:52:54AM +0100, fdmanana@kernel.org wrote:
> From: Filipe Manana <fdmanana@suse.com>
>
> The following are a bug fix for an extref key offset computation, several
> cleanups to eliminate duplicated or not needed code, memory allocations
> and preparation work for the final change which is to make log replay
> failures dump contextual information useful to help debug failures during
> log replay. Details in the change logs.
>
> V2: Update subject of patch 18/33 which was a duplicate of another one
> likely due to copy paste. Update patch 33/33 to avoid use-after-free
> on a name if we had an error during replay of xattr deletes.
>
> Filipe Manana (33):
> btrfs: fix invalid extref key setup when replaying dentry
> btrfs: use booleans in walk control structure for log replay
> btrfs: rename replay_dest member of struct walk_control to root
> btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree()
> btrfs: add and use a log root field to struct walk_control
> btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees()
> btrfs: stop passing transaction parameter to log tree walk functions
> btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees()
> btrfs: always drop log root tree reference in btrfs_replay_log()
> btrfs: pass walk_control structure to replay_xattr_deletes()
> btrfs: move up the definition of struct walk_control
> btrfs: pass walk_control structure to replay_dir_deletes()
> btrfs: pass walk_control structure to check_item_in_log()
> btrfs: pass walk_control structure to replay_one_extent()
> btrfs: pass walk_control structure to add_inode_ref() and helpers
> btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name()
> btrfs: pass walk_control structure to drop_one_dir_item() and helpers
> btrfs: pass walk_control structure to overwrite_item()
> btrfs: use level argument in log tree walk callback process_one_buffer()
> btrfs: use level argument in log tree walk callback replay_one_buffer()
> btrfs: use the inode item boolean everywhere in overwrite_item()
> btrfs: add current log leaf, key and slot to struct walk_control
> btrfs: avoid unnecessary path allocation at fixup_inode_link_count()
> btrfs: avoid path allocations when dropping extents during log replay
> btrfs: avoid unnecessary path allocation when replaying a dir item
> btrfs: remove redundant path release when processing dentry during log replay
> btrfs: remove redundant path release when overwriting item during log replay
> btrfs: add path for subvolume tree changes to struct walk_control
> btrfs: stop passing inode object IDs to __add_inode_ref() in log replay
> btrfs: remove pointless inode lookup when processing extrefs during log replay
> btrfs: abort transaction if we fail to find dir item during log replay
> btrfs: abort transaction if we fail to update inode in log replay dir fixup
> btrfs: dump detailed info and specific messages on log replay failures
Nice, thanks.
Reviewed-by: David Sterba <dsterba@suse.com>
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v2 33/33] btrfs: dump detailed info and specific messages on log replay failures
2025-09-09 0:53 ` David Sterba
@ 2025-09-09 14:42 ` Filipe Manana
0 siblings, 0 replies; 72+ messages in thread
From: Filipe Manana @ 2025-09-09 14:42 UTC (permalink / raw)
To: dsterba; +Cc: linux-btrfs
On Tue, Sep 9, 2025 at 1:53 AM David Sterba <dsterba@suse.cz> wrote:
>
> On Mon, Sep 08, 2025 at 10:53:27AM +0100, fdmanana@kernel.org wrote:
> > From: Filipe Manana <fdmanana@suse.com>
> >
> > Currently debugging log replay f
> > 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>
> > ---
> > fs/btrfs/fs.h | 2 +
> > fs/btrfs/tree-log.c | 432 +++++++++++++++++++++++++++++++++++---------
> > 2 files changed, 348 insertions(+), 86 deletions(-)
> >
> > diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
> > index 5f0b185a7f21..28bb52bc33f1 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,
>
> As the series improves error handling and debugging output, it may be
> also useful to add the state BTRFS_FS_STATE_LOG_REPLAY_ABORTED to the
> table messages.c:fs_state_chars. The log cleanup error is there (L).
>
> When the log replay is aborted the normal transaction abort won't set
> the bit so the reason wouldn't be visible in the message headers.
>
> (This is on top of the patchset, no need to update it or reset.)
I've updated messages.c to add:
[BTRFS_FS_STATE_LOG_REPLAY_ABORTED] = 'O',
And pushed to for-next, thanks.
^ permalink raw reply [flat|nested] 72+ messages in thread
end of thread, other threads:[~2025-09-09 14:43 UTC | newest]
Thread overview: 72+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-05 16:09 [PATCH 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
2025-09-05 16:09 ` [PATCH 01/33] btrfs: fix invalid extref key setup when replaying dentry fdmanana
2025-09-05 16:09 ` [PATCH 02/33] btrfs: use booleans in walk control structure for log replay fdmanana
2025-09-05 16:09 ` [PATCH 03/33] btrfs: rename replay_dest member of struct walk_control to root fdmanana
2025-09-05 16:09 ` [PATCH 04/33] btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree() fdmanana
2025-09-05 16:09 ` [PATCH 05/33] btrfs: add and use a log root field to struct walk_control fdmanana
2025-09-05 16:09 ` [PATCH 06/33] btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees() fdmanana
2025-09-05 16:09 ` [PATCH 07/33] btrfs: stop passing transaction parameter to log tree walk functions fdmanana
2025-09-05 16:09 ` [PATCH 08/33] btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees() fdmanana
2025-09-05 16:09 ` [PATCH 09/33] btrfs: always drop log root tree reference in btrfs_replay_log() fdmanana
2025-09-05 16:09 ` [PATCH 10/33] btrfs: pass walk_control structure to replay_xattr_deletes() fdmanana
2025-09-05 16:09 ` [PATCH 11/33] btrfs: move up the definition of struct walk_control fdmanana
2025-09-05 16:10 ` [PATCH 12/33] btrfs: pass walk_control structure to replay_dir_deletes() fdmanana
2025-09-05 16:10 ` [PATCH 13/33] btrfs: pass walk_control structure to check_item_in_log() fdmanana
2025-09-05 16:10 ` [PATCH 14/33] btrfs: pass walk_control structure to replay_one_extent() fdmanana
2025-09-05 16:10 ` [PATCH 15/33] btrfs: pass walk_control structure to add_inode_ref() and helpers fdmanana
2025-09-05 16:10 ` [PATCH 16/33] btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name() fdmanana
2025-09-05 16:10 ` [PATCH 17/33] btrfs: pass walk_control structure to drop_one_dir_item() and helpers fdmanana
2025-09-05 16:10 ` [PATCH 18/33] btrfs: pass walk_control structure to replay_one_extent() fdmanana
2025-09-05 16:10 ` [PATCH 19/33] btrfs: use level argument in log tree walk callback process_one_buffer() fdmanana
2025-09-05 16:10 ` [PATCH 20/33] btrfs: use level argument in log tree walk callback replay_one_buffer() fdmanana
2025-09-05 16:10 ` [PATCH 21/33] btrfs: use the inode item boolean everywhere in overwrite_item() fdmanana
2025-09-05 16:10 ` [PATCH 22/33] btrfs: add current log leaf, key and slot to struct walk_control fdmanana
2025-09-05 16:10 ` [PATCH 23/33] btrfs: avoid unnecessary path allocation at fixup_inode_link_count() fdmanana
2025-09-05 16:10 ` [PATCH 24/33] btrfs: avoid path allocations when dropping extents during log replay fdmanana
2025-09-05 16:10 ` [PATCH 25/33] btrfs: avoid unnecessary path allocation when replaying a dir item fdmanana
2025-09-05 16:10 ` [PATCH 26/33] btrfs: remove redundant path release when processing dentry during log replay fdmanana
2025-09-05 16:10 ` [PATCH 27/33] btrfs: remove redundant path release when overwriting item " fdmanana
2025-09-05 16:10 ` [PATCH 28/33] btrfs: add path for subvolume tree changes to struct walk_control fdmanana
2025-09-05 16:10 ` [PATCH 29/33] btrfs: stop passing inode object IDs to __add_inode_ref() in log replay fdmanana
2025-09-05 16:10 ` [PATCH 30/33] btrfs: remove pointless inode lookup when processing extrefs during " fdmanana
2025-09-05 16:10 ` [PATCH 31/33] btrfs: abort transaction if we fail to find dir item " fdmanana
2025-09-05 16:10 ` [PATCH 32/33] btrfs: abort transaction if we fail to update inode in log replay dir fixup fdmanana
2025-09-05 16:10 ` [PATCH 33/33] btrfs: dump detailed info and specific messages on log replay failures fdmanana
2025-09-07 11:25 ` Dan Carpenter
2025-09-08 9:52 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes fdmanana
2025-09-08 9:52 ` [PATCH v2 01/33] btrfs: fix invalid extref key setup when replaying dentry fdmanana
2025-09-08 9:52 ` [PATCH v2 02/33] btrfs: use booleans in walk control structure for log replay fdmanana
2025-09-08 9:52 ` [PATCH v2 03/33] btrfs: rename replay_dest member of struct walk_control to root fdmanana
2025-09-08 9:52 ` [PATCH v2 04/33] btrfs: rename root to log in walk_down_log_tree() and walk_up_log_tree() fdmanana
2025-09-08 9:52 ` [PATCH v2 05/33] btrfs: add and use a log root field to struct walk_control fdmanana
2025-09-08 9:53 ` [PATCH v2 06/33] btrfs: deduplicate log root free in error paths from btrfs_recover_log_trees() fdmanana
2025-09-08 9:53 ` [PATCH v2 07/33] btrfs: stop passing transaction parameter to log tree walk functions fdmanana
2025-09-08 9:53 ` [PATCH v2 08/33] btrfs: stop setting log_root_tree->log_root to NULL in btrfs_recover_log_trees() fdmanana
2025-09-08 9:53 ` [PATCH v2 09/33] btrfs: always drop log root tree reference in btrfs_replay_log() fdmanana
2025-09-08 9:53 ` [PATCH v2 10/33] btrfs: pass walk_control structure to replay_xattr_deletes() fdmanana
2025-09-08 9:53 ` [PATCH v2 11/33] btrfs: move up the definition of struct walk_control fdmanana
2025-09-08 9:53 ` [PATCH v2 12/33] btrfs: pass walk_control structure to replay_dir_deletes() fdmanana
2025-09-08 9:53 ` [PATCH v2 13/33] btrfs: pass walk_control structure to check_item_in_log() fdmanana
2025-09-08 9:53 ` [PATCH v2 14/33] btrfs: pass walk_control structure to replay_one_extent() fdmanana
2025-09-08 9:53 ` [PATCH v2 15/33] btrfs: pass walk_control structure to add_inode_ref() and helpers fdmanana
2025-09-08 9:53 ` [PATCH v2 16/33] btrfs: pass walk_control structure to replay_one_dir_item() and replay_one_name() fdmanana
2025-09-08 9:53 ` [PATCH v2 17/33] btrfs: pass walk_control structure to drop_one_dir_item() and helpers fdmanana
2025-09-08 9:53 ` [PATCH v2 18/33] btrfs: pass walk_control structure to overwrite_item() fdmanana
2025-09-08 9:53 ` [PATCH v2 19/33] btrfs: use level argument in log tree walk callback process_one_buffer() fdmanana
2025-09-08 9:53 ` [PATCH v2 20/33] btrfs: use level argument in log tree walk callback replay_one_buffer() fdmanana
2025-09-08 9:53 ` [PATCH v2 21/33] btrfs: use the inode item boolean everywhere in overwrite_item() fdmanana
2025-09-08 9:53 ` [PATCH v2 22/33] btrfs: add current log leaf, key and slot to struct walk_control fdmanana
2025-09-08 9:53 ` [PATCH v2 23/33] btrfs: avoid unnecessary path allocation at fixup_inode_link_count() fdmanana
2025-09-08 9:53 ` [PATCH v2 24/33] btrfs: avoid path allocations when dropping extents during log replay fdmanana
2025-09-08 9:53 ` [PATCH v2 25/33] btrfs: avoid unnecessary path allocation when replaying a dir item fdmanana
2025-09-08 9:53 ` [PATCH v2 26/33] btrfs: remove redundant path release when processing dentry during log replay fdmanana
2025-09-08 9:53 ` [PATCH v2 27/33] btrfs: remove redundant path release when overwriting item " fdmanana
2025-09-08 9:53 ` [PATCH v2 28/33] btrfs: add path for subvolume tree changes to struct walk_control fdmanana
2025-09-08 9:53 ` [PATCH v2 29/33] btrfs: stop passing inode object IDs to __add_inode_ref() in log replay fdmanana
2025-09-08 9:53 ` [PATCH v2 30/33] btrfs: remove pointless inode lookup when processing extrefs during " fdmanana
2025-09-08 9:53 ` [PATCH v2 31/33] btrfs: abort transaction if we fail to find dir item " fdmanana
2025-09-08 9:53 ` [PATCH v2 32/33] btrfs: abort transaction if we fail to update inode in log replay dir fixup fdmanana
2025-09-08 9:53 ` [PATCH v2 33/33] btrfs: dump detailed info and specific messages on log replay failures fdmanana
2025-09-09 0:53 ` David Sterba
2025-09-09 14:42 ` Filipe Manana
2025-09-09 0:55 ` [PATCH v2 00/33] btrfs: log replay bug fix, cleanups and error reporting changes David Sterba
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox