FSTests Archive on lore.kernel.org
 help / color / Atom feed
From: "Darrick J. Wong" <darrick.wong@oracle.com>
To: Eryu Guan <guaneryu@gmail.com>
Cc: fstests@vger.kernel.org, xfs <linux-xfs@vger.kernel.org>
Subject: Re: [PATCH] generic: test race between appending AIO DIO and fallocate
Date: Mon, 2 Dec 2019 08:28:48 -0800
Message-ID: <20191202162848.GB7335@magnolia> (raw)
In-Reply-To: <20191201142824.GI8664@desktop>

On Sun, Dec 01, 2019 at 10:28:39PM +0800, Eryu Guan wrote:
> On Tue, Nov 12, 2019 at 06:44:16PM -0800, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Dave Chinner reports[1] that an appending AIO DIO write to the second
> > block of a zero-length file and an fallocate request to the first block
> > of the same file can race to set isize, with the user-visible end result
> > that the file size is set incorrectly to one block long.  Write a small
> > test to reproduce the results.
> > 
> > [1] https://lore.kernel.org/linux-xfs/20191029100342.GA41131@bfoster/T/
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  .../aio-dio-append-write-fallocate-race.c          |  212 ++++++++++++++++++++
> 
> I added an entry in .gitignore for it.
> 
> >  tests/generic/722                                  |   43 ++++
> >  tests/generic/722.out                              |    2 
> >  tests/generic/group                                |    1 
> >  4 files changed, 258 insertions(+)
> >  create mode 100644 src/aio-dio-regress/aio-dio-append-write-fallocate-race.c
> >  create mode 100755 tests/generic/722
> >  create mode 100644 tests/generic/722.out
> > 
> > diff --git a/src/aio-dio-regress/aio-dio-append-write-fallocate-race.c b/src/aio-dio-regress/aio-dio-append-write-fallocate-race.c
> > new file mode 100644
> > index 00000000..091b047d
> > --- /dev/null
> > +++ b/src/aio-dio-regress/aio-dio-append-write-fallocate-race.c
> > @@ -0,0 +1,212 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-newer
> > +/*
> > + * Copyright (c) 2019 Oracle.
> > + * All Rights Reserved.
> > + *
> > + * Race appending aio dio and fallocate to make sure we get the correct file
> > + * size afterwards.
> > + */
> > +#include <stdio.h>
> > +#include <pthread.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +#include <errno.h>
> > +#include <libaio.h>
> > +#include <stdlib.h>
> > +#include <stdbool.h>
> > +#include <limits.h>
> > +
> > +static int fd;
> > +static int blocksize;
> > +
> > +static void *
> > +falloc_thread(
> > +	void		*p)
> > +{
> > +	int		ret;
> > +
> > +	ret = fallocate(fd, 0, 0, blocksize);
> > +	if (ret)
> > +		perror("falloc");
> > +
> > +	return NULL;
> > +}
> > +
> > +static int
> > +test(
> > +	const char	*fname,
> > +	unsigned int	iteration,
> > +	unsigned int	*passed)
> > +{
> > +	struct stat	sbuf;
> > +	pthread_t	thread;
> > +	io_context_t	ioctx = 0;
> > +	struct iocb	iocb;
> > +	struct iocb	*iocbp = &iocb;
> > +	struct io_event	event;
> > +	char		*buf;
> > +	bool		wait_thread = false;
> > +	int		ret;
> > +
> > +	/* Truncate file, allocate resources for doing IO. */
> > +	fd = open(fname, O_DIRECT | O_RDWR | O_TRUNC | O_CREAT, 0644);
> > +	if (fd < 0) {
> > +		perror(fname);
> > +		return -1;
> > +	}
> > +
> > +	ret = fstat(fd, &sbuf);
> > +	if (ret) {
> > +		perror(fname);
> > +		goto out;
> > +	}
> > +	blocksize = sbuf.st_blksize;
> > +
> > +	ret = posix_memalign((void **)&buf, blocksize, blocksize);
> > +	if (ret) {
> > +		errno = ret;
> > +		perror("buffer");
> > +		goto out;
> > +	}
> > +	memset(buf, 'X', blocksize);
> > +	memset(&event, 0, sizeof(event));
> > +
> > +	ret = io_queue_init(1, &ioctx);
> > +	if (ret) {
> > +		errno = -ret;
> > +		perror("io_queue_init");
> > +		goto out_buf;
> > +	}
> > +
> > +	/*
> > +	 * Set ourselves up to race fallocate(0..blocksize) with aio dio
> > +	 * pwrite(blocksize..blocksize * 2).  This /should/ give us a file
> > +	 * with length (2 * blocksize).
> > +	 */
> > +	io_prep_pwrite(&iocb, fd, buf, blocksize, blocksize);
> > +
> > +	ret = pthread_create(&thread, NULL, falloc_thread, NULL);
> > +	if (ret) {
> > +		errno = ret;
> > +		perror("pthread");
> > +		goto out_io;
> > +	}
> > +	wait_thread = true;
> > +
> > +	ret = io_submit(ioctx, 1, &iocbp);
> > +	if (ret != 1) {
> > +		errno = -ret;
> > +		perror("io_submit");
> > +		goto out_join;
> > +	}
> > +
> > +	ret = io_getevents(ioctx, 1, 1, &event, NULL);
> > +	if (ret != 1) {
> > +		errno = -ret;
> > +		perror("io_getevents");
> > +		goto out_join;
> > +	}
> > +
> > +	if (event.res < 0) {
> > +		errno = -event.res;
> > +		perror("io_event.res");
> > +		goto out_join;
> > +	}
> > +
> > +	if (event.res2 < 0) {
> > +		errno = -event.res2;
> > +		perror("io_event.res2");
> > +		goto out_join;
> > +	}
> > +
> > +	wait_thread = false;
> > +	ret = pthread_join(thread, NULL);
> > +	if (ret) {
> > +		errno = ret;
> > +		perror("join");
> > +		goto out_io;
> > +	}
> > +
> > +	/* Make sure we actually got a file of size (2 * blocksize). */
> > +	ret = fstat(fd, &sbuf);
> > +	if (ret) {
> > +		perror(fname);
> > +		goto out_buf;
> > +	}
> > +
> > +	if (sbuf.st_size != 2 * blocksize) {
> > +		fprintf(stderr, "[%u]: sbuf.st_size=%llu, expected %llu.\n",
> > +				iteration,
> > +				(unsigned long long)sbuf.st_size,
> > +				(unsigned long long)2 * blocksize);
> > +	} else {
> > +		printf("[%u]: passed.\n", iteration);
> > +		(*passed)++;
> > +	}
> > +
> > +out_join:
> > +	if (wait_thread) {
> > +		ret = pthread_join(thread, NULL);
> > +		if (ret) {
> > +			errno = ret;
> > +			perror("join");
> > +			goto out_io;
> > +		}
> > +	}
> > +out_io:
> > +	ret = io_queue_release(ioctx);
> > +	if (ret) {
> > +		errno = -ret;
> > +		perror("io_queue_release");
> > +	}
> > +
> > +out_buf:
> > +	free(buf);
> > +out:
> > +	ret = close(fd);
> > +	fd = -1;
> > +	if (ret) {
> > +		perror("close");
> > +		return -1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +	int		ret;
> > +	long		l;
> > +	unsigned int	i;
> > +	unsigned int	passed = 0;
> > +
> > +	if (argc != 3) {
> > +		printf("Usage: %s filename iterations\n", argv[0]);
> > +		return 1;
> > +	}
> > +
> > +	errno = 0;
> > +	l = strtol(argv[2], NULL, 0);
> > +	if (errno) {
> > +		perror(argv[2]);
> > +		return 1;
> > +	}
> > +	if (l < 1 || l > UINT_MAX) {
> > +		fprintf(stderr, "%ld: must be between 1 and %u.\n",
> > +				l, UINT_MAX);
> > +		return 1;
> > +	}
> > +
> > +	for (i = 0; i < l; i++) {
> > +		ret = test(argv[1], i, &passed);
> > +		if (ret)
> > +			return 1;
> > +	}
> > +
> > +	printf("pass rate: %u/%u (%.2f%%)\n", passed, i, 100.0 * passed / i);
> > +
> > +	return 0;
> > +}
> > diff --git a/tests/generic/722 b/tests/generic/722
> > new file mode 100755
> > index 00000000..937abf36
> > --- /dev/null
> > +++ b/tests/generic/722
> > @@ -0,0 +1,43 @@
> > +#! /bin/bash
> > +# SPDX-License-Identifier: GPL-2.0-or-later
> > +# Copyright (c) 2019, Oracle and/or its affiliates.  All Rights Reserved.
> > +#
> > +# FS QA Test No. 722
> > +#
> > +# Race an appending aio dio write to the second block of a file while
> > +# simultaneously fallocating to the first block.  Make sure that we end up
> > +# with a two-block file.
> > +
> > +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.* $testfile
> > +}
> > +
> > +# get standard environment, filters and checks
> > +. ./common/rc
> > +
> > +# real QA test starts here
> > +_supported_os Linux
> > +_supported_fs generic
> > +_require_aiodio "aio-dio-append-write-fallocate-race"
> > +_require_test
> 
> Also added
> 
> _require_xfs_io_command "falloc"

