btrfs-progs: cleanup dirty buffers on transaction abort

When adding the extent buffer leak detection I started getting failures
on some of the fuzz tests.  This is because we don't clean up dirty
buffers for aborted transactions, we just leave them dirty and thus we
leak them.  Fix this up by making btrfs_commit_transaction() on an
aborted transaction properly cleanup the dirty buffers that exist in the
system.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Josef Bacik 2023-09-05 16:21:51 -04:00 committed by David Sterba
parent 21aa6777b2
commit 2a34fd3892
1 changed files with 26 additions and 19 deletions

View File

@ -139,6 +139,25 @@ int commit_tree_roots(struct btrfs_trans_handle *trans,
return 0;
}
static void clean_dirty_buffers(struct btrfs_trans_handle *trans)
{
struct btrfs_fs_info *fs_info = trans->fs_info;
struct extent_io_tree *tree = &fs_info->dirty_buffers;
struct extent_buffer *eb;
u64 start, end;
while (find_first_extent_bit(tree, 0, &start, &end, EXTENT_DIRTY,
NULL) == 0) {
while (start <= end) {
eb = find_first_extent_buffer(fs_info, start);
BUG_ON(!eb || eb->start != start);
start += eb->len;
btrfs_clear_buffer_dirty(trans, eb);
free_extent_buffer(eb);
}
}
}
int __commit_transaction(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
@ -181,23 +200,7 @@ cleanup:
* Mark all remaining dirty ebs clean, as they have no chance to be written
* back anymore.
*/
while (1) {
int find_ret;
find_ret = find_first_extent_bit(tree, 0, &start, &end,
EXTENT_DIRTY, NULL);
if (find_ret)
break;
while (start <= end) {
eb = find_first_extent_buffer(fs_info, start);
BUG_ON(!eb || eb->start != start);
start += eb->len;
btrfs_clear_buffer_dirty(trans, eb);
free_extent_buffer(eb);
}
}
clean_dirty_buffers(trans);
return ret;
}
@ -209,8 +212,11 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_space_info *sinfo;
if (trans->fs_info->transaction_aborted)
return -EROFS;
if (trans->fs_info->transaction_aborted) {
ret = -EROFS;
goto error;
}
/*
* Flush all accumulated delayed refs so that root-tree updates are
* consistent
@ -284,6 +290,7 @@ commit_tree:
return ret;
error:
btrfs_abort_transaction(trans, ret);
clean_dirty_buffers(trans);
btrfs_destroy_delayed_refs(trans);
free(trans);
return ret;