| From 0b34c261e235a5c74dcf78bd305845bd15fe2b42 Mon Sep 17 00:00:00 2001 |
| From: Goldwyn Rodrigues <rgoldwyn@suse.com> |
| Date: Fri, 30 Sep 2016 10:40:52 -0500 |
| Subject: btrfs: qgroup: Prevent qgroup->reserved from going subzero |
| |
| From: Goldwyn Rodrigues <rgoldwyn@suse.com> |
| |
| commit 0b34c261e235a5c74dcf78bd305845bd15fe2b42 upstream. |
| |
| While free'ing qgroup->reserved resources, we much check if |
| the page has not been invalidated by a truncate operation |
| by checking if the page is still dirty before reducing the |
| qgroup resources. Resources in such a case are free'd when |
| the entire extent is released by delayed_ref. |
| |
| This fixes a double accounting while releasing resources |
| in case of truncating a file, reproduced by the following testcase. |
| |
| SCRATCH_DEV=/dev/vdb |
| SCRATCH_MNT=/mnt |
| mkfs.btrfs -f $SCRATCH_DEV |
| mount -t btrfs $SCRATCH_DEV $SCRATCH_MNT |
| cd $SCRATCH_MNT |
| btrfs quota enable $SCRATCH_MNT |
| btrfs subvolume create a |
| btrfs qgroup limit 500m a $SCRATCH_MNT |
| sync |
| for c in {1..15}; do |
| dd if=/dev/zero bs=1M count=40 of=$SCRATCH_MNT/a/file; |
| done |
| |
| sleep 10 |
| sync |
| sleep 5 |
| |
| touch $SCRATCH_MNT/a/newfile |
| |
| echo "Removing file" |
| rm $SCRATCH_MNT/a/file |
| |
| Fixes: b9d0b38928 ("btrfs: Add handler for invalidate page") |
| Signed-off-by: Goldwyn Rodrigues <rgoldwyn@suse.com> |
| Reviewed-by: Qu Wenruo <quwenruo@cn.fujitsu.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/btrfs/inode.c | 9 +++++++-- |
| 1 file changed, 7 insertions(+), 2 deletions(-) |
| |
| --- a/fs/btrfs/inode.c |
| +++ b/fs/btrfs/inode.c |
| @@ -8915,9 +8915,14 @@ again: |
| * So even we call qgroup_free_data(), it won't decrease reserved |
| * space. |
| * 2) Not written to disk |
| - * This means the reserved space should be freed here. |
| + * This means the reserved space should be freed here. However, |
| + * if a truncate invalidates the page (by clearing PageDirty) |
| + * and the page is accounted for while allocating extent |
| + * in btrfs_check_data_free_space() we let delayed_ref to |
| + * free the entire extent. |
| */ |
| - btrfs_qgroup_free_data(inode, page_start, PAGE_SIZE); |
| + if (PageDirty(page)) |
| + btrfs_qgroup_free_data(inode, page_start, PAGE_SIZE); |
| if (!inode_evicting) { |
| clear_extent_bit(tree, page_start, page_end, |
| EXTENT_LOCKED | EXTENT_DIRTY | |