From: Filipe Manana Date: Thu, 1 Sep 2022 13:18:25 +0000 (+0100) Subject: btrfs: properly flush delalloc when entering fiemap X-Git-Tag: v6.6-pxa1908~2836^2~96 X-Git-Url: https://git.dujemihanovic.xyz/?a=commitdiff_plain;h=33a86cfa1741ef1668b0d312c7964b4f35e9d31e;p=linux.git btrfs: properly flush delalloc when entering fiemap If the flag FIEMAP_FLAG_SYNC is passed to fiemap, it means all delalloc should be flushed and writeback complete. We call the generic helper fiemap_prep() which does a filemap_write_and_wait() in case that flag is given, however that is not enough if we have compression. Because a single filemap_fdatawrite_range() only starts compression (in an async thread) and therefore returns before the compression is done and writeback is started. So make btrfs_fiemap(), actually wait for all writeback to start and complete if FIEMAP_FLAG_SYNC is set. We start and wait for writeback on the whole possible file range, from 0 to LLONG_MAX, because that is what the generic code at fiemap_prep() does. Reviewed-by: Josef Bacik Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index e5284f2686c8..c479e5f16f74 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8252,6 +8252,26 @@ static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, if (ret) return ret; + /* + * fiemap_prep() called filemap_write_and_wait() for the whole possible + * file range (0 to LLONG_MAX), but that is not enough if we have + * compression enabled. The first filemap_fdatawrite_range() only kicks + * in the compression of data (in an async thread) and will return + * before the compression is done and writeback is started. A second + * filemap_fdatawrite_range() is needed to wait for the compression to + * complete and writeback to start. Without this, our user is very + * likely to get stale results, because the extents and extent maps for + * delalloc regions are only allocated when writeback starts. + */ + if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC) { + ret = btrfs_fdatawrite_range(inode, 0, LLONG_MAX); + if (ret) + return ret; + ret = filemap_fdatawait_range(inode->i_mapping, 0, LLONG_MAX); + if (ret) + return ret; + } + return extent_fiemap(BTRFS_I(inode), fieinfo, start, len); }