btrfs: fix RWF_NOWAIT writes blocking on extent locks and waiting for IO
authorFilipe Manana <fdmanana@suse.com>
Mon, 15 Jun 2020 17:49:39 +0000 (18:49 +0100)
committerDavid Sterba <dsterba@suse.com>
Tue, 16 Jun 2020 17:22:45 +0000 (19:22 +0200)
commit5dbb75ed6900048e146247b6325742d92c892548
treeed71a36aaad8a325ccb50d3dd242c8cd50a89c69
parent260a63395f90f67d6ab89e4266af9e3dc34a77e9
btrfs: fix RWF_NOWAIT writes blocking on extent locks and waiting for IO

A RWF_NOWAIT write is not supposed to wait on filesystem locks that can be
held for a long time or for ongoing IO to complete.

However when calling check_can_nocow(), if the inode has prealloc extents
or has the NOCOW flag set, we can block on extent (file range) locks
through the call to btrfs_lock_and_flush_ordered_range(). Such lock can
take a significant amount of time to be available. For example, a fiemap
task may be running, and iterating through the entire file range checking
all extents and doing backref walking to determine if they are shared,
or a readpage operation may be in progress.

Also at btrfs_lock_and_flush_ordered_range(), called by check_can_nocow(),
after locking the file range we wait for any existing ordered extent that
is in progress to complete. Another operation that can take a significant
amount of time and defeat the purpose of RWF_NOWAIT.

So fix this by trying to lock the file range and if it's currently locked
return -EAGAIN to user space. If we are able to lock the file range without
waiting and there is an ordered extent in the range, return -EAGAIN as
well, instead of waiting for it to complete. Finally, don't bother trying
to lock the snapshot lock of the root when attempting a RWF_NOWAIT write,
as that is only important for buffered writes.

Fixes: edf064e7c6fec3 ("btrfs: nowait aio support")
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/file.c