Skip to contentSkip to conversation

add a new test for racing AIO COW writes

PATCH Christoph Hellwig 3 participants 6 messages Oct 3, 2017 – Oct 9, 2017

lore
Share & tools
lore
#0 lore raw

This can be used to trigger an assert in the current XFS code because it can't handle the case where there are COW extents on a file, but none at or below the range converted by the AIO completion handler.

Note that it doesn't trigger the assert 100% but fairly reliably.

src/aio-dio-regress/aio-dio-cow-race.c new file +119 -0
new file mode 100755
index 0000000..c7f8e89
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Christoph Hellwig.  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
+ */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include <libaio.h>
+
+#ifndef FICLONE
+#define FICLONE		_IOW(0x94, 9, int)
+#endif
+
+#define IO_PATTERN	0xab
+
+int main(int argc, char *argv[])
+{
+	struct io_context *ctx = NULL;
+	struct io_event evs[4];
+	struct iocb iocb1, iocb2, iocb3, iocb4;
+	struct iocb *iocbs[] = { &iocb1, &iocb2, &iocb3, &iocb4 };
+	void *buf;
+	int fd, clone, err = 0;
+	unsigned long buf_size = getpagesize() * 2;
+	char *filename, *clonename;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s filename clonename\n", argv[0]);
+		exit(1);
+	}
+
+	err = posix_memalign(&buf, getpagesize(), buf_size);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "posix_memalign");
+		return 1;
+	}
+	memset(buf, IO_PATTERN, buf_size);
+
+	filename = argv[1];
+	fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (fd == -1) {
+		perror("open");
+		return 1;
+	}
+
+	if (write(fd, buf, buf_size) != buf_size) {
+		perror("write");
+		return 1;
+	}
+
+	clonename = argv[2];
+	clone = open(clonename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (clone == -1) {
+		perror("open clone");
+		return 1;
+	}
+
+	if (ioctl(clone, FICLONE, fd)) {
+		perror("FICLONE");
+		return 1;
+	}
+
+	err = io_setup(4, &ctx);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err),
+			"io_setup");
+		return 1;
+	}
+
+	/*
+	 * Test overlapping aio writes, where an earlier one clears the whole
+	 * range of a later aio, thus leaving nothing to do for the I/O
+	 * completion to do.  To make things harder also make sure there is
+	 * other outstanding COW I/O.
+	 */
+	io_prep_pwrite(&iocb1, fd, buf, buf_size, 0);
+	io_prep_pwrite(&iocb2, fd, buf, buf_size / 2, 0);
+	io_prep_pwrite(&iocb3, fd, buf, buf_size / 2, buf_size);
+	io_prep_pwrite(&iocb4, fd, buf, buf_size, 0);
+
+	err = io_submit(ctx, 4, iocbs);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_submit");
+		return 1;
+	}
+
+	err = io_getevents(ctx, 4, 4, evs, NULL);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_getevents");
+		return 1;
+	}
+
+	return 0;
+}
tests/generic/463 new file +60 -0

60 lines not on this page · raw message

tests/generic/group +1 -0
index f2a6cda..014d6ea 100644
@@ -465,3 +465,4 @@
 460 auto quick rw
 461 auto shutdown stress
 462 auto quick dax
+463 auto quick reflink dangerous
Reviewed-by
#1 lore raw
Quoted 204 lines · Christoph Hellwig

This can be used to trigger an assert in the current XFS code because it can't handle the case where there are COW extents on a file, but none at or below the range converted by the AIO completion handler.

Note that it doesn't trigger the assert 100% but fairly reliably.

