btrfs: fix deadlock with fsync+fiemap+transaction commit
authorJosef Bacik <josef@toxicpanda.com>
Mon, 13 Jun 2022 19:09:49 +0000 (15:09 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Jun 2022 07:03:27 +0000 (09:03 +0200)
commitd98b5032c9d046c946099fa09a01657f194796ad
treef837651c60e1b310a07435b2e4d93621d6372602
parent1238f580cd812c588d5ad67067795692d7a1828f
btrfs: fix deadlock with fsync+fiemap+transaction commit

commit bf7ba8ee759b7b7a34787ddd8dc3f190a3d7fa24 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/btrfs/file.c