| #! /bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright (c) 2019 Red Hat, Inc. All Rights Reserved. |
| # |
| # FS QA Test No. 563 |
| # |
| # This test verifies that cgroup aware writeback properly accounts I/Os in |
| # various scenarios. We perform reads/writes from different combinations of |
| # cgroups and verify that pages are accounted against the group that brought |
| # them into cache. |
| # |
| |
| 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 / |
| rm -f $tmp.* |
| |
| echo $$ > $cgdir/cgroup.procs |
| rmdir $cgdir/$seq-cg* > /dev/null 2>&1 |
| umount $SCRATCH_MNT > /dev/null 2>&1 |
| _destroy_loop_device $LOOP_DEV > /dev/null 2>&1 |
| } |
| |
| # get standard environment, filters and checks |
| . ./common/rc |
| . ./common/filter |
| . ./common/cgroup2 |
| |
| # remove previous $seqres.full before test |
| rm -f $seqres.full |
| |
| # real QA test starts here |
| |
| # Modify as appropriate. |
| _supported_fs generic |
| _require_scratch_nocheck |
| _require_cgroup2 io |
| _require_loop |
| |
| # cgroup v2 writeback is only support on block devices so far |
| _require_block_device $SCRATCH_DEV |
| |
| cgdir=$CGROUP2_PATH |
| iosize=$((1024 * 1024 * 8)) |
| |
| # Check cgroup read/write charges against expected values. Allow for some |
| # tolerance as different filesystems seem to account slightly differently. |
| check_cg() |
| { |
| cgroot=$1 |
| cgname=$(basename $cgroot) |
| expectedread=$2 |
| expectedwrite=$3 |
| readtol=$4 |
| writetol=$5 |
| rbytes=0 |
| wbytes=0 |
| |
| iobytes=`cat $cgroot/io.stat | grep $smajor:$sminor` |
| if [ $? == 0 ]; then |
| rbytes=`echo $iobytes | awk '{ print $2 }' | \ |
| awk -F = '{ print $2 }'` |
| wbytes=`echo $iobytes | awk '{ print $3 }' | \ |
| awk -F = '{ print $2 }'` |
| fi |
| |
| _within_tolerance "read" $rbytes $expectedread $readtol -v |
| _within_tolerance "write" $wbytes $expectedwrite $writetol -v |
| } |
| |
| # Move current process to another cgroup. |
| switch_cg() |
| { |
| mkdir -p $1 |
| echo $$ > $1/cgroup.procs |
| } |
| |
| # Reset cgroup state for a new test. |
| reset() |
| { |
| echo $$ > $cgdir/cgroup.procs |
| rmdir $cgdir/$seq-cg* > /dev/null 2>&1 |
| $XFS_IO_PROG -fc "pwrite 0 $iosize" $SCRATCH_MNT/file \ |
| >> $seqres.full 2>&1 |
| umount $SCRATCH_MNT || _fail "umount failed" |
| _mount $LOOP_DEV $SCRATCH_MNT || _fail "mount failed" |
| stat $SCRATCH_MNT/file > /dev/null |
| } |
| |
| # cgroup I/O accounting doesn't work on partitions. Use a loop device to rule |
| # that out. |
| LOOP_DEV=$(_create_loop_device $SCRATCH_DEV) |
| smajor=$((0x`stat -L -c %t $LOOP_DEV`)) |
| sminor=$((0x`stat -L -c %T $LOOP_DEV`)) |
| |
| _mkfs_dev $LOOP_DEV >> $seqres.full 2>&1 |
| _mount $LOOP_DEV $SCRATCH_MNT || _fail "mount failed" |
| |
| drop_io_cgroup= |
| grep -q -w io $cgdir/cgroup.subtree_control || drop_io_cgroup=1 |
| |
| echo "+io" > $cgdir/cgroup.subtree_control || _fail "subtree control" |
| |
| # Read and write from a single group. |
| echo "read/write" |
| reset |
| switch_cg $cgdir/$seq-cg |
| $XFS_IO_PROG -c "pread 0 $iosize" -c "pwrite 0 $iosize" -c fsync \ |
| $SCRATCH_MNT/file >> $seqres.full 2>&1 |
| switch_cg $cgdir |
| $XFS_IO_PROG -c fsync $SCRATCH_MNT/file |
| check_cg $cgdir/$seq-cg $iosize $iosize 5% 5% |
| |
| # Write from one cgroup then read and write from a second. Writes are charged to |
| # the first group and nothing to the second. |
| echo "write -> read/write" |
| reset |
| switch_cg $cgdir/$seq-cg |
| $XFS_IO_PROG -c "pwrite 0 $iosize" $SCRATCH_MNT/file >> $seqres.full 2>&1 |
| switch_cg $cgdir/$seq-cg-2 |
| $XFS_IO_PROG -c "pread 0 $iosize" -c "pwrite 0 $iosize" $SCRATCH_MNT/file \ |
| >> $seqres.full 2>&1 |
| switch_cg $cgdir |
| $XFS_IO_PROG -c fsync $SCRATCH_MNT/file |
| # Use a fixed value tolerance for the expected value of zero here |
| # because filesystems might perform a small number of metadata reads to |
| # complete the write. On ext2/3 with 1k block size, the read bytes is |
| # as large as 33792. |
| check_cg $cgdir/$seq-cg 0 $iosize 33792 5% |
| check_cg $cgdir/$seq-cg-2 0 0 0 0 |
| |
| # Read from one cgroup, read & write from a second. Both reads and writes are |
| # charged to the first group and nothing to the second. |
| echo "read -> read/write" |
| reset |
| switch_cg $cgdir/$seq-cg |
| $XFS_IO_PROG -c "pread 0 $iosize" $SCRATCH_MNT/file >> $seqres.full 2>&1 |
| switch_cg $cgdir/$seq-cg-2 |
| $XFS_IO_PROG -c "pread 0 $iosize" -c "pwrite 0 $iosize" $SCRATCH_MNT/file \ |
| >> $seqres.full 2>&1 |
| switch_cg $cgdir |
| $XFS_IO_PROG -c fsync $SCRATCH_MNT/file |
| check_cg $cgdir/$seq-cg $iosize $iosize 5% 5% |
| check_cg $cgdir/$seq-cg-2 0 0 0 0 |
| |
| if [ "$drop_io_cgroup" = 1 ]; then |
| echo "-io" > $cgdir/cgroup.subtree_control || _fail "subtree control" |
| fi |
| |
| # success, all done |
| status=0 |
| exit |