src/aio-dio-regress/aio-dio-cow-race.c new file +119 -0
new file mode 100755
index 0000000..c7f8e89
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Christoph Hellwig.  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
+ */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include <libaio.h>
+
+#ifndef FICLONE
+#define FICLONE		_IOW(0x94, 9, int)
+#endif
+
+#define IO_PATTERN	0xab
+
+int main(int argc, char *argv[])
+{
+	struct io_context *ctx = NULL;
+	struct io_event evs[4];
+	struct iocb iocb1, iocb2, iocb3, iocb4;
+	struct iocb *iocbs[] = { &iocb1, &iocb2, &iocb3, &iocb4 };
+	void *buf;
+	int fd, clone, err = 0;
+	unsigned long buf_size = getpagesize() * 2;
+	char *filename, *clonename;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s filename clonename\n", argv[0]);
+		exit(1);
+	}
+
+	err = posix_memalign(&buf, getpagesize(), buf_size);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "posix_memalign");
+		return 1;
+	}
+	memset(buf, IO_PATTERN, buf_size);
+
+	filename = argv[1];
+	fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (fd == -1) {
+		perror("open");
+		return 1;
+	}
+
+	if (write(fd, buf, buf_size) != buf_size) {
+		perror("write");
+		return 1;
+	}
+
+	clonename = argv[2];
+	clone = open(clonename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (clone == -1) {
+		perror("open clone");
+		return 1;
+	}
+
+	if (ioctl(clone, FICLONE, fd)) {
+		perror("FICLONE");
+		return 1;
+	}
+
+	err = io_setup(4, &ctx);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err),
+			"io_setup");
+		return 1;
+	}
+
+	/*
+	 * Test overlapping aio writes, where an earlier one clears the whole
+	 * range of a later aio, thus leaving nothing to do for the I/O
+	 * completion to do.  To make things harder also make sure there is
+	 * other outstanding COW I/O.
+	 */
+	io_prep_pwrite(&iocb1, fd, buf, buf_size, 0);
+	io_prep_pwrite(&iocb2, fd, buf, buf_size / 2, 0);
+	io_prep_pwrite(&iocb3, fd, buf, buf_size / 2, buf_size);
+	io_prep_pwrite(&iocb4, fd, buf, buf_size, 0);
+
+	err = io_submit(ctx, 4, iocbs);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_submit");
+		return 1;
+	}
+
+	err = io_getevents(ctx, 4, 4, evs, NULL);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_getevents");
+		return 1;
+	}
+
+	return 0;
+}
tests/generic/463 new file +60 -0
new file mode 100755
index 0000000..b3afbb0
@@ -0,0 +1,60 @@
+#! /bin/bash
+# FS QA Test 463
+#
+# Test racy COW AIO write completions.
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2017 Christoph Hellwig.  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 $TEST_DIR/file $TEST_DIR/clone
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/reflink
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+_supported_fs generic
+_supported_os Linux
+
+_require_test
+_require_test_reflink
+_require_aio
+_require_odirect
+
+$here/src/aio-dio-regress/aio-dio-cow-race $TEST_DIR/file $TEST_DIR/clone
+
+echo "Silence is golden"
+status=0
+exit
tests/generic/group +0 -0
index f2a6cda..014d6ea 100644

tests/generic/group line 468

Quoted 4 lines
tests/generic/group +1 -0
@@ -465,3 +465,4 @@
 460 auto quick rw
 461 auto shutdown stress
 462 auto quick dax
+463 auto quick reflink dangerous

'clone', not 'reflink'.

Otherwise looks ok,

--D

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#2 lore raw

add a new test for racing AIO COW writes V2

In reply to Christoph Hellwig

This can be used to trigger an assert in the current XFS code because it can't handle the case where there are COW extents on a file, but none at or below the range converted by the AIO completion handler.

src/aio-dio-regress/aio-dio-cow-race.c new file +119 -0
new file mode 100755
index 0000000..c7f8e89
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Christoph Hellwig.  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
+ */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include <libaio.h>
+
+#ifndef FICLONE
+#define FICLONE		_IOW(0x94, 9, int)
+#endif
+
+#define IO_PATTERN	0xab
+
+int main(int argc, char *argv[])
+{
+	struct io_context *ctx = NULL;
+	struct io_event evs[4];
+	struct iocb iocb1, iocb2, iocb3, iocb4;
+	struct iocb *iocbs[] = { &iocb1, &iocb2, &iocb3, &iocb4 };
+	void *buf;
+	int fd, clone, err = 0;
+	unsigned long buf_size = getpagesize() * 2;
+	char *filename, *clonename;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s filename clonename\n", argv[0]);
+		exit(1);
+	}
+
+	err = posix_memalign(&buf, getpagesize(), buf_size);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "posix_memalign");
+		return 1;
+	}
+	memset(buf, IO_PATTERN, buf_size);
+
+	filename = argv[1];
+	fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (fd == -1) {
+		perror("open");
+		return 1;
+	}
+
+	if (write(fd, buf, buf_size) != buf_size) {
+		perror("write");
+		return 1;
+	}
+
+	clonename = argv[2];
+	clone = open(clonename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (clone == -1) {
+		perror("open clone");
+		return 1;
+	}
+
+	if (ioctl(clone, FICLONE, fd)) {
+		perror("FICLONE");
+		return 1;
+	}
+
+	err = io_setup(4, &ctx);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err),
+			"io_setup");
+		return 1;
+	}
+
+	/*
+	 * Test overlapping aio writes, where an earlier one clears the whole
+	 * range of a later aio, thus leaving nothing to do for the I/O
+	 * completion to do.  To make things harder also make sure there is
+	 * other outstanding COW I/O.
+	 */
+	io_prep_pwrite(&iocb1, fd, buf, buf_size, 0);
+	io_prep_pwrite(&iocb2, fd, buf, buf_size / 2, 0);
+	io_prep_pwrite(&iocb3, fd, buf, buf_size / 2, buf_size);
+	io_prep_pwrite(&iocb4, fd, buf, buf_size, 0);
+
+	err = io_submit(ctx, 4, iocbs);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_submit");
+		return 1;
+	}
+
+	err = io_getevents(ctx, 4, 4, evs, NULL);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_getevents");
+		return 1;
+	}
+
+	return 0;
+}
tests/generic/463 new file +60 -0

60 lines not on this page · raw message

tests/generic/group +1 -0
index f2a6cda..014d6ea 100644
@@ -465,3 +465,4 @@
 460 auto quick rw
 461 auto shutdown stress
 462 auto quick dax
+463 auto quick clone dangerous
Reviewed-by
#3 lore raw

Re: add a new test for racing AIO COW writes V2

Quoted 5 lines · Christoph Hellwig

This can be used to trigger an assert in the current XFS code because it can't handle the case where there are COW extents on a file, but none at or below the range converted by the AIO completion handler.

Looks ok,

Quoted 205 lines
src/aio-dio-regress/aio-dio-cow-race.c new file +119 -0
new file mode 100755
index 0000000..c7f8e89
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Christoph Hellwig.  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
+ */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include <libaio.h>
+
+#ifndef FICLONE
+#define FICLONE		_IOW(0x94, 9, int)
+#endif
+
+#define IO_PATTERN	0xab
+
+int main(int argc, char *argv[])
+{
+	struct io_context *ctx = NULL;
+	struct io_event evs[4];
+	struct iocb iocb1, iocb2, iocb3, iocb4;
+	struct iocb *iocbs[] = { &iocb1, &iocb2, &iocb3, &iocb4 };
+	void *buf;
+	int fd, clone, err = 0;
+	unsigned long buf_size = getpagesize() * 2;
+	char *filename, *clonename;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s filename clonename\n", argv[0]);
+		exit(1);
+	}
+
+	err = posix_memalign(&buf, getpagesize(), buf_size);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "posix_memalign");
+		return 1;
+	}
+	memset(buf, IO_PATTERN, buf_size);
+
+	filename = argv[1];
+	fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (fd == -1) {
+		perror("open");
+		return 1;
+	}
+
+	if (write(fd, buf, buf_size) != buf_size) {
+		perror("write");
+		return 1;
+	}
+
+	clonename = argv[2];
+	clone = open(clonename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (clone == -1) {
+		perror("open clone");
+		return 1;
+	}
+
+	if (ioctl(clone, FICLONE, fd)) {
+		perror("FICLONE");
+		return 1;
+	}
+
+	err = io_setup(4, &ctx);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err),
+			"io_setup");
+		return 1;
+	}
+
+	/*
+	 * Test overlapping aio writes, where an earlier one clears the whole
+	 * range of a later aio, thus leaving nothing to do for the I/O
+	 * completion to do.  To make things harder also make sure there is
+	 * other outstanding COW I/O.
+	 */
+	io_prep_pwrite(&iocb1, fd, buf, buf_size, 0);
+	io_prep_pwrite(&iocb2, fd, buf, buf_size / 2, 0);
+	io_prep_pwrite(&iocb3, fd, buf, buf_size / 2, buf_size);
+	io_prep_pwrite(&iocb4, fd, buf, buf_size, 0);
+
+	err = io_submit(ctx, 4, iocbs);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_submit");
+		return 1;
+	}
+
+	err = io_getevents(ctx, 4, 4, evs, NULL);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_getevents");
+		return 1;
+	}
+
+	return 0;
+}
tests/generic/463 new file +60 -0
new file mode 100755
index 0000000..b3afbb0
@@ -0,0 +1,60 @@
+#! /bin/bash
+# FS QA Test 463
+#
+# Test racy COW AIO write completions.
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2017 Christoph Hellwig.  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 $TEST_DIR/file $TEST_DIR/clone
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/reflink
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+_supported_fs generic
+_supported_os Linux
+
+_require_test
+_require_test_reflink
+_require_aio
+_require_odirect
+
+$here/src/aio-dio-regress/aio-dio-cow-race $TEST_DIR/file $TEST_DIR/clone
+
+echo "Silence is golden"
+status=0
+exit
tests/generic/group +1 -1
index f2a6cda..014d6ea 100644
@@ -465,3 +465,4 @@
 460 auto quick rw
 461 auto shutdown stress
 462 auto quick dax
+463 auto quick clone dangerous
--

To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#4 lore raw

Re: add a new test for racing AIO COW writes V2

In reply to Christoph Hellwig
Quoted 5 lines · Christoph Hellwig

This can be used to trigger an assert in the current XFS code because it can't handle the case where there are COW extents on a file, but none at or below the range converted by the AIO completion handler.

I fixed several minor issues on commit (see inline comments).

I haven't reproduced the assert failure yet on my test vm, but as the commit log in v1 patch, it's not 100% reproducible, maybe I'm just lucky (or not lucky :)

src/aio-dio-regress/aio-dio-cow-race.c

Quoted 6 lines
src/aio-dio-regress/aio-dio-cow-race.c new file +0 -0
new file mode 100755
index 0000000..c7f8e89

Added new gitignore entry for this new binary file.

Quoted 80 lines
src/aio-dio-regress/aio-dio-cow-race.c new file +79 -0
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Christoph Hellwig.  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
+ */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include <libaio.h>
+
+#ifndef FICLONE
+#define FICLONE		_IOW(0x94, 9, int)
+#endif
+
+#define IO_PATTERN	0xab
+
+int main(int argc, char *argv[])
+{
+	struct io_context *ctx = NULL;
+	struct io_event evs[4];
+	struct iocb iocb1, iocb2, iocb3, iocb4;
+	struct iocb *iocbs[] = { &iocb1, &iocb2, &iocb3, &iocb4 };
+	void *buf;
+	int fd, clone, err = 0;
+	unsigned long buf_size = getpagesize() * 2;
+	char *filename, *clonename;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s filename clonename\n", argv[0]);
+		exit(1);
+	}
+
+	err = posix_memalign(&buf, getpagesize(), buf_size);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "posix_memalign");
+		return 1;
+	}
+	memset(buf, IO_PATTERN, buf_size);
+
+	filename = argv[1];
+	fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (fd == -1) {
+		perror("open");
+		return 1;
+	}
+
+	if (write(fd, buf, buf_size) != buf_size) {
+		perror("write");
+		return 1;
+	}
+
+	clonename = argv[2];
+	clone = open(clonename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+	if (clone == -1) {
+		perror("open clone");
+		return 1;
+	}
+

src/aio-dio-regress/aio-dio-cow-race.c line 89

Quoted 10 lines
src/aio-dio-regress/aio-dio-cow-race.c new file +10 -0
+	if (ioctl(clone, FICLONE, fd)) {
+		perror("FICLONE");
+		return 1;
+	}
+
+	err = io_setup(4, &ctx);
+	if (err) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err),
+			"io_setup");

This error message format looks weired, I changed it to

		fprintf(stderr, "io_setup error %s\n", strerror(err));

Did the same to io_submit and io_getevents.

Quoted 80 lines
src/aio-dio-regress/aio-dio-cow-race.c new file +30 -0
+		return 1;
+	}
+
+	/*
+	 * Test overlapping aio writes, where an earlier one clears the whole
+	 * range of a later aio, thus leaving nothing to do for the I/O
+	 * completion to do.  To make things harder also make sure there is
+	 * other outstanding COW I/O.
+	 */
+	io_prep_pwrite(&iocb1, fd, buf, buf_size, 0);
+	io_prep_pwrite(&iocb2, fd, buf, buf_size / 2, 0);
+	io_prep_pwrite(&iocb3, fd, buf, buf_size / 2, buf_size);
+	io_prep_pwrite(&iocb4, fd, buf, buf_size, 0);
+
+	err = io_submit(ctx, 4, iocbs);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_submit");
+		return 1;
+	}
+
+	err = io_getevents(ctx, 4, 4, evs, NULL);
+	if (err != 4) {
+		fprintf(stderr, "error %s during %s\n",
+			strerror(err), "io_getevents");
+		return 1;
+	}
+
+	return 0;
+}
tests/generic/463 new file +44 -0
new file mode 100755
index 0000000..b3afbb0
@@ -0,0 +1,60 @@
+#! /bin/bash
+# FS QA Test 463
+#
+# Test racy COW AIO write completions.
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2017 Christoph Hellwig.  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 $TEST_DIR/file $TEST_DIR/clone
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/reflink
+

tests/generic/463 line 54

Quoted 10 lines
tests/generic/463 new file +10 -0
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+_supported_fs generic
+_supported_os Linux
+
+_require_test
+_require_test_reflink
+_require_aio
+_require_odirect

Need _require_test_program "aio-dio-regress/aio-dio-cow-race"

But a single '_require_aiodio "aio-dio-cow-race"' did all the three requirements check above.

tests/generic/463 line 56

Quoted 2 lines
tests/generic/463 new file +2 -0
+
+$here/src/aio-dio-regress/aio-dio-cow-race $TEST_DIR/file $TEST_DIR/clone

With _require_aiodio, we can use $AIO_TEST here for the test binary.

tests/generic/463 line 58

Quoted 2 lines
tests/generic/463 new file +2 -0
+
+echo "Silence is golden"

Added generic/463.out file too.

Thanks,
Eryu

Quoted 15 lines
tests/generic/463 new file +2 -0
+status=0
+exit
tests/generic/group +1 -1
index f2a6cda..014d6ea 100644
@@ -465,3 +465,4 @@
 460 auto quick rw
 461 auto shutdown stress
 462 auto quick dax
+463 auto quick clone dangerous
--

To unsubscribe from this list: send the line "unsubscribe fstests" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#5 lore raw

Re: add a new test for racing AIO COW writes V2

Quoted 5 lines · Eryu Guan
Quoted 4 lines · Christoph Hellwig

This can be used to trigger an assert in the current XFS code because it can't handle the case where there are COW extents on a file, but none at or below the range converted by the AIO completion handler.

Quoted 7 lines
Quoted 1 line

I fixed several minor issues on commit (see inline comments).

I haven't reproduced the assert failure yet on my test vm, but as the commit log in v1 patch, it's not 100% reproducible, maybe I'm just lucky (or not lucky :)

Hmm, I only got 1 non-reproduce out of about 100 runs. Maybe your device is too fast, as this was in a VM.