btrfs: trim adjacent extents across adjacent block groups boundary

The test case checks if btrfs can trim adjacent extents across
adjacent block groups boundary correctly.

The test case craft the following extents layout:

         |  BG1 |      BG2        |       BG3            |
 Bytenr: X-8M   X      X+512M     X+1G  X+1G+128M
         |//////|//////|          |     |//|

There is a long existing bug that, when btrfs is trying to trim the
range at [X+512M, X+1G+128M), it will only trim [X+512M, X+1G).

This test case is the regression test for this long existing bug.

It will verify the trimmed bytes by using loopback device backed up
by a file, and checking the bytes used by the file to determine how
many bytes are trimmed.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
This commit is contained in:
Qu Wenruo
2019-10-24 18:16:29 +08:00
committed by Eryu Guan
parent 4bd44aa86b
commit 24f2c46da1
3 changed files with 156 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019 SUSE Linux Products GmbH. All Rights Reserved.
#
# FS QA Test 199
#
# Test if btrfs discard mount option is trimming adjacent extents across
# block groups boundary.
# The test case uses loopback device and file used space to detect trimmed
# bytes.
#
# There is a long existing bug that btrfs doesn't discard all space for
# above mentioned case.
#
# The fix is: "btrfs: extent-tree: Ensure we trim ranges across block group
# boundary"
#
seq=`basename $0`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"
here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15
_cleanup()
{
cd /
umount $loop_mnt &> /dev/null
_destroy_loop_device $loop_dev &> /dev/null
rm -rf $tmp.*
}
# get standard environment, filters and checks
. ./common/rc
. ./common/filter
# remove previous $seqres.full before test
rm -f $seqres.full
# real QA test starts here
# Modify as appropriate.
_supported_fs btrfs
_supported_os Linux
_require_loop
_require_xfs_io_command "fiemap"
# We need less than 2G data write, consider it 2G and double it just in case
_require_scratch_size $((4 * 1024 * 1024))
loop_file="$SCRATCH_MNT/image"
# The margin when checking trimmed size.
#
# The margin is for tree blocks, calculated by
# 3 * max_tree_block_size
# | |- 64K
# |- 3 trees get modified (root tree, extent tree, fs tree)
#
# In reality, there should be no margin at all due to the mount options.
# But who knows what will happen in later kernels.
margin_kb=$(( 3 * 64 ))
trimmed_kb=$(( 768 * 1024 )) # 768M
_scratch_mkfs >> $seqres.full
_scratch_mount
# Create a sparse file as the loopback target.
# 10G size makes sure we have 1G chunk size.
truncate -s 10G "$loop_file"
_mkfs_dev -d SINGLE "$loop_file"
loop_dev=$(_create_loop_device "$loop_file")
loop_mnt=$tmp/loop_mnt
mkdir -p $loop_mnt
# - nospace_cache
# Since v1 cache using DATA space, it can break data extent bytenr
# continuousness.
# - nodatasum
# As there will be 1.5G data write, generating 1.5M csum.
# Disabling datasum could reduce the margin caused by metadata to minimal
# - discard
# What we're testing
_mount -o nospace_cache,nodatasum,discard $loop_dev $loop_mnt
# Craft the following extent layout:
# | BG1 | BG2 | BG3 |
# Bytenr: X-8M X X+512M X+1G X+1G+128M
# |//////|//////| |//|
# V V V V
# | | | |- file 'tail_padding'
# | | |- file 'cross_boundary'
# | |- file 'lead_padding2'
# |- file 'lead_padding1'
# So that all extents of file 'cross_boundary' are all adjacent and crosses the
# boundary of BG1 and BG2
# File 'lead_padding1' and 'lead_padding2' are all paddings to fill the
# leading gap.
# File 'tail_padding' is to ensure after deleting file 'cross_boundary' we still
# have used extent in BG3, to prevent trimming the whole BG3.
# And since BG1 needs exactly 8M to fill, we need to sync write to ensure
# the write sequence.
_pwrite_byte 0xcd 0 8M $loop_mnt/lead_padding1 > /dev/null
sync
_pwrite_byte 0xcd 0 512M $loop_mnt/lead_padding2 > /dev/null
sync
_pwrite_byte 0xcd 0 $(($trimmed_kb * 1024)) $loop_mnt/cross_boundary \
> /dev/null
sync
_pwrite_byte 0xcd 0 1M $loop_mnt/tail_padding > /dev/null
sync
$XFS_IO_PROG -c "fiemap" $loop_mnt/cross_boundary >> $seqres.full
# Ensure all extent are continuous
# Btrfs fiemap will merge continuous results, so the output should be only
# 2 lines, 1 line for filename, 1 line for a large merged fiemap result.
if [ $($XFS_IO_PROG -c "fiemap" $loop_mnt/cross_boundary | wc -l) -ne 2 ]; then
_notrun "Non-continuous extent bytenr detected"
fi
size1_kb=$(du $loop_file| cut -f1)
# Delete the file 'cross_boundary'.
# This will delete $trimmed_kb data extents across the chunk boundary.
rm -f $loop_mnt/cross_boundary
# sync so btrfs will commit transaction and trim the freed extents
sync
size2_kb=$(du $loop_file | cut -f1)
echo "loopback file size before discard: $size1_kb KiB" >> $seqres.full
echo "loopback file size after discard: $size2_kb KiB" >> $seqres.full
echo "Expect trimmed size: $trimmed_kb KiB" >> $seqres.full
echo "Have trimmed size: $(($size1_kb - $size2_kb)) KiB" >> $seqres.full
if [ $(($size2_kb+ $trimmed_kb)) -gt $(($size1_kb + $margin_kb)) -o \
$(($size2_kb+ $trimmed_kb)) -lt $(($size1_kb - $margin_kb)) ]; then
echo "Btrfs doesn't trim the range correctly"
fi
echo "Silence is golden"
# success, all done
status=0
exit
+2
View File
@@ -0,0 +1,2 @@
QA output created by 199
Silence is golden
+1
View File
@@ -201,3 +201,4 @@
196 auto metadata log volume
197 auto quick volume
198 auto quick volume
199 auto quick trim