btrfs: fix deadlock with fsync+fiemap+transaction commit
authorJosef Bacik <josef@toxicpanda.com>
Mon, 13 Jun 2022 19:09:49 +0000 (15:09 -0400)
committerDavid Sterba <dsterba@suse.com>
Tue, 21 Jun 2022 12:47:08 +0000 (14:47 +0200)
commitbf7ba8ee759b7b7a34787ddd8dc3f190a3d7fa24
tree2aee6217a8bc95482711810a8ac697f1aa520ea7
parent97e86631bccddfbbe0c13f9a9605cdef11d31296
btrfs: fix deadlock with fsync+fiemap+transaction commit

We are hitting the following deadlock in production occasionally

Task 1 Task 2 Task 3 Task 4 Task 5
fsync(A)
 start trans
start commit
falloc(A)
 lock 5m-10m
 start trans
  wait for commit
fiemap(A)
 lock 0-10m
  wait for 5m-10m
   (have 0-5m locked)

 have btrfs_need_log_full_commit
  !full_sync
  wait_ordered_extents
finish_ordered_io(A)
lock 0-5m
DEADLOCK

We have an existing dependency of file extent lock -> transaction.
However in fsync if we tried to do the fast logging, but then had to
fall back to committing the transaction, we will be forced to call
btrfs_wait_ordered_range() to make sure all of our extents are updated.

This creates a dependency of transaction -> file extent lock, because
btrfs_finish_ordered_io() will need to take the file extent lock in
order to run the ordered extents.

Fix this by stopping the transaction if we have to do the full commit
and we attempted to do the fast logging.  Then attach to the transaction
and commit it if we need to.

CC: stable@vger.kernel.org # 5.15+
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/file.c