Btrfs: fix unprotected list operations at btrfs_write_dirty_block_groups
authorFilipe Manana <fdmanana@suse.com>
Fri, 18 Dec 2015 03:02:48 +0000 (03:02 +0000)
committerFilipe Manana <fdmanana@suse.com>
Mon, 21 Dec 2015 17:51:22 +0000 (17:51 +0000)
commite44081ef611832b47a86abf4e36dc0ed2e950884
treed30d0dd196f66f9ebc56233312f41668cf875381
parent0376374a98abd533fb49c6db12967bddc2f4b4b3
Btrfs: fix unprotected list operations at btrfs_write_dirty_block_groups

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: Filipe Manana <fdmanana@suse.com>
fs/btrfs/extent-tree.c