Skip to content

Commit 7511e29

Browse files
boryaskdave
authored andcommitted
btrfs: harden block_group::bg_list against list_del() races
As far as I can tell, these calls of list_del_init() on bg_list cannot run concurrently with btrfs_mark_bg_unused() or btrfs_mark_bg_to_reclaim(), as they are in transaction error paths and situations where the block group is readonly. However, if there is any chance at all of racing with mark_bg_unused(), or a different future user of bg_list, better to be safe than sorry. Otherwise we risk the following interleaving (bg_list refcount in parens) T1 (some random op) T2 (btrfs_mark_bg_unused) !list_empty(&bg->bg_list); (1) list_del_init(&bg->bg_list); (1) list_move_tail (1) btrfs_put_block_group (0) btrfs_delete_unused_bgs bg = list_first_entry list_del_init(&bg->bg_list); btrfs_put_block_group(bg); (-1) Ultimately, this results in a broken ref count that hits zero one deref early and the real final deref underflows the refcount, resulting in a WARNING. Reviewed-by: Qu Wenruo <[email protected]> Reviewed-by: Filipe Manana <[email protected]> Signed-off-by: Boris Burkov <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 2d8e516 commit 7511e29

File tree

2 files changed

+20
-0
lines changed

2 files changed

+20
-0
lines changed

fs/btrfs/extent-tree.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2868,7 +2868,15 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans)
28682868
block_group->length,
28692869
&trimmed);
28702870

2871+
/*
2872+
* Not strictly necessary to lock, as the block_group should be
2873+
* read-only from btrfs_delete_unused_bgs().
2874+
*/
2875+
ASSERT(block_group->ro);
2876+
spin_lock(&fs_info->unused_bgs_lock);
28712877
list_del_init(&block_group->bg_list);
2878+
spin_unlock(&fs_info->unused_bgs_lock);
2879+
28722880
btrfs_unfreeze_block_group(block_group);
28732881
btrfs_put_block_group(block_group);
28742882

fs/btrfs/transaction.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,13 @@ void btrfs_put_transaction(struct btrfs_transaction *transaction)
160160
cache = list_first_entry(&transaction->deleted_bgs,
161161
struct btrfs_block_group,
162162
bg_list);
163+
/*
164+
* Not strictly necessary to lock, as no other task will be using a
165+
* block_group on the deleted_bgs list during a transaction abort.
166+
*/
167+
spin_lock(&transaction->fs_info->unused_bgs_lock);
163168
list_del_init(&cache->bg_list);
169+
spin_unlock(&transaction->fs_info->unused_bgs_lock);
164170
btrfs_unfreeze_block_group(cache);
165171
btrfs_put_block_group(cache);
166172
}
@@ -2096,7 +2102,13 @@ static void btrfs_cleanup_pending_block_groups(struct btrfs_trans_handle *trans)
20962102

20972103
list_for_each_entry_safe(block_group, tmp, &trans->new_bgs, bg_list) {
20982104
btrfs_dec_delayed_refs_rsv_bg_inserts(fs_info);
2105+
/*
2106+
* Not strictly necessary to lock, as no other task will be using a
2107+
* block_group on the new_bgs list during a transaction abort.
2108+
*/
2109+
spin_lock(&fs_info->unused_bgs_lock);
20992110
list_del_init(&block_group->bg_list);
2111+
spin_unlock(&fs_info->unused_bgs_lock);
21002112
}
21012113
}
21022114

0 commit comments

Comments
 (0)