Re: [PATCH] generic: test race between appending AIO DIO and fallocate

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Nov 12, 2019 at 06:44:16PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
> 
> 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@xxxxxxxxxx>
> ---
>  .../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,
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



[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux