From patchwork Tue Mar 12 09:19:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zorro Lang X-Patchwork-Id: 10848855 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ED1B01669 for ; Tue, 12 Mar 2019 09:20:36 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D5EEF2945A for ; Tue, 12 Mar 2019 09:20:36 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C6A33294B2; Tue, 12 Mar 2019 09:20:36 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6E28E2945A for ; Tue, 12 Mar 2019 09:20:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726396AbfCLJUB (ORCPT ); Tue, 12 Mar 2019 05:20:01 -0400 Received: from mx1.redhat.com ([209.132.183.28]:39548 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726677AbfCLJUA (ORCPT ); Tue, 12 Mar 2019 05:20:00 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CB4FA30842AB for ; Tue, 12 Mar 2019 09:19:59 +0000 (UTC) Received: from dhcp-12-114.nay.redhat.com (dhcp-12-114.nay.redhat.com [10.66.12.114]) by smtp.corp.redhat.com (Postfix) with ESMTP id A499766075; Tue, 12 Mar 2019 09:19:56 +0000 (UTC) From: Zorro Lang To: fstests@vger.kernel.org Cc: fsorenso@redhat.com, lczerner@redhat.com Subject: [PATCH] generic: unaligned direct AIO write test Date: Tue, 12 Mar 2019 17:19:44 +0800 Message-Id: <20190312091944.2044-1-zlang@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.40]); Tue, 12 Mar 2019 09:19:59 +0000 (UTC) Sender: fstests-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: fstests@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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_prep_pwrite(iocbs[2], fd, buf[2], 1048576, 66060800); io_prep_pwrite(iocbs[3], fd, buf[3], 1048576, 67109376); io_submit(io_ctx, 1, &iocbs[0]); io_submit(io_ctx, 1, &iocbs[1]); io_submit(io_ctx, 1, &iocbs[2]); io_submit(io_ctx, 1, &iocbs[3]); io_getevents(io_ctx, 4, 4, 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. Signed-off-by: Zorro Lang --- Hi, The aio-dio-write-verify.c nearly copy from aio-dio-eof-race.c, then change a few logic. I've changed aio-dio-eof-race.c several times, to fit different new AIO test cases. But this time I need to truncate the file, then it won't write at EOF. This against the motive of aio-dio-eof-race.c. So I think it's time to shift a new program to do more AIO write test. It's only my personal opinion :) Thanks, Zorro src/aio-dio-regress/aio-dio-write-verify.c | 223 +++++++++++++++++++++ tests/generic/999 | 71 +++++++ tests/generic/999.out | 2 + tests/generic/group | 1 + 4 files changed, 297 insertions(+) create mode 100644 src/aio-dio-regress/aio-dio-write-verify.c create mode 100755 tests/generic/999 create mode 100644 tests/generic/999.out diff --git a/src/aio-dio-regress/aio-dio-write-verify.c b/src/aio-dio-regress/aio-dio-write-verify.c new file mode 100644 index 00000000..402ddcdb --- /dev/null +++ b/src/aio-dio-regress/aio-dio-write-verify.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Red Hat, Inc. All Rights reserved. + * + * AIO writes from a start offset on a truncated file, verify there's not + * data corruption. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +unsigned long buf_size = 0; +unsigned long size_KB = 0; +#define IO_PATTERN 0x5a + +void +usage(char *progname) +{ + fprintf(stderr, "usage: %s [-s datasize] [-b bufsize] [-o startoff] [-t truncsize] filename\n" + "\t-s datasize: specify the minimum data size(KB), doesn't count holes\n" + "\t-b bufsize: buffer size\n" + "\t-o startoff: start offset to write data, 0 by default\n" + "\t-t truncsize: truncate the file to a special size at first 0 by default\n", + progname); + exit(1); +} + +void +dump_buffer( + void *buf, + off64_t offset, + ssize_t len) +{ + int i, j; + char *p; + int new; + + for (i = 0, p = (char *)buf; i < len; i += 16) { + char *s = p; + + if (i && !memcmp(p, p - 16, 16)) { + new = 0; + } else { + if (i) + printf("*\n"); + new = 1; + } + + if (!new) { + p += 16; + continue; + } + + printf("%08llx ", (unsigned long long)offset + i); + for (j = 0; j < 16 && i + j < len; j++, p++) + printf("%02x ", *p); + printf(" "); + for (j = 0; j < 16 && i + j < len; j++, s++) { + if (isalnum((int)*s)) + printf("%c", *s); + else + printf("."); + } + printf("\n"); + + } + printf("%08llx\n", (unsigned long long)offset + i); +} + +int main(int argc, char *argv[]) +{ + struct io_context *ctx = NULL; + struct io_event evs[2]; + struct iocb iocb1, iocb2; + struct iocb *iocbs[] = { &iocb1, &iocb2 }; + void *buf; + int fd, err = 0; + off_t bytes; + int c; + char *cmp_buf = NULL; + char *filename = NULL; + /* start offset to write */ + long long startoff = 0; + /* truncate size */ + off_t tsize = 0; + + while ((c = getopt(argc, argv, "s:b:o:t:")) != -1) { + char *endp; + + switch (c) { + case 's': /* XXX MB size will be extended */ + size_KB = strtol(optarg, &endp, 0); + break; + case 'b': /* buffer size */ + buf_size = strtol(optarg, &endp, 0); + break; + case 'o': /* start offset */ + startoff = strtoll(optarg, &endp, 0); + break; + case 't': /* initial truncate size */ + tsize = strtoul(optarg, &endp, 0); + break; + default: + usage(argv[0]); + } + } + + if (size_KB == 0) /* default size is 64KB */ + size_KB = 64; + if (buf_size < 2048) /* default minimum buffer size is 2048 bytes */ + buf_size = 2048; + + if (optind == argc - 1) + filename = argv[optind]; + else + usage(argv[0]); + + fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600); + if (fd == -1) { + perror("open"); + return 1; + } + + /* truncate the file to a special size at first */ + if (tsize != 0) { + ftruncate(fd, tsize); + if (fd == -1) { + perror("ftruncate"); + return 1; + } + } + + err = posix_memalign(&buf, getpagesize(), buf_size); + if (err) { + fprintf(stderr, "error %s during %s\n", + strerror(err), + "posix_memalign"); + return 1; + } + cmp_buf = malloc(buf_size); + memset(cmp_buf, IO_PATTERN, buf_size); + + err = io_setup(5, &ctx); + if (err) { + fprintf(stderr, "error %s during %s\n", + strerror(err), + "io_setup"); + return 1; + } + + bytes = 0; + /* Keep extending until size_KB */ + while (bytes < size_KB * 1024) { + ssize_t sret; + int i; + + memset(buf, IO_PATTERN, buf_size); + + io_prep_pwrite(&iocb1, fd, buf, buf_size/2, \ + startoff + bytes + 0*buf_size/2); + io_prep_pwrite(&iocb2, fd, buf, buf_size/2, \ + startoff + bytes + 1*buf_size/2); + + err = io_submit(ctx, 2, iocbs); + if (err != 2) { + fprintf(stderr, "error %s during %s\n", + strerror(err), + "io_submit"); + return 1; + } + + err = io_getevents(ctx, 2, 2, evs, NULL); + if (err != 2) { + fprintf(stderr, "error %s during %s\n", + strerror(err), + "io_getevents"); + return 1; + } + + for (i = 0; i < err; i++) { + /* + * res is unsigned for some reason, so this is the best + * way to detect that it contains a negative errno. + */ + if (evs[i].res > buf_size / 2) { + fprintf(stderr, "pwrite: %s\n", + strerror(-evs[i].res)); + return 1; + } + } + fsync(fd); + + /* + * And then read it back, then compare with original content. + */ + sret = pread(fd, buf, buf_size, startoff + bytes); + if (sret == -1) { + perror("pread"); + return 1; + } else if (sret != buf_size) { + fprintf(stderr, "short read %zd was less than %zu\n", + sret, buf_size); + return 1; + } + if (memcmp(buf, cmp_buf, buf_size)) { + printf("Find corruption\n"); + dump_buffer(buf, 0, buf_size); + return 1; + } + + /* Calculate how many bytes we've written after pread */ + bytes += buf_size; + } + + return 0; +} diff --git a/tests/generic/999 b/tests/generic/999 new file mode 100755 index 00000000..6fcc593a --- /dev/null +++ b/tests/generic/999 @@ -0,0 +1,71 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019 Red Hat, Inc. All Rights Reserved. +# +# FS QA Test No. 999 +# +# Non-page-aligned direct AIO write test. AIO write from unalinged offset +# on a file with different initial truncate i_size. +# +# Uncover "ext4: Fix data corruption caused by unaligned direct AIO" +# +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 +_require_test_program "feature" + +localfile=$TEST_DIR/tst-aio-dio-testfile +diosize=`_min_dio_alignment $TEST_DEV` +pagesize=`src/feature -s` +bufsize=$((pagesize * 2)) +filesize=$((bufsize * 3 / 1024)) + +# Need smaller logical block size to do non-page-aligned test +if [ $diosize -ge $pagesize ];then + _notrun "Need device logical block size($diosize) < page size($pagesize)" +fi + +rm -rf $localfile 2>/dev/null +# page-aligned aiodio write verification at first +$AIO_TEST -s $((bufsize * 10)) -b $bufsize $localfile + +# non-page-aligned aiodio write verification +i=0 +while [ $((diosize * i)) -lt $bufsize ];do + truncsize=$((diosize * i++)) + # non-page-aligned AIO write on different i_size file + $AIO_TEST -s $filesize -b $bufsize -o $diosize -t $truncsize $localfile + if [ $? -ne 0 ];then + echo "[FAIL] $AIO_TEST -s $filesize -b $bufsize -o $diosize -t $truncsize $localfile" + fi +done + +echo "Silence is golden" + +# success, all done +status=0 +exit diff --git a/tests/generic/999.out b/tests/generic/999.out new file mode 100644 index 00000000..3b276ca8 --- /dev/null +++ b/tests/generic/999.out @@ -0,0 +1,2 @@ +QA output created by 999 +Silence is golden diff --git a/tests/generic/group b/tests/generic/group index 15227b67..b1f93d99 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -534,3 +534,4 @@ 529 auto quick attr 530 auto quick unlink 531 auto quick unlink +999 auto quick aio