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 <zlang@redhat.com>
Reviewed-by: Eryu Guan <eguan@redhat.com>
Signed-off-by: Eryu Guan <eguan@redhat.com>
This commit is contained in:
Zorro Lang
2017-08-03 22:22:25 +08:00
committed by Eryu Guan
parent 101f0c34ef
commit 4939477f3f
3 changed files with 175 additions and 0 deletions
+163
View File
@@ -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
+11
View File
@@ -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
+1
View File
@@ -452,3 +452,4 @@
447 auto quick clone
448 auto quick rw
449 auto quick acl enospc
450 auto quick rw