generic: test read around EOF
authorZorro Lang <zlang@redhat.com>
Thu, 3 Aug 2017 14:22:25 +0000 (22:22 +0800)
committerEryu Guan <eguan@redhat.com>
Tue, 15 Aug 2017 06:37:59 +0000 (14:37 +0800)
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>
tests/generic/450 [new file with mode: 0755]
tests/generic/450.out [new file with mode: 0644]
tests/generic/group

diff --git a/tests/generic/450 b/tests/generic/450
new file mode 100755 (executable)
index 0000000..a430b85
--- /dev/null
@@ -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 (file)
index 0000000..707e2f3
--- /dev/null
@@ -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
index b9cd0e85d1ca44f97a56c67f11a2abe75f2e99db..044ec3f355ed03b429f63f8a33199bd28f2527c0 100644 (file)
 447 auto quick clone
 448 auto quick rw
 449 auto quick acl enospc
+450 auto quick rw