Re: [PATCH] generic: add fcntl corner cases tests

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



On Wed, Aug 23, 2023 at 05:08:14PM -0400, Alexander Aring wrote:
> This patch adds generic 730 testcase. It will test on various fcntl()

The generic/730 has been taken. The case number might be changed when I merge
a patch, so you don't need to specify a fixed number in commit log or comments.

> corner cases that was making problems on a GFS2 filesystem. GFS2 has
> it's own lock() implementation that has it's own posix lock
> implementation behind it. There are testcases to find issues with struct
> file_lock matches. Currently the Linux kernel does not have a unique
> identifier per lock request to e.g. find the original lock request when
> a complete handler of an async lock request comes back. The current way
> is to use struct file_lock fields to fine the original lock request.
> However there was issues being found that in some cases it wasn't an
> unique match because multiple pending struct file_lock could have the
> same state. To find issues the testcases fcntl_lock_equal_file_lock and
> fcntl_lock_same_owner are introduced and their OFD variants.
> 
> Other test like fcntl_lock_kill_child tests cleanup routines when a
> process blocking in F_SETLKW to wait the lock request getting granted
> and the process gets killed.
> 
> A similar test is fcntl_lock_signal_interrupt which checks for
> side-effects e.g. unlock all previous acquired locks when a blocking
> F_SETLKW gets interrupted by a signal.
> 
> Signed-off-by: Alexander Aring <aahringo@xxxxxxxxxx>
> ---

Is there a known issue cause a deadlock or something else? I tried to run this
case on XFS, but it's always blocked on fcntl_lock_same_owner test[1], even 1
day passed, it's still there.

[1]
[root@xxxxxx ~]# ps aux|grep fcntl
root       72361  0.0  0.0   3056  1536 pts/1    S+   12:10   0:00 /root/git/xfstests/src/fcntl_lock_same_owner /mnt/test/testfile
root       72362  0.0  0.0  19580  1280 pts/1    Sl+  12:10   0:00 /root/git/xfstests/src/fcntl_lock_same_owner /mnt/test/testfile
root       72405  0.0  0.0   6760  2048 pts/2    S+   12:12   0:00 grep --color=auto fcntl
[root@xxxxxx ~]# cat /proc/72361/stack
[<0>] do_wait+0x160/0x2f0
[<0>] kernel_wait4+0xb8/0x160
[<0>] __do_sys_wait4+0xa2/0xb0
[<0>] do_syscall_64+0x60/0x90
[<0>] entry_SYSCALL_64_after_hwframe+0x73/0xdd
[root@xxxxxx ~]# cat /proc/72362/stack
[<0>] futex_wait_queue+0x63/0x90
[<0>] futex_wait+0x189/0x270
[<0>] do_futex+0xc6/0x190
[<0>] __x64_sys_futex+0x129/0x1e0
[<0>] do_syscall_64+0x60/0x90
[<0>] entry_SYSCALL_64_after_hwframe+0x73/0xdd


>  src/Makefile                         |   5 +-
>  src/fcntl_lock_equal_file_lock.c     | 140 +++++++++++++++++++++++
>  src/fcntl_lock_equal_file_lock_ofd.c | 144 ++++++++++++++++++++++++
>  src/fcntl_lock_kill_child.c          | 148 +++++++++++++++++++++++++
>  src/fcntl_lock_same_owner.c          | 146 ++++++++++++++++++++++++
>  src/fcntl_lock_same_owner_ofd.c      | 144 ++++++++++++++++++++++++
>  src/fcntl_lock_signal_interrupt.c    | 159 +++++++++++++++++++++++++++
>  tests/generic/730                    |  70 ++++++++++++
>  tests/generic/730.out                |   2 +

The g/730 has been taken.

>  9 files changed, 957 insertions(+), 1 deletion(-)
>  create mode 100644 src/fcntl_lock_equal_file_lock.c
>  create mode 100644 src/fcntl_lock_equal_file_lock_ofd.c
>  create mode 100644 src/fcntl_lock_kill_child.c
>  create mode 100644 src/fcntl_lock_same_owner.c
>  create mode 100644 src/fcntl_lock_same_owner_ofd.c
>  create mode 100644 src/fcntl_lock_signal_interrupt.c
>  create mode 100755 tests/generic/730
>  create mode 100644 tests/generic/730.out
> 

[snip]

> diff --git a/src/fcntl_lock_same_owner.c b/src/fcntl_lock_same_owner.c
> new file mode 100644
> index 00000000..071a3b49
> --- /dev/null
> +++ b/src/fcntl_lock_same_owner.c
> @@ -0,0 +1,146 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * This program tests fcntl() operations that have two
> + * fcntl() calls in waiting state in two different threads.
> + * Those fcntl() ends in a struct file_lock have the same fl_owner
> + * field. One fcntl() call gets granted, there will be a verifier
> + * going on if the right lock was granted.
> + */
> +
> +#include <sys/wait.h>
> +#include <pthread.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdio.h>
> +
> +static const char *filename;
> +static int fd;
> +
> +static void *do_same_owner_thread0(void *arg)
> +{
> +	struct flock fl = {
> +		.l_type = F_WRLCK,
> +		.l_whence = SEEK_SET,
> +		.l_start = 0L,
> +		.l_len = 1L,
> +	};
> +	int rv;
> +
> +	rv = fcntl(fd, F_SETLKW, &fl);
> +	if (rv == -1)
> +		_exit(1);
> +
> +	return NULL;
> +}
> +
> +static void *do_same_owner_thread1(void *arg)
> +{
> +	struct flock fl = {
> +		.l_type = F_WRLCK,
> +		.l_whence = SEEK_SET,
> +		.l_start = 1L,
> +		.l_len = 1L,
> +	};
> +	int rv;
> +
> +	rv = fcntl(fd, F_SETLKW, &fl);
> +	if (rv == -1)
> +		_exit(1);
> +
> +	return NULL;
> +}
> +
> +static void do_setup()
> +{
> +	fd = open(filename, O_RDWR | O_CREAT, 0700);
> +	if (fd == -1)
> +		_exit(1);
> +}
> +
> +static void do_teardown()
> +{
> +	close(fd);
> +}
> +
> +static void do_same_owner()
> +{
> +	struct flock fl = {
> +		.l_type = F_WRLCK,
> +		.l_whence = SEEK_SET,
> +		.l_start = 0L,
> +		.l_len = 2L,
> +	};
> +	pthread_t t[2];
> +	int pid, rv;
> +
> +	rv = fcntl(fd, F_SETLK, &fl);
> +	if (rv == -1)
> +		_exit(1);
> +
> +	pid = fork();
> +	if (pid == 0) {
> +		rv = pthread_create(&t[0], NULL, do_same_owner_thread0, NULL);
> +		if (rv != 0)
> +			_exit(1);
> +
> +		rv = pthread_create(&t[1], NULL, do_same_owner_thread1, NULL);
> +		if (rv != 0)
> +			_exit(1);
> +
> +		pthread_join(t[0], NULL);
> +		pthread_join(t[1], NULL);
> +
> +		_exit(0);
> +	}
> +
> +	/* wait threads should block */
> +	sleep(3);
> +
> +	fl.l_type = F_UNLCK;
> +	fl.l_start = 1;
> +	fl.l_len = 1;
> +	rv = fcntl(fd, F_SETLK, &fl);
> +	if (rv == -1)
> +		_exit(1);
> +
> +	sleep(3);
> +
> +	/* check if the lock() implementation got the
> +	 * right lock because two waiter with the
> +	 * same fl_owner were waiting.
> +	 */
> +	fl.l_type = F_WRLCK;
> +	rv = fcntl(fd, F_SETLK, &fl);
> +	if (!(rv == -1 && errno == EAGAIN))
> +		_exit(1);
> +
> +	fl.l_type = F_UNLCK;
> +	fl.l_start = 1;
> +	fl.l_len = 1;
> +	rv = fcntl(fd, F_SETLK, &fl);
> +	if (rv == -1)
> +		_exit(1);
> +
> +	wait(NULL);
> +}
> +
> +static void usage(const char *argv0)
> +{
> +	fprintf(stderr, "Usage: %s {filename}\n", argv0);
> +	_exit(1);
> +}
> +
> +int main(int argc, const char *argv[])
> +{
> +	if (argc != 2)
> +		usage(argv[0]);
> +
> +	filename = argv[1];
> +
> +	do_setup();
> +	do_same_owner();
> +	do_teardown();
> +
> +	return 0;
> +}

[snip]

> diff --git a/tests/generic/730 b/tests/generic/730
> new file mode 100755
> index 00000000..4a1e7f20
> --- /dev/null
> +++ b/tests/generic/730
> @@ -0,0 +1,70 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2023 Alexander Aring.  All Rights Reserved.
> +#
> +# FS QA Test 730
> +#
> +# This tests performs some fcntl() corner cases when
> +# two waiter have the same or some (fl_owner) fields. In Linux
> +# there exists no unique lock request identifier, some lock()
> +# filesystem implementation does that over struct file_lock fields.
> +# Other tests check for bad side-effects if a blocking F_SETLKW lock
> +# request got interrupted or the process got killed.
> +#
> +. ./common/preamble
> +_begin_fstest auto quick
> +
> +# Import common functions.
> +. ./common/filter
> +
> +# real QA test starts here
> +
> +_supported_fs generic
> +_require_test
> +_require_test_program fcntl_lock_equal_file_lock
> +_require_test_program fcntl_lock_equal_file_lock_ofd
> +_require_test_program fcntl_lock_kill_child
> +_require_test_program fcntl_lock_same_owner
> +_require_test_program fcntl_lock_same_owner_ofd
> +_require_test_program fcntl_lock_signal_interrupt

Does these test programs try to do different test? You might want to
split them to seperated generic cases, or combine these .c into
one program?

> +
> +echo "Silence is golden"
> +
> +$here/src/fcntl_lock_equal_file_lock $TEST_DIR/testfile
> +if [ $? -ne 0 ]
> +then
> +	exit

So it's still the question, does these programs test independently or they
have progressive relationship? When a test fails, should we test others or
ignore others directly?

And is there more debug info output if a test fails? To figure out which test
fails and fail on where? From above .c programs, I didn't see some designed
printf, and if we exit directly at here, how to know which test fails?

Thanks,
Zorro

> +fi
> +
> +$here/src/fcntl_lock_equal_file_lock_ofd $TEST_DIR/testfile
> +if [ $? -ne 0 ]
> +then
> +	exit
> +fi
> +
> +$here/src/fcntl_lock_kill_child $TEST_DIR/testfile
> +if [ $? -ne 0 ]
> +then
> +	exit
> +fi
> +
> +$here/src/fcntl_lock_same_owner $TEST_DIR/testfile
> +if [ $? -ne 0 ]
> +then
> +	exit
> +fi
> +
> +$here/src/fcntl_lock_same_owner_ofd $TEST_DIR/testfile
> +if [ $? -ne 0 ]
> +then
> +	exit
> +fi
> +
> +$here/src/fcntl_lock_signal_interrupt $TEST_DIR/testfile
> +if [ $? -ne 0 ]
> +then
> +	exit
> +fi
> +
> +status=0
> +exit
> diff --git a/tests/generic/730.out b/tests/generic/730.out
> new file mode 100644
> index 00000000..50c3c832
> --- /dev/null
> +++ b/tests/generic/730.out
> @@ -0,0 +1,2 @@
> +QA output created by 730
> +Silence is golden
> -- 
> 2.31.1
> 




[Index of Archives]     [Linux Filesystems Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux