| #! /bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved. |
| # |
| # FS QA Test 276 |
| # |
| # Verify that fiemap correctly reports the sharedness of extents for a file with |
| # a very large number of extents, spanning many b+tree leaves in the fs tree, |
| # and when the file's subvolume was snapshoted. |
| # |
| . ./common/preamble |
| _begin_fstest auto snapshot fiemap remount |
| |
| . ./common/filter |
| . ./common/filter.btrfs |
| . ./common/attr |
| |
| _require_scratch |
| _require_xfs_io_command "fiemap" "ranged" |
| _require_attrs |
| _require_odirect |
| |
| _scratch_mkfs >> $seqres.full 2>&1 |
| _scratch_mount |
| |
| fiemap_test_file() |
| { |
| local offset=$1 |
| local len=$2 |
| |
| # Skip the first two lines of xfs_io's fiemap output (file path and |
| # header describing the output columns) as well as holes. |
| $XFS_IO_PROG -c "fiemap -v $offset $len" $SCRATCH_MNT/foo | \ |
| grep -v 'hole' | tail -n +3 |
| } |
| |
| # Count the number of shared extents for the whole test file or just for a given |
| # range. |
| count_shared_extents() |
| { |
| local offset=$1 |
| local len=$2 |
| |
| # Column 5 (from xfs_io's "fiemap -v" command) is the flags (hex field). |
| # 0x2000 is the value for the FIEMAP_EXTENT_SHARED flag. |
| fiemap_test_file $offset $len | \ |
| $AWK_PROG --source 'BEGIN { cnt = 0 }' \ |
| --source '{ if (and(strtonum($5), 0x2000)) cnt++ }' \ |
| --source 'END { print cnt }' |
| } |
| |
| # Count the number of non shared extents for the whole test file or just for a |
| # given range. |
| count_not_shared_extents() |
| { |
| local offset=$1 |
| local len=$2 |
| |
| # Column 5 (from xfs_io's "fiemap -v" command) is the flags (hex field). |
| # 0x2000 is the value for the FIEMAP_EXTENT_SHARED flag. |
| fiemap_test_file $offset $len | \ |
| $AWK_PROG --source 'BEGIN { cnt = 0 }' \ |
| --source '{ if (!and(strtonum($5), 0x2000)) cnt++ }' \ |
| --source 'END { print cnt }' |
| } |
| |
| # Create a file with 2000 extents, and a fs tree with a height of at least 3 |
| # (root node at level 2). We want to verify later that fiemap correctly reports |
| # the sharedness of each extent, even when it needs to switch from one leaf to |
| # the next one and from a node at level 1 to the next node at level 1. |
| # To speedup creating a fs tree of height >= 3, add several large xattrs. |
| ext_size=$(( 64 * 1024 )) |
| file_size=$(( 2000 * 2 * $ext_size )) # about 250M |
| nr_cpus=$("$here/src/feature" -o) |
| workers=0 |
| for (( i = 0; i < $file_size; i += 2 * $ext_size )); do |
| $XFS_IO_PROG -f -d -c "pwrite -b $ext_size $i $ext_size" \ |
| $SCRATCH_MNT/foo > /dev/null & |
| workers=$(( workers + 1 )) |
| if [ "$workers" -ge "$nr_cpus" ]; then |
| workers=0 |
| wait |
| fi |
| done |
| wait |
| |
| workers=0 |
| xattr_value=$(printf '%0.sX' $(seq 1 3900)) |
| for (( i = 1; i <= 29000; i++ )); do |
| echo -n > $SCRATCH_MNT/filler_$i |
| $SETFATTR_PROG -n 'user.x1' -v $xattr_value $SCRATCH_MNT/filler_$i & |
| workers=$(( workers + 1 )) |
| if [ "$workers" -ge "$nr_cpus" ]; then |
| workers=0 |
| wait |
| fi |
| done |
| wait |
| |
| # Make sure every ordered extent completed and therefore updated the fs tree. |
| sync |
| |
| # All extents should be reported as non shared (2000 extents). |
| echo "Number of non-shared extents in the whole file: $(count_not_shared_extents)" |
| |
| # Creating a snapshot. |
| _btrfs subvolume snapshot $SCRATCH_MNT $SCRATCH_MNT/snap |
| |
| # We have a snapshot, so now all extents should be reported as shared. |
| echo "Number of shared extents in the whole file: $(count_shared_extents)" |
| |
| # Now COW two file ranges, of 64K each, in the snapshot's file. |
| # So 2 extents should become non-shared after this. Each file extent item is in |
| # different leaf of the snapshot tree. |
| # |
| $XFS_IO_PROG -d -c "pwrite -b $ext_size 512K $ext_size" \ |
| -d -c "pwrite -b $ext_size 249M $ext_size" \ |
| $SCRATCH_MNT/snap/foo | _filter_xfs_io |
| |
| # Wait for ordered extents to complete and commit current transaction to make |
| # sure fiemap will see all extents in the subvolume and extent trees. |
| sync |
| |
| # Now we should have 2 non-shared extents and 1998 shared extents. |
| echo "Number of non-shared extents in the whole file: $(count_not_shared_extents)" |
| echo "Number of shared extents in the whole file: $(count_shared_extents)" |
| |
| # Check that the non-shared extents are indeed in the expected file ranges. |
| echo "Number of non-shared extents in range [512K, 512K + 64K): $(count_not_shared_extents 512K 64K)" |
| echo "Number of non-shared extents in range [249M, 249M + 64K): $(count_not_shared_extents 249M 64K)" |
| |
| # Now delete the snapshot. |
| $BTRFS_UTIL_PROG subvolume delete -c $SCRATCH_MNT/snap | _filter_btrfs_subvol_delete |
| |
| # We deleted the snapshot and committed the transaction used to delete it (-c), |
| # but all its extents (both metadata and data) are actually only deleted in the |
| # background, by the cleaner kthread. So remount, which wakes up the cleaner |
| # kthread, with a commit interval of 1 second and sleep for 1.1 seconds - after |
| # this we are guaranteed all extents of the snapshot were deleted. |
| _scratch_remount commit=1 |
| sleep 1.1 |
| |
| # Now all extents should be reported as not shared (2000 extents). |
| echo "Number of non-shared extents in the whole file: $(count_not_shared_extents)" |
| |
| # success, all done |
| status=0 |
| exit |