Files
apfstests/tests/generic/538
T
Zorro Lang ed8463509b generic: unaligned direct AIO write test
A simply reproducer from Frank Sorenson:

  ftruncate(fd, 65012224)
  io_prep_pwrite(iocbs[0], fd, buf[0], 1048576, 63963648);
  io_prep_pwrite(iocbs[1], fd, buf[1], 1048576, 65012224);

  io_submit(io_ctx, 1, &iocbs[0]);
  io_submit(io_ctx, 1, &iocbs[1]);

  io_getevents(io_ctx, 2, 2, events, NULL)

help to find an ext4 corruption:
           **************** **************** ****************
           *    page 1    * *    page 2    * *    page 3    *
           **************** **************** ****************
  existing 0000000000000000 0000000000000000 0000000000000000
  write 1    AAAAAAAAAAAAAA AA
  write 2                     BBBBBBBBBBBBBB BB

  result   00AAAAAAAAAAAAAA 00BBBBBBBBBBBBBB BB00000000000000
  desired  00AAAAAAAAAAAAAA AABBBBBBBBBBBBBB BB00000000000000

This issue remind us we might miss unaligned AIO test for long time.
We thought fsx cover this part, but looks like it's not. So this case
trys to cover unaligned direct AIO write test on file with different
initial truncate i_size.

The following patches fix the issue on xfs and ext4.

xfs: serialize unaligned dio writes against all other dio writes
ext4: Fix data corruption caused by unaligned direct AIO

Signed-off-by: Zorro Lang <zlang@redhat.com>
Reviewed-by: Lukas Czerner <lczerner@redhat.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
2019-04-06 19:14:59 +08:00

96 lines
2.9 KiB
Bash
Executable File

#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2019 Red Hat, Inc. All Rights Reserved.
#
# FS QA Test No. 538
#
# Non-block-aligned direct AIO write test with an initial truncate i_size.
#
# Uncover "ext4: Fix data corruption caused by unaligned direct AIO":
# (Ext4 needs to serialize unaligned direct AIO because the zeroing of
# partial blocks of two competing unaligned AIOs can result in data
# corruption.
#
# However it decides not to serialize if the potentially unaligned aio is
# past i_size with the rationale that no pending writes are possible past
# i_size. Unfortunately if the i_size is not block aligned and the second
# unaligned write lands past i_size, but still into the same block, it has
# the potential of corrupting the previous unaligned write to the same
# block.)
#
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.*
}
# get standard environment, filters and checks
. ./common/rc
. ./common/filter
# remove previous $seqres.full before test
rm -f $seqres.full
# real QA test starts here
_supported_fs generic
_supported_os Linux
_require_test
_require_aiodio aio-dio-write-verify
localfile=$TEST_DIR/${seq}-aio-dio-write-verify-testfile
diosize=`_min_dio_alignment $TEST_DEV`
blocksize=`_get_block_size $TEST_DIR`
bufsize=$((blocksize * 2))
truncsize=$((bufsize+diosize))
# Need smaller logical block size to do non-block-aligned test
if [ $diosize -ge $blocksize ];then
_notrun "Need device logical block size($diosize) < fs block size($blocksize)"
fi
rm -rf $localfile 2>/dev/null
# block-aligned aiodio write verification at first
$AIO_TEST -a size=$bufsize,off=0 -a size=$bufsize,off=$bufsize $localfile
# non-block-aligned aiodio write verification
# **************** **************** ****************
# * block 1&2 * * block 3&4 * * block 5&6 *
# **************** **************** ****************
# existing 0000000000000000 0000000000000000 0000000000000000
# truncate ---------------->|
# write 1 ZZZZZZZZZZZZZZZ Z
# write 2 |<---- ZZZZZZZZZZZZZZZ Z ---->|
#
# "Write 1" writes 2 blocks data at off=$diosize.
# "Write 2" seeks from 0 to "Write 1" end + block size, shift $diosize bytes each
# time, writes 2 blocksize data too.
# Verify there's not corruption each time.
i=0
while [ $((diosize * i)) -lt $((diosize + bufsize + blocksize)) ];do
position=$((diosize * i++))
# non-block-aligned AIO write on different i_size file
$AIO_TEST -t $truncsize -a size=$bufsize,off=$diosize \
-a size=$bufsize,off=$position \
$localfile
if [ $? -ne 0 ];then
echo "FAIL: [$truncsize, $bufsize, $diosize, $position]"
echo "-------------------------------------------------"
fi
rm -f $localfile
done
echo "Silence is golden"
# success, all done
status=0
exit