--- /dev/null
+#! /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