From: Zorro Lang Date: Thu, 3 Aug 2017 14:22:25 +0000 (+0800) Subject: generic: test read around EOF X-Git-Tag: v2022.05.01~1920 X-Git-Url: http://git.apps.os.sepia.ceph.com/?p=xfstests-dev.git;a=commitdiff_plain;h=4939477f3feec0428e2a40c51a74bc6badc8d85e generic: test read around EOF As posix standard, if the file offset is at or past the end of file, no bytes are read, and read() returns zero. There was a bug, when DIO read offset is just past the EOF a little, but in the same block with EOF, read returns different negative values. Kernel commit 74cedf9b6c60 ("direct-io: Fix negative return from dio read beyond eof") and commit 2d4594acbf6d ("fix the regression from "direct-io: Fix negative return from dio read beyond eof"") fixed the bug. This case reads from range within EOF, past EOF and at EOF, to make sure the return value as expected, especially read from past/at EOF returns 0. [eguan: update commit log and comments about information of the specific bug, adjust read_test param order (offset, count, ret) and test description] Signed-off-by: Zorro Lang Reviewed-by: Eryu Guan Signed-off-by: Eryu Guan --- diff --git a/tests/generic/450 b/tests/generic/450 new file mode 100755 index 00000000..a430b85b --- /dev/null +++ b/tests/generic/450 @@ -0,0 +1,163 @@ +#! /bin/bash +# FS QA Test 450 +# +# Test read around EOF. If the file offset is at or past the end of file, +# no bytes are read, and read() returns zero. There was a bug, when DIO +# read offset is just past the EOF a little, but in the same block with +# EOF, read returns different negative values. +# +# The following two kernel commits fixed this bug: +# 74cedf9b6c60 direct-io: Fix negative return from dio read beyond eof +# 2d4594acbf6d fix the regression from "direct-io: Fix negative return +# from dio read beyond eof" +# +#----------------------------------------------------------------------- +# Copyright (c) 2017 Red Hat, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +#----------------------------------------------------------------------- +# + +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.* + rm -f $tfile +} + +# get standard environment, filters and checks +. ./common/rc + +# remove previous $seqres.full before test +rm -f $seqres.full + +# real QA test starts here +_supported_fs generic +_supported_os Linux +_require_test +_require_odirect + +tfile=$TEST_DIR/testfile_${seq} +ssize=`_min_dio_alignment $TEST_DEV` +bsize=`_get_block_size $TEST_DIR` + +# let's focus on the specific bug that only happens when $ssize <= $bsize +if [ $ssize -gt $((bsize/4)) ]; then + _notrun "Only test on sector size < half of block size" +fi + +rm -f $tfile 2>/dev/null + +# check xfs_io pread result, especially for +# Param1: expected pread offset +# Param2: expected pread count +# Param3: expected pread return +# +# If any of above values are not as expected, the output keeps +# using the real value +check_xfs_io_read() +{ + OFFSET=$1 + COUNT=$2 + RETURN=$3 + + $AWK_PROG -v ret="$RETURN" -v cnt="$COUNT" -v off="$OFFSET" ' + /read/{ + split($2, bytes, "/") + + retval=bytes[1] + count=bytes[2] + offset=$NF + + if(retval != ret || count != cnt || offset != off) + printf("expect [%s,%s,%s], got [%s,%s,%s]\n", \ + off, cnt, ret, offset, count, retval) + + next + } + ' +} + +# +-------------------------------------------------------+ +# | block | block | +# +-------------------------------------------------------+ +# | sect | sect | sect | sect | sect | sect | sect | sect | +# | +# EOF +# |<--------------- move EOF -------------->| xxxxxxxxxxx | +# [pread1] +# [ pread2 ] +# [pread3] +# [pread4] ... [pread5] +# +# Run below steps with different $operation and $openflag +# +# 1) write 2 blocks (6 sectors) data to move EOF to the penultimate sector +# 2) read (pread1) the first sector within EOF +# 3) read (pread2) the second block contain EOF +# 4) read (pread3) a sector at (after) EOF +# 5) read (pread4) the last sector past EOF +# 6) read (pread5) at far away from EOF +# +asize=$((bsize * 2)) +tsize=$((asize - ssize * 2)) + +read_test() +{ + local of="$1" + local op="buffer read" + + if [ "$of" != "" ]; then + op="direct read" + fi + + echo "$op the first sector within EOF" + $XFS_IO_PROG $of -c "pread 0 $ssize" $tfile | \ + check_xfs_io_read 0 "$ssize" "$ssize" + + echo "$op the second block contains EOF" + $XFS_IO_PROG $of -c "pread $bsize $bsize" $tfile | \ + check_xfs_io_read "$bsize" "$bsize" "$((tsize - bsize))" + + echo "$op a sector at (after) EOF" + $XFS_IO_PROG $of -c "pread $tsize $ssize" $tfile | \ + check_xfs_io_read "$tsize" "$ssize" "0" + + echo "$op the last sector past EOF" + $XFS_IO_PROG $of -c "pread $((tsize + ssize)) $ssize" $tfile | \ + check_xfs_io_read "$((tsize + ssize))" "$ssize" "0" + + echo "$op at far away from EOF" + $XFS_IO_PROG $of -c "pread $((bsize * 100)) $ssize" $tfile | \ + check_xfs_io_read "$((bsize * 100))" "$ssize" "0" +} + +# Test buffer/direct I/O read +$XFS_IO_PROG -ft -c "pwrite 0 ${tsize}" -c "fsync" $tfile >>$seqres.full +for oflag in "" "-d"; do + read_test "$oflag" +done + +# success, all done +status=0 +exit diff --git a/tests/generic/450.out b/tests/generic/450.out new file mode 100644 index 00000000..707e2f37 --- /dev/null +++ b/tests/generic/450.out @@ -0,0 +1,11 @@ +QA output created by 450 +buffer read the first sector within EOF +buffer read the second block contains EOF +buffer read a sector at (after) EOF +buffer read the last sector past EOF +buffer read at far away from EOF +direct read the first sector within EOF +direct read the second block contains EOF +direct read a sector at (after) EOF +direct read the last sector past EOF +direct read at far away from EOF diff --git a/tests/generic/group b/tests/generic/group index b9cd0e85..044ec3f3 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -452,3 +452,4 @@ 447 auto quick clone 448 auto quick rw 449 auto quick acl enospc +450 auto quick rw