summaryrefslogtreecommitdiff
path: root/tests/btrfs/199
blob: 709ad1f988c3d2cfe81a231bf26e0ac1dafab705 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#! /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"
#
. ./common/preamble
_begin_fstest auto quick trim fiemap

# Override the default cleanup function.
_cleanup()
{
	cd /
	umount $loop_mnt &> /dev/null
	_destroy_loop_device $loop_dev &> /dev/null
	rm -rf $tmp.*
}

# Import common functions.
. ./common/filter

# real QA test starts here

# Modify as appropriate.
_supported_fs btrfs
_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
# - $(_btrfs_no_v1_cache_opt)
#   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 $(_btrfs_no_v1_cache_opt) -o 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