]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
btrfs: abort transaction on specific error places when walking log tree
authorFilipe Manana <fdmanana@suse.com>
Wed, 16 Jul 2025 13:56:11 +0000 (14:56 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 2 Nov 2025 13:15:21 +0000 (22:15 +0900)
[ Upstream commit 6ebd726b104fa99d47c0d45979e6a6109844ac18 ]

We do several things while walking a log tree (for replaying and for
freeing a log tree) like reading extent buffers and cleaning them up,
but we don't immediately abort the transaction, or turn the fs into an
error state, when one of these things fails. Instead we the transaction
abort or turn the fs into error state in the caller of the entry point
function that walks a log tree - walk_log_tree() - which means we don't
get to know exactly where an error came from.

Improve on this by doing a transaction abort / turn fs into error state
after each such failure so that when it happens we have a better
understanding where the failure comes from. This deliberately leaves
the transaction abort / turn fs into error state in the callers of
walk_log_tree() as to ensure we don't get into an inconsistent state in
case we forget to do it deeper in call chain. It also deliberately does
not do it after errors from the calls to the callback defined in
struct walk_control::process_func(), as we will do it later on another
patch.

Reviewed-by: Boris Burkov <boris@bur.io>
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/btrfs/tree-log.c

index 0022ad003791f47b796d0762ffe0b505b4799f98..f3ca530f032df3a8596745941d1463f9e6cd508f 100644 (file)
@@ -2612,15 +2612,24 @@ static int unaccount_log_buffer(struct btrfs_fs_info *fs_info, u64 start)
 static int clean_log_buffer(struct btrfs_trans_handle *trans,
                            struct extent_buffer *eb)
 {
+       int ret;
+
        btrfs_tree_lock(eb);
        btrfs_clear_buffer_dirty(trans, eb);
        wait_on_extent_buffer_writeback(eb);
        btrfs_tree_unlock(eb);
 
-       if (trans)
-               return btrfs_pin_reserved_extent(trans, eb);
+       if (trans) {
+               ret = btrfs_pin_reserved_extent(trans, eb);
+               if (ret)
+                       btrfs_abort_transaction(trans, ret);
+               return ret;
+       }
 
-       return unaccount_log_buffer(eb->fs_info, eb->start);
+       ret = unaccount_log_buffer(eb->fs_info, eb->start);
+       if (ret)
+               btrfs_handle_fs_error(eb->fs_info, ret, NULL);
+       return ret;
 }
 
 static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
@@ -2656,8 +2665,14 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                next = btrfs_find_create_tree_block(fs_info, bytenr,
                                                    btrfs_header_owner(cur),
                                                    *level - 1);
-               if (IS_ERR(next))
-                       return PTR_ERR(next);
+               if (IS_ERR(next)) {
+                       ret = PTR_ERR(next);
+                       if (trans)
+                               btrfs_abort_transaction(trans, ret);
+                       else
+                               btrfs_handle_fs_error(fs_info, ret, NULL);
+                       return ret;
+               }
 
                if (*level == 1) {
                        ret = wc->process_func(root, next, wc, ptr_gen,
@@ -2672,6 +2687,10 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                                ret = btrfs_read_extent_buffer(next, &check);
                                if (ret) {
                                        free_extent_buffer(next);
+                                       if (trans)
+                                               btrfs_abort_transaction(trans, ret);
+                                       else
+                                               btrfs_handle_fs_error(fs_info, ret, NULL);
                                        return ret;
                                }
 
@@ -2687,6 +2706,10 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                ret = btrfs_read_extent_buffer(next, &check);
                if (ret) {
                        free_extent_buffer(next);
+                       if (trans)
+                               btrfs_abort_transaction(trans, ret);
+                       else
+                               btrfs_handle_fs_error(fs_info, ret, NULL);
                        return ret;
                }