Thanks for fixing these. :)

--D

> Thanks,
> Eryu
> 
> > +
> > +rm -f $seqres.full
> > +
> > +testfile=$TEST_DIR/test-$seq
> > +$AIO_TEST $testfile 100 >> $seqres.full
> > +
> > +echo Silence is golden.
> > +# success, all done
> > +status=0
> > +exit
> > diff --git a/tests/generic/722.out b/tests/generic/722.out
> > new file mode 100644
> > index 00000000..8621a87d
> > --- /dev/null
> > +++ b/tests/generic/722.out
> > @@ -0,0 +1,2 @@
> > +QA output created by 722
> > +Silence is golden.
> > diff --git a/tests/generic/group b/tests/generic/group
> > index e5d0c1da..308f86f2 100644
> > --- a/tests/generic/group
> > +++ b/tests/generic/group
> > @@ -588,3 +588,4 @@
> >  583 auto quick encrypt
> >  584 auto quick encrypt
> >  585 auto rename
> > +722 auto quick rw falloc

      reply index

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-13  2:44 Darrick J. Wong
2019-12-01 14:28 ` Eryu Guan
2019-12-02 16:28   ` Darrick J. Wong [this message]

Reply instructions:

You may reply publically to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191202162848.GB7335@magnolia \
    --to=darrick.wong@oracle.com \
    --cc=fstests@vger.kernel.org \
    --cc=guaneryu@gmail.com \
    --cc=linux-xfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

FSTests Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/fstests/0 fstests/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 fstests fstests/ https://lore.kernel.org/fstests \
		fstests@vger.kernel.org
	public-inbox-index fstests

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.fstests


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git