On Wed, Jun 03, 2020 at 02:21:15PM +0800, Qu Wenruo wrote: > [BUG] > When the data space is exhausted, even the inode has NOCOW attribute, > btrfs will still refuse to truncate unaligned range due to ENOSPC. > > The following script can reproduce it pretty easily: > #!/bin/bash > > dev=/dev/test/test > mnt=/mnt/btrfs > > umount $dev &> /dev/null > umount $mnt&> /dev/null > > mkfs.btrfs -f $dev -b 1G > mount -o nospace_cache $dev $mnt > touch $mnt/foobar > chattr +C $mnt/foobar > > xfs_io -f -c "pwrite -b 4k 0 4k" $mnt/foobar > /dev/null > xfs_io -f -c "pwrite -b 4k 0 1G" $mnt/padding &> /dev/null > sync > > xfs_io -c "fpunch 0 2k" $mnt/foobar > umount $mnt > > Current btrfs will fail at the fpunch part. > > [CAUSE] > Because btrfs_truncate_block() always reserve space without checking the > NOCOW attribute. > > Since the writeback path follows NOCOW bit, we only need to bother the > space reservation code in btrfs_truncate_block(). > > [FIX] > Make btrfs_truncate_block() to follow btrfs_buffered_write() to try to > reserve data space first, and falls back to NOCOW check only when we > don't have enough space. > > Such always-try-reserve is an optimization introduced in > btrfs_buffered_write(), to avoid expensive btrfs_check_can_nocow() call. > > Since now check_can_nocow() is needed outside of inode.c, also export it > and rename it to btrfs_check_can_nocow(). > > Reported-by: Martin Doucha <martin.doucha@xxxxxxxx> > Signed-off-by: Qu Wenruo <wqu@xxxxxxxx> > Reviewed-by: Filipe Manana <fdmanana@xxxxxxxx> > --- > Changelog: > v2: > - Rebased to misc-next > Only one minor conflict in ctree.h > --- > fs/btrfs/ctree.h | 2 ++ > fs/btrfs/file.c | 10 +++++----- > fs/btrfs/inode.c | 41 ++++++++++++++++++++++++++++++++++------- > 3 files changed, 41 insertions(+), 12 deletions(-) > > diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h > index 161533040978..40e8c8170d39 100644 > --- a/fs/btrfs/ctree.h > +++ b/fs/btrfs/ctree.h > @@ -2984,6 +2984,8 @@ int btrfs_dirty_pages(struct inode *inode, struct page **pages, > size_t num_pages, loff_t pos, size_t write_bytes, > struct extent_state **cached); > int btrfs_fdatawrite_range(struct inode *inode, loff_t start, loff_t end); > +int btrfs_check_can_nocow(struct btrfs_inode *inode, loff_t pos, > + size_t *write_bytes); This does not apply anymore after Filipe's aio nowait fixes, can you please rebase it on top of current misc-next?
