btrfs: qgroup: don't try to wait flushing if we're already holding a transaction
authorQu Wenruo <wqu@suse.com>
Fri, 4 Dec 2020 01:24:47 +0000 (09:24 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Jan 2021 19:18:24 +0000 (20:18 +0100)
commit1888e5df8449ef16655f827bd46d0a809b3048a4
tree727b5e593a83014fea6a380b8edd2a1521891b44
parent1c31964eca1397b923ff388866c67a25dc24b0da
btrfs: qgroup: don't try to wait flushing if we're already holding a transaction

commit ae5e070eaca9dbebde3459dd8f4c2756f8c097d0 upstream.

There is a chance of racing for qgroup flushing which may lead to
deadlock:

Thread A | Thread B
   (not holding trans handle) |  (holding a trans handle)
--------------------------------+--------------------------------
__btrfs_qgroup_reserve_meta()   | __btrfs_qgroup_reserve_meta()
|- try_flush_qgroup() | |- try_flush_qgroup()
   |- QGROUP_FLUSHING bit set   |    |
   | |    |- test_and_set_bit()
   | |    |- wait_event()
   |- btrfs_join_transaction() |
   |- btrfs_commit_transaction()|

!!! DEAD LOCK !!!

Since thread A wants to commit transaction, but thread B is holding a
transaction handle, blocking the commit.
At the same time, thread B is waiting for thread A to finish its commit.

This is just a hot fix, and would lead to more EDQUOT when we're near
the qgroup limit.

The proper fix would be to make all metadata/data reservations happen
without holding a transaction handle.

CC: stable@vger.kernel.org # 5.9+
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/btrfs/qgroup.c