• Filipe Manana's avatar
    Btrfs: fix unprotected list operations at btrfs_write_dirty_block_groups · e44081ef
    Filipe Manana authored
    We call btrfs_write_dirty_block_groups() in the critical section of a
    transaction's commit, when no other tasks can join the transaction and
    add more block groups to the transaction's list of dirty block groups,
    so we not taking the dirty block groups spinlock when checking for the
    list's emptyness, grabbing its first element or deleting elements from
    it.
    
    However there's a special and rare case where we can have a concurrent
    task adding elements to this list. We trigger writeback for space
    caches before at btrfs_start_dirty_block_groups() and in past iterations
    of the loop at btrfs_write_dirty_block_groups(), this means that when
    the writeback finishes (which happens asynchronously) it creates a
    task for the endio free space work queue that executes
    btrfs_finish_ordered_io() - this function is able to join the transaction,
    through btrfs_join_transaction_nolock(), and update the free space cache's
    inode item in the root tree, which can result in COWing nodes of this tree
    and therefore allocation of a new block group can happen, which gets added
    to the transaction's list of dirty block groups while the transaction
    commit task is operating on it concurrently.
    
    So fix this by taking the dirty block groups spinlock before doing
    operations on the dirty block groups list at
    btrfs_write_dirty_block_groups().
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    e44081ef
extent-tree.c 290 KB