* [PATCH] generic: add OFD lock tests @ 2017-10-11 8:42 Xiong Zhou 2017-10-13 9:13 ` [PATCH v2] " Xiong Zhou 0 siblings, 1 reply; 11+ messages in thread From: Xiong Zhou @ 2017-10-11 8:42 UTC (permalink / raw) To: fstests; +Cc: Xiong Zhou The basic idea is placing a type of lock, eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl getlk with a type of lock, eg RDLCK, checking the returned flock struct to see if it works fine. We can also test these situations: that the two locks are conflicting or not; that open testfile RDONLY or RDWR. Signed-off-by: Xiong Zhou <xzhou@redhat.com> --- .gitignore | 1 + src/Makefile | 3 +- src/t_ofd_locks.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/generic/463 | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/generic/463.out | 2 + tests/generic/group | 1 + 6 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 src/t_ofd_locks.c create mode 100755 tests/generic/463 create mode 100644 tests/generic/463.out diff --git a/.gitignore b/.gitignore index ae7ef87..2824c3d 100644 --- a/.gitignore +++ b/.gitignore @@ -126,6 +126,7 @@ /src/t_mmap_write_ro /src/t_mmap_writev /src/t_mtab +/src/t_ofd_locks /src/t_readdir_1 /src/t_readdir_2 /src/t_rename_overwrite diff --git a/src/Makefile b/src/Makefile index 3eb25b1..f37af74 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ + t_ofd_locks LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c new file mode 100644 index 0000000..8723e8e --- /dev/null +++ b/src/t_ofd_locks.c @@ -0,0 +1,129 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> + +void err_exit(int fd, char *op) +{ + fprintf(stderr, "%s: %s\n", op, strerror(errno)); + if (fd > 0) + close(fd); + exit(errno); +} + +void set_lock(int fd, struct flock *flkp) +{ + if (fcntl(fd, F_OFD_SETLKW, flkp) < 0) + err_exit(fd, "ofd_setlkw"); +} + +void get_lock(int fd, struct flock *flkp) +{ + if (fcntl(fd, F_OFD_GETLK, flkp) < 0) + err_exit(fd, "ofd_getlk"); +} + +void wait_exit(int fd) +{ + sleep(1); + close(fd); + exit(0); +} + +int main(int argc, char **argv) +{ + int fd; + int lock_cmd; + int lock_rw; + int lock_start = 0; + int open_rw = 1; + + struct flock flk = { + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 10, /* lock range [0,9] */ + .l_type = F_RDLCK, + }; + + /* lock_cmd : 1 <--> setlk + * 0 <--> getlk + * + * lock_rw : 1 <--> set/get wrlck + * 0 <--> set/get rdlck + * + * lock_start : l_start to getlk + * + * open_rw : 1 <--> open file RDWR + * 0 <--> open file RDONLY + */ + + if (argc < 5) { + printf("Usage: %s filename lock_cmd lock_rw lock_start open_rw\n", argv[0]); + return 1; + } + + lock_cmd = atoi(argv[2]); + lock_rw = atoi(argv[3]); + lock_start = atoi(argv[4]); + open_rw = atoi(argv[5]); + + if (open_rw == 0) + fd = open(argv[1], O_RDONLY); + else + fd = open(argv[1], O_RDWR); + + if (fd < 0) + err_exit(fd, "open"); + + /* set rdlck */ + if (lock_cmd == 1 && lock_rw == 0) { + + flk.l_type = F_RDLCK; + set_lock(fd, &flk); + wait_exit(fd); + } + + /* set wrlck */ + if (lock_cmd == 1 && lock_rw == 1) { + + flk.l_type = F_WRLCK; + set_lock(fd, &flk); + wait_exit(fd); + } + + /* getlck */ + if (lock_cmd == 0) { + + if (lock_rw == 1) + flk.l_type = F_WRLCK; + else + flk.l_type = F_RDLCK; + + flk.l_start = lock_start; + + get_lock(fd, &flk); + close(fd); + + switch (flk.l_type) { + case F_UNLCK: + return 1; // lock can be placed + case F_RDLCK: + return 2; // got rdlck + case F_WRLCK: + return 3; // got wrlck + default: + return 0; + } + } + + close(fd); + return 0; +} diff --git a/tests/generic/463 b/tests/generic/463 new file mode 100755 index 0000000..d85d86b --- /dev/null +++ b/tests/generic/463 @@ -0,0 +1,133 @@ +#! /bin/bash +# FS QA Test 463 +# +# test OFD locks, F_OFD_SETLK/F_OFD_GETLK +# +#----------------------------------------------------------------------- +# Copyright (c) 2017 Red Hat Inc. 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.* +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# remove previous $seqres.full before test +rm -f $seqres.full + +# Modify as appropriate. +_supported_fs generic +_supported_os Linux +_require_scratch +_require_test_program "t_ofd_locks" + +# real QA test starts here +_scratch_mkfs >> $seqres.full 2>&1 + +# prepare a 4k test file +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ + $SCRATCH_MNT/testfile >> $seqres.full 2>&1 + +function do_test() +{ + # set_rw : 1 <--> set wrlck + # 0 <--> set rdlck + local set_rw=$1 + + # get_rw : 1 <--> get wrlck + # 0 <--> get rdlck + local get_rw=$2 + + # start : l_start to getlk + local start=$3 + + # rw : 1 <--> open file RDWR + # 0 <--> open file RDONLY + local rw=$4 + + # rs : expected getlk return value + # 1 <--> lock can be placed + # 2 <--> get conflict rdlck + # 3 <--> get conflict wrlck + local rs=$5 + + # setlk and wait + src/t_ofd_locks $SCRATCH_MNT/testfile 1 $set_rw $start $rw & + # check the lock, we've set lock range as [0,9] + if ! grep -q "OFD.*0 *9" /proc/locks ; then + echo setlk failed $* + wait + return + fi + # getlk + src/t_ofd_locks $SCRATCH_MNT/testfile 0 $get_rw $start $rw + ret=$? + echo $ret >> $seqres.full + # checking return value + [ $ret -ne $rs ] && echo $ret fail $* + + wait +} + +# Always setlk at range [0,9], getlk at range [0,9] or [20,29] +# To open file RDONLY or RDWR should not break the locks. + +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck +do_test 1 1 0 1 3 +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck +do_test 1 1 20 1 1 +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck +do_test 1 0 0 1 3 +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck +do_test 1 0 20 1 1 +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck +do_test 0 1 0 0 2 +do_test 0 1 0 1 2 +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck +do_test 0 1 20 0 1 +do_test 0 1 20 1 1 +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck +do_test 0 0 0 0 1 +do_test 0 0 0 1 1 +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck +do_test 0 0 20 0 1 +do_test 0 0 20 1 1 + +# getlk with wrlck only, expect unlck +src/t_ofd_locks $SCRATCH_MNT/testfile 0 1 0 1 +ret=$? +echo $ret >> $seqres.full +[ $ret -ne 1 ] && echo $ret Lock could be placed + +# success, all done +echo "Silence is golden" +status=0 +exit diff --git a/tests/generic/463.out b/tests/generic/463.out new file mode 100644 index 0000000..dd61371 --- /dev/null +++ b/tests/generic/463.out @@ -0,0 +1,2 @@ +QA output created by 463 +Silence is golden diff --git a/tests/generic/group b/tests/generic/group index f2a6cda..9031e84 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -465,3 +465,4 @@ 460 auto quick rw 461 auto shutdown stress 462 auto quick dax +463 auto quick -- 1.8.3.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2] generic: add OFD lock tests 2017-10-11 8:42 [PATCH] generic: add OFD lock tests Xiong Zhou @ 2017-10-13 9:13 ` Xiong Zhou 2017-10-16 10:28 ` Eryu Guan 0 siblings, 1 reply; 11+ messages in thread From: Xiong Zhou @ 2017-10-13 9:13 UTC (permalink / raw) To: fstests; +Cc: Xiong Zhou The basic idea is placing a type of lock, eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl getlk with a type of lock, eg RDLCK, checking the returned flock struct to see if it works fine. We can also test these situations: that the two locks are conflicting or not; that open testfile RDONLY or RDWR. Signed-off-by: Xiong Zhou <xzhou@redhat.com> --- v2: add helper checking if OFD lock is available. mv simple getlk WRLCK test to the helper. add macro definitions in c programme to avoid build errors. .gitignore | 1 + common/rc | 8 +++ src/Makefile | 3 +- src/t_ofd_locks.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/generic/463 | 128 ++++++++++++++++++++++++++++++++++++++++++ tests/generic/463.out | 2 + tests/generic/group | 1 + 7 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 src/t_ofd_locks.c create mode 100755 tests/generic/463 create mode 100644 tests/generic/463.out diff --git a/.gitignore b/.gitignore index ae7ef87..2824c3d 100644 --- a/.gitignore +++ b/.gitignore @@ -126,6 +126,7 @@ /src/t_mmap_write_ro /src/t_mmap_writev /src/t_mtab +/src/t_ofd_locks /src/t_readdir_1 /src/t_readdir_2 /src/t_rename_overwrite diff --git a/common/rc b/common/rc index 53bbb11..79f97a4 100644 --- a/common/rc +++ b/common/rc @@ -3310,6 +3310,14 @@ _require_test_fcntl_advisory_locks() _notrun "Require fcntl advisory locks support" } +_require_scratch_fcntl_ofd_locks() +{ + # getlk with wrlck only, expect unlck + touch $SCRATCH_MNT/testfile + src/t_ofd_locks $SCRATCH_MNT/testfile 0 1 0 1 + [ $? -ne 1 ] && _notrun "Require fcntl OFD locks support" +} + _require_test_lsattr() { testio=$(lsattr -d $TEST_DIR 2>&1) diff --git a/src/Makefile b/src/Makefile index 3eb25b1..f37af74 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ + t_ofd_locks LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c new file mode 100644 index 0000000..2aab48b --- /dev/null +++ b/src/t_ofd_locks.c @@ -0,0 +1,150 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> + +/* + * In distributions that do not have these macros ready in + * glibc-headers, compilation fails. Adding them here to avoid + * build errors, relevant tests would fail at the helper which + * requires OFD locks support and notrun if the kernel does not + * support OFD locks. If the kernel does support OFD locks, + * we are good to go. + * + */ +#ifndef F_OFD_GETLK +#define F_OFD_GETLK 36 +#endif + +#ifndef F_OFD_SETLK +#define F_OFD_SETLK 37 +#endif + +#ifndef F_OFD_SETLKW +#define F_OFD_SETLKW 38 +#endif + +void err_exit(int fd, char *op) +{ + fprintf(stderr, "%s: %s\n", op, strerror(errno)); + if (fd > 0) + close(fd); + exit(errno); +} + +void set_lock(int fd, struct flock *flkp) +{ + if (fcntl(fd, F_OFD_SETLKW, flkp) < 0) + err_exit(fd, "ofd_setlkw"); +} + +void get_lock(int fd, struct flock *flkp) +{ + if (fcntl(fd, F_OFD_GETLK, flkp) < 0) + err_exit(fd, "ofd_getlk"); +} + +void wait_exit(int fd) +{ + sleep(1); + close(fd); + exit(0); +} + +int main(int argc, char **argv) +{ + int fd; + int lock_cmd; + int lock_rw; + int lock_start = 0; + int open_rw = 1; + + struct flock flk = { + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 10, /* lock range [0,9] */ + .l_type = F_RDLCK, + }; + + /* lock_cmd : 1 <--> setlk + * 0 <--> getlk + * + * lock_rw : 1 <--> set/get wrlck + * 0 <--> set/get rdlck + * + * lock_start : l_start to getlk + * + * open_rw : 1 <--> open file RDWR + * 0 <--> open file RDONLY + */ + + if (argc < 5) { + printf("Usage: %s filename lock_cmd lock_rw lock_start open_rw\n", argv[0]); + return 1; + } + + lock_cmd = atoi(argv[2]); + lock_rw = atoi(argv[3]); + lock_start = atoi(argv[4]); + open_rw = atoi(argv[5]); + + if (open_rw == 0) + fd = open(argv[1], O_RDONLY); + else + fd = open(argv[1], O_RDWR); + + if (fd < 0) + err_exit(fd, "open"); + + /* set rdlck */ + if (lock_cmd == 1 && lock_rw == 0) { + + flk.l_type = F_RDLCK; + set_lock(fd, &flk); + wait_exit(fd); + } + + /* set wrlck */ + if (lock_cmd == 1 && lock_rw == 1) { + + flk.l_type = F_WRLCK; + set_lock(fd, &flk); + wait_exit(fd); + } + + /* getlck */ + if (lock_cmd == 0) { + + if (lock_rw == 1) + flk.l_type = F_WRLCK; + else + flk.l_type = F_RDLCK; + + flk.l_start = lock_start; + + get_lock(fd, &flk); + close(fd); + + switch (flk.l_type) { + case F_UNLCK: + return 1; // lock can be placed + case F_RDLCK: + return 2; // got rdlck + case F_WRLCK: + return 3; // got wrlck + default: + return 0; + } + } + + close(fd); + return 0; +} diff --git a/tests/generic/463 b/tests/generic/463 new file mode 100755 index 0000000..ad4f0dc --- /dev/null +++ b/tests/generic/463 @@ -0,0 +1,128 @@ +#! /bin/bash +# FS QA Test 463 +# +# test OFD locks, F_OFD_SETLK/F_OFD_GETLK +# +#----------------------------------------------------------------------- +# Copyright (c) 2017 Red Hat Inc. 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.* +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# remove previous $seqres.full before test +rm -f $seqres.full + +# Modify as appropriate. +_supported_fs generic +_supported_os Linux +_require_scratch +_require_test_program "t_ofd_locks" +_require_scratch_fcntl_ofd_locks + +# real QA test starts here +_scratch_mkfs >> $seqres.full 2>&1 + +# prepare a 4k test file +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ + $SCRATCH_MNT/testfile >> $seqres.full 2>&1 + +function do_test() +{ + # set_rw : 1 <--> set wrlck + # 0 <--> set rdlck + local set_rw=$1 + + # get_rw : 1 <--> get wrlck + # 0 <--> get rdlck + local get_rw=$2 + + # start : l_start to getlk + local start=$3 + + # rw : 1 <--> open file RDWR + # 0 <--> open file RDONLY + local rw=$4 + + # rs : expected getlk return value + # 1 <--> lock can be placed + # 2 <--> get conflict rdlck + # 3 <--> get conflict wrlck + local rs=$5 + + # setlk and wait + src/t_ofd_locks $SCRATCH_MNT/testfile 1 $set_rw $start $rw & + # check the lock, we've set lock range as [0,9] + if ! grep -qE "OFD.*0 *9" /proc/locks ; then + echo setlk failed $* + wait + return + fi + # getlk + src/t_ofd_locks $SCRATCH_MNT/testfile 0 $get_rw $start $rw + ret=$? + echo $ret >> $seqres.full + # checking return value + [ $ret -ne $rs ] && echo $ret fail $* + + wait +} + +# Always setlk at range [0,9], getlk at range [0,9] or [20,29] +# To open file RDONLY or RDWR should not break the locks. + +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck +do_test 1 1 0 1 3 +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck +do_test 1 1 20 1 1 +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck +do_test 1 0 0 1 3 +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck +do_test 1 0 20 1 1 +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck +do_test 0 1 0 0 2 +do_test 0 1 0 1 2 +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck +do_test 0 1 20 0 1 +do_test 0 1 20 1 1 +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck +do_test 0 0 0 0 1 +do_test 0 0 0 1 1 +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck +do_test 0 0 20 0 1 +do_test 0 0 20 1 1 + +# success, all done +echo "Silence is golden" +status=0 +exit diff --git a/tests/generic/463.out b/tests/generic/463.out new file mode 100644 index 0000000..dd61371 --- /dev/null +++ b/tests/generic/463.out @@ -0,0 +1,2 @@ +QA output created by 463 +Silence is golden diff --git a/tests/generic/group b/tests/generic/group index f2a6cda..9031e84 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -465,3 +465,4 @@ 460 auto quick rw 461 auto shutdown stress 462 auto quick dax +463 auto quick -- 1.8.3.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2] generic: add OFD lock tests 2017-10-13 9:13 ` [PATCH v2] " Xiong Zhou @ 2017-10-16 10:28 ` Eryu Guan 2017-10-19 8:30 ` [PATCH v3] " Xiong Zhou 0 siblings, 1 reply; 11+ messages in thread From: Eryu Guan @ 2017-10-16 10:28 UTC (permalink / raw) To: Xiong Zhou; +Cc: fstests On Fri, Oct 13, 2017 at 05:13:50PM +0800, Xiong Zhou wrote: > The basic idea is placing a type of lock, eg WRLCK, on > a testfile in SCRATCH_MNT, then do fcntl getlk with a > type of lock, eg RDLCK, checking the returned flock > struct to see if it works fine. > > We can also test these situations: > that the two locks are conflicting or not; > that open testfile RDONLY or RDWR. > > Signed-off-by: Xiong Zhou <xzhou@redhat.com> > --- > > v2: > add helper checking if OFD lock is available. > mv simple getlk WRLCK test to the helper. > add macro definitions in c programme to avoid build errors. > > .gitignore | 1 + > common/rc | 8 +++ > src/Makefile | 3 +- > src/t_ofd_locks.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++ > tests/generic/463 | 128 ++++++++++++++++++++++++++++++++++++++++++ > tests/generic/463.out | 2 + > tests/generic/group | 1 + > 7 files changed, 292 insertions(+), 1 deletion(-) > create mode 100644 src/t_ofd_locks.c > create mode 100755 tests/generic/463 > create mode 100644 tests/generic/463.out > > diff --git a/.gitignore b/.gitignore > index ae7ef87..2824c3d 100644 > --- a/.gitignore > +++ b/.gitignore > @@ -126,6 +126,7 @@ > /src/t_mmap_write_ro > /src/t_mmap_writev > /src/t_mtab > +/src/t_ofd_locks > /src/t_readdir_1 > /src/t_readdir_2 > /src/t_rename_overwrite > diff --git a/common/rc b/common/rc > index 53bbb11..79f97a4 100644 > --- a/common/rc > +++ b/common/rc > @@ -3310,6 +3310,14 @@ _require_test_fcntl_advisory_locks() > _notrun "Require fcntl advisory locks support" > } > > +_require_scratch_fcntl_ofd_locks() > +{ > + # getlk with wrlck only, expect unlck > + touch $SCRATCH_MNT/testfile > + src/t_ofd_locks $SCRATCH_MNT/testfile 0 1 0 1 > + [ $? -ne 1 ] && _notrun "Require fcntl OFD locks support" > +} > + I think we can work on a file resides in TEST_DIR and rename it to _require_ofd_locks(), as TEST_DIR is always mounted pre-test and OFD is not controlled by any mkfs or mount options. There're multiple places that could return 1 in t_ofd_locks.c, e.g. wrong argument count and F_GETLK returned F_UNLCK. How about checking if t_ofd_locks returned EINVAL? If so then the running kernel doesn't know F_OFD_* lock commands. > _require_test_lsattr() > { > testio=$(lsattr -d $TEST_DIR 2>&1) > diff --git a/src/Makefile b/src/Makefile > index 3eb25b1..f37af74 100644 > --- a/src/Makefile > +++ b/src/Makefile > @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ > multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ > t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ > holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ > - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro > + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ > + t_ofd_locks > > LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ > preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ > diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c > new file mode 100644 > index 0000000..2aab48b > --- /dev/null > +++ b/src/t_ofd_locks.c > @@ -0,0 +1,150 @@ > +#ifndef _GNU_SOURCE > +#define _GNU_SOURCE > +#endif > +#include <unistd.h> > +#include <fcntl.h> > +#include <errno.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <string.h> > +#include <stdlib.h> > +#include <sys/stat.h> > +#include <sys/types.h> > + > +/* > + * In distributions that do not have these macros ready in > + * glibc-headers, compilation fails. Adding them here to avoid > + * build errors, relevant tests would fail at the helper which > + * requires OFD locks support and notrun if the kernel does not > + * support OFD locks. If the kernel does support OFD locks, > + * we are good to go. > + * > + */ > +#ifndef F_OFD_GETLK > +#define F_OFD_GETLK 36 > +#endif > + > +#ifndef F_OFD_SETLK > +#define F_OFD_SETLK 37 > +#endif > + > +#ifndef F_OFD_SETLKW > +#define F_OFD_SETLKW 38 > +#endif > + > +void err_exit(int fd, char *op) > +{ > + fprintf(stderr, "%s: %s\n", op, strerror(errno)); > + if (fd > 0) > + close(fd); > + exit(errno); errno can be changed by close(2) above, you need to save a local copy of errno. > +} > + > +void set_lock(int fd, struct flock *flkp) > +{ > + if (fcntl(fd, F_OFD_SETLKW, flkp) < 0) > + err_exit(fd, "ofd_setlkw"); > +} > + > +void get_lock(int fd, struct flock *flkp) > +{ > + if (fcntl(fd, F_OFD_GETLK, flkp) < 0) > + err_exit(fd, "ofd_getlk"); > +} > + > +void wait_exit(int fd) > +{ > + sleep(1); > + close(fd); > + exit(0); > +} > + > +int main(int argc, char **argv) > +{ > + int fd; > + int lock_cmd; > + int lock_rw; > + int lock_start = 0; > + int open_rw = 1; > + > + struct flock flk = { > + .l_whence = SEEK_SET, > + .l_start = 0, > + .l_len = 10, /* lock range [0,9] */ > + .l_type = F_RDLCK, > + }; > + > + /* lock_cmd : 1 <--> setlk > + * 0 <--> getlk > + * > + * lock_rw : 1 <--> set/get wrlck > + * 0 <--> set/get rdlck > + * > + * lock_start : l_start to getlk > + * > + * open_rw : 1 <--> open file RDWR > + * 0 <--> open file RDONLY > + */ This takes me a few seconds to realize that these comments are describing command line options.. Coule you please make it more obvious? > + > + if (argc < 5) { > + printf("Usage: %s filename lock_cmd lock_rw lock_start open_rw\n", argv[0]); > + return 1; > + } > + > + lock_cmd = atoi(argv[2]); > + lock_rw = atoi(argv[3]); > + lock_start = atoi(argv[4]); > + open_rw = atoi(argv[5]); And it seems that when lock_cmd is 1 (we're setting a lock), the lock_start is not used. And it's hard to remember which argument presents which variable, I'd prefer a getopt(3) parse of arguments, e.g. -f testfile -s set lock -g get lock -r read lock -w write lock -R readonly open -o offset So getting a write lock on testfile with rw open could be: ./src/t_ofd_locks -g -w -f /path/to/testfile I think this is a bit easier to read. > + > + if (open_rw == 0) > + fd = open(argv[1], O_RDONLY); > + else > + fd = open(argv[1], O_RDWR); > + > + if (fd < 0) > + err_exit(fd, "open"); > + > + /* set rdlck */ > + if (lock_cmd == 1 && lock_rw == 0) { > + Extra empty line. > + flk.l_type = F_RDLCK; > + set_lock(fd, &flk); > + wait_exit(fd); > + } > + > + /* set wrlck */ > + if (lock_cmd == 1 && lock_rw == 1) { > + Extra empty line. > + flk.l_type = F_WRLCK; > + set_lock(fd, &flk); > + wait_exit(fd); > + } > + > + /* getlck */ > + if (lock_cmd == 0) { > + Same here. > + if (lock_rw == 1) > + flk.l_type = F_WRLCK; > + else > + flk.l_type = F_RDLCK; > + > + flk.l_start = lock_start; > + > + get_lock(fd, &flk); > + close(fd); > + > + switch (flk.l_type) { > + case F_UNLCK: > + return 1; // lock can be placed > + case F_RDLCK: > + return 2; // got rdlck > + case F_WRLCK: > + return 3; // got wrlck > + default: > + return 0; As mentioned above, there're multiple places that could return 1. And I don't think you need to check the expected return value in test script. How about just printing the result here and let the results match golden output? > + } > + } > + > + close(fd); > + return 0; > +} > diff --git a/tests/generic/463 b/tests/generic/463 > new file mode 100755 > index 0000000..ad4f0dc > --- /dev/null > +++ b/tests/generic/463 > @@ -0,0 +1,128 @@ > +#! /bin/bash > +# FS QA Test 463 > +# > +# test OFD locks, F_OFD_SETLK/F_OFD_GETLK Need more descriptions in the test. > +# > +#----------------------------------------------------------------------- > +# Copyright (c) 2017 Red Hat Inc. 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.* > +} > + > +# get standard environment, filters and checks > +. ./common/rc > +. ./common/filter > + > +# remove previous $seqres.full before test > +rm -f $seqres.full > + > +# Modify as appropriate. > +_supported_fs generic > +_supported_os Linux > +_require_scratch > +_require_test_program "t_ofd_locks" > +_require_scratch_fcntl_ofd_locks > + > +# real QA test starts here > +_scratch_mkfs >> $seqres.full 2>&1 > + > +# prepare a 4k test file > +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ > + $SCRATCH_MNT/testfile >> $seqres.full 2>&1 > + > +function do_test() > +{ > + # set_rw : 1 <--> set wrlck > + # 0 <--> set rdlck > + local set_rw=$1 > + > + # get_rw : 1 <--> get wrlck > + # 0 <--> get rdlck > + local get_rw=$2 > + > + # start : l_start to getlk > + local start=$3 > + > + # rw : 1 <--> open file RDWR > + # 0 <--> open file RDONLY > + local rw=$4 > + > + # rs : expected getlk return value > + # 1 <--> lock can be placed > + # 2 <--> get conflict rdlck > + # 3 <--> get conflict wrlck > + local rs=$5 > + > + # setlk and wait > + src/t_ofd_locks $SCRATCH_MNT/testfile 1 $set_rw $start $rw & In most cases the 1s sleep in t_ofd_locks is enough, but it's still possible process exits before we check /proc/locks. Also, this makes every do_test call spends at least 1s, which makes test run time longer unnecessarily. Better to make t_ofd_locks wait forever until it's told to exit, e.g. check the existence of a given file and only exit when the file is created (or removed). > + # check the lock, we've set lock range as [0,9] > + if ! grep -qE "OFD.*0 *9" /proc/locks ; then Hmm, I think this check is racy. Sometimes I got "setlok failed" error here, and I think that's because this check is scheduled before t_ofd_locks gets the lock. We should make sure the test binary gets the lock first. And check that process ID match the process placed the lock too? > + echo setlk failed $* Dump /proc/locks to $seqres.full too. > + wait > + return > + fi > + # getlk > + src/t_ofd_locks $SCRATCH_MNT/testfile 0 $get_rw $start $rw > + ret=$? > + echo $ret >> $seqres.full Dump all the t_ofd_locks command lines to $seqres.full too for a better understandable debug log. > + # checking return value > + [ $ret -ne $rs ] && echo $ret fail $* As mentioned above, there's no need to check return values, just dump the F_GETLK results to stdout. Thanks, Eryu > + > + wait > +} > + > +# Always setlk at range [0,9], getlk at range [0,9] or [20,29] > +# To open file RDONLY or RDWR should not break the locks. > + > +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck > +do_test 1 1 0 1 3 > +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck > +do_test 1 1 20 1 1 > +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck > +do_test 1 0 0 1 3 > +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck > +do_test 1 0 20 1 1 > +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck > +do_test 0 1 0 0 2 > +do_test 0 1 0 1 2 > +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck > +do_test 0 1 20 0 1 > +do_test 0 1 20 1 1 > +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck > +do_test 0 0 0 0 1 > +do_test 0 0 0 1 1 > +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck > +do_test 0 0 20 0 1 > +do_test 0 0 20 1 1 > + > +# success, all done > +echo "Silence is golden" > +status=0 > +exit > diff --git a/tests/generic/463.out b/tests/generic/463.out > new file mode 100644 > index 0000000..dd61371 > --- /dev/null > +++ b/tests/generic/463.out > @@ -0,0 +1,2 @@ > +QA output created by 463 > +Silence is golden > diff --git a/tests/generic/group b/tests/generic/group > index f2a6cda..9031e84 100644 > --- a/tests/generic/group > +++ b/tests/generic/group > @@ -465,3 +465,4 @@ > 460 auto quick rw > 461 auto shutdown stress > 462 auto quick dax > +463 auto quick > -- > 1.8.3.1 > > -- > To unsubscribe from this list: send the line "unsubscribe fstests" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3] generic: add OFD lock tests 2017-10-16 10:28 ` Eryu Guan @ 2017-10-19 8:30 ` Xiong Zhou 2017-10-24 8:16 ` Eryu Guan 0 siblings, 1 reply; 11+ messages in thread From: Xiong Zhou @ 2017-10-19 8:30 UTC (permalink / raw) To: fstests; +Cc: Xiong Zhou The basic idea is placing a type of lock in one process, eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl getlk with a type of lock, eg RDLCK, in another process. In the end, we check the returned flock.l_type by getlk to see if the lock mechanism works fine. We can also test these situations: that the two locks are conflicting or not; that open testfile RDONLY or RDWR. Signed-off-by: Xiong Zhou <xzhou@redhat.com> --- v3: Use golden output to match test result; Use semaphore to sync getlk/setlk; Add option parse to the test programme; Add a testrun code path for the helper; Add more comments; Skip the not stable /proc/locks check. .gitignore | 1 + common/rc | 11 +++ src/Makefile | 3 +- src/t_ofd_locks.c | 262 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/generic/466 | 119 +++++++++++++++++++++++ tests/generic/466.out | 13 +++ tests/generic/group | 1 + 7 files changed, 409 insertions(+), 1 deletion(-) create mode 100644 src/t_ofd_locks.c create mode 100755 tests/generic/466 create mode 100644 tests/generic/466.out diff --git a/.gitignore b/.gitignore index 2014c08..77acb42 100644 --- a/.gitignore +++ b/.gitignore @@ -126,6 +126,7 @@ /src/t_mmap_write_ro /src/t_mmap_writev /src/t_mtab +/src/t_ofd_locks /src/t_readdir_1 /src/t_readdir_2 /src/t_rename_overwrite diff --git a/common/rc b/common/rc index e2a8229..983a214 100644 --- a/common/rc +++ b/common/rc @@ -3192,6 +3192,17 @@ _require_test_fcntl_advisory_locks() _notrun "Require fcntl advisory locks support" } +_require_ofd_locks() +{ + # Give a test run by setlk/getlk rdlck on testfile. + # If the running kernel does not support OFD locks, + # EINVAL will be returned. + touch $TEST_DIR/ofd_testfile || \ + _notrun "Something wrong with $TEST_DIR" + src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1 + [ $? -eq 22 ] && _notrun "Require OFD locks support" +} + _require_test_lsattr() { testio=$(lsattr -d $TEST_DIR 2>&1) diff --git a/src/Makefile b/src/Makefile index 3eb25b1..f37af74 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ + t_ofd_locks LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c new file mode 100644 index 0000000..f87c9c5 --- /dev/null +++ b/src/t_ofd_locks.c @@ -0,0 +1,262 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> + +/* + * In distributions that do not have these macros ready in + * glibc-headers, compilation fails. Adding them here to avoid + * build errors, relevant tests would fail at the helper which + * requires OFD locks support and notrun if the kernel does not + * support OFD locks. If the kernel does support OFD locks, + * we are good to go. + * + */ +#ifndef F_OFD_GETLK +#define F_OFD_GETLK 36 +#endif + +#ifndef F_OFD_SETLK +#define F_OFD_SETLK 37 +#endif + +#ifndef F_OFD_SETLKW +#define F_OFD_SETLKW 38 +#endif + +/* This is required by semctl to set semaphore value */ +union semun { + int val; /* Value for SETVAL */ + struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* Array for GETALL, SETALL */ + struct seminfo *__buf; /* Buffer for IPC_INFO + (Linux-specific) */ +}; + +int fd; + +void err_exit(char *op, int errn) +{ + fprintf(stderr, "%s: %s\n", op, strerror(errn)); + if (fd > 0) + close(fd); + exit(errn); +} + +int main(int argc, char **argv) +{ + /* Five flags that used to specify operation details. + * They can be specified via command line options. + * + * option: -s/-g + * lock_cmd : 1 <--> setlk + * 0 <--> getlk + * + * option: -r/-w + * lock_rw : 1 <--> set/get wrlck + * 0 <--> set/get rdlck + * + * option: -o num + * lock_start : l_start to getlk + * + * option: -R/-W + * open_rw : 1 <--> open file RDWR + * 0 <--> open file RDONLY + * + * This option is for _require_ofd_locks helper, just do + * fcntl setlk then return errno. + * option: -t + * testrun : 1 <--> this is a testrun, return after setlk + * 0 <--> this is not a testrun, run as usual + */ + + /* By default, we setlk WRLCK on [0,9], opening file RDWR. + */ + + int lock_cmd = 1; + int lock_rw = 1; + int lock_start = 0; + int open_rw = 1; + int testrun = 0; + + /* We use semaphore to synchronize between getlk and setlk. + * + * Although we run getlk routine after running setlk routine + * in background, getlk still can be executed before setlk + * sometimes, which is invalid for our tests. + * + * In setlk routine, we wait getlk done, then exit, making sure + * the lock is still there when doing getlk. + * + * In getlk routine, we wait till setlk done firstly, making + * sure the lock is valid at that time, then we do getlk, + * after that, we tell setlk we are done, then exit. + */ + key_t semkey; + unsigned short vals[2]; + union semun arg; + int semid; + struct sembuf sop; + + struct flock flk = { + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 10, /* lock range [0,9] */ + .l_type = F_RDLCK, + .l_pid = 0, + }; + + int opt; + + while((opt = getopt(argc, argv, "sgrwo:RWt")) != -1) { + switch(opt) { + case 's': + lock_cmd = 1; + break; + case 'g': + lock_cmd = 0; + break; + case 'r': + lock_rw = 0; + break; + case 'w': + lock_rw = 1; + break; + case 'o': + lock_start = atoi(optarg); + break; + case 'R': + open_rw = 0; + break; + case 'W': + open_rw = 1; + break; + case 't': + testrun = 1; + break; + default: + printf("Usage: %s [-sgrwo:RW] filename\n", argv[0]); + return -1; + } + } + + if (lock_rw == 1) + flk.l_type = F_WRLCK; + else + flk.l_type = F_RDLCK; + + if (open_rw == 0) + fd = open(argv[optind], O_RDONLY); + else + fd = open(argv[optind], O_RDWR); + if (fd == -1) + err_exit("open", errno); + + /* In a testun, we do a fcntl getlk call and exit + * immediately no matter it succeeds or not. + */ + if (testrun == 1) { + if (fcntl(fd, F_OFD_GETLK, &flk) < 0) + err_exit("test_ofd_getlk", errno); + if (fd > 0) + close(fd); + exit(0); + } + + /* Init the semaphore, with key related to the same file. + * If the sem set has not been created, we initialnize it. + * If it exists, we semget again to get the exist one. + * To make sure getlk routine and setlk routine are looking + * at the same semaphore set in one single round of test. + */ + if((semkey = ftok("ofd_key", 255)) == -1) + err_exit("ftok", errno); + semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL); + if (semid < 0) { + /* The sem exists or errer happens. */ + if (errno != EEXIST) + err_exit("semget0", errno); + + semid = semget(semkey, 2, 0); + if (semid < 0) + err_exit("semget1", errno); + } else { + /* Init both new sem to 1. */ + vals[0] = 1; + vals[1] = 1; + arg.array = vals; + if (semctl(semid, 2, SETALL, arg) == -1) + err_exit("init sem", errno); + } + + /* setlk */ + if (lock_cmd == 1) { + if (fcntl(fd, F_OFD_SETLKW, &flk) < 0) + err_exit("ofd_setlkw", errno); + + /* set sem0 to 0 after setlk done */ + arg.val = 0; + if (semctl(semid, 0, SETVAL, arg) == -1) + err_exit("set sem0 0", errno); + + /* wating sem 1 to be zero */ + sop.sem_num = 1; + sop.sem_op = 0; + sop.sem_flg = 0; + if (semop(semid, &sop, 1) == -1) + err_exit("wait sem1 0", errno); + + /* remove sem set after one round of test */ + if (semctl(semid, 2, IPC_RMID, arg) == -1) + err_exit("rmid", errno); + } + + /* getlck */ + if (lock_cmd == 0) { + flk.l_start = lock_start; + + /* wating sem 0 to be zero */ + sop.sem_num = 0; + sop.sem_op = 0; + sop.sem_flg = 0; + if (semop(semid, &sop, 1) == -1) + err_exit("wait sem0 0", errno); + + if (fcntl(fd, F_OFD_GETLK, &flk) < 0) + err_exit("ofd_getlk", errno); + + /* set sem1 to 0 after getlk done */ + arg.val = 0; + if (semctl(semid, 1, SETVAL, arg) == -1) + err_exit("set sem1 0", errno); + + switch (flk.l_type) { + case F_UNLCK: + printf("lock could be placed\n"); + break; + case F_RDLCK: + printf("get rdlck\n"); + break; + case F_WRLCK: + printf("get wrlck\n"); + break; + default: + printf("error\n"); + close(fd); + return 0; + } + } + + close(fd); + return 0; +} diff --git a/tests/generic/466 b/tests/generic/466 new file mode 100755 index 0000000..8903af4 --- /dev/null +++ b/tests/generic/466 @@ -0,0 +1,119 @@ +#! /bin/bash +# FS QA Test 466 +# +# Test OFD locks, F_OFD_SETLK/F_OFD_GETLK +# +# The basic idea is placing a type of lock in one process, +# eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl +# getlk with a type of lock, eg RDLCK, in another process. +# In the end, we check the returned flock.l_type by getlk +# to see if the lock mechanism works fine. +# +# We can also test these situations: +# that the two locks are conflicting or not; +# that open testfile RDONLY or RDWR. +# +#----------------------------------------------------------------------- +# Copyright (c) 2017 Red Hat Inc. 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() +{ + rm -f ofd_key + cd / + rm -f $tmp.* +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# remove previous $seqres.full before test +rm -f $seqres.full + +# Modify as appropriate. +_supported_fs generic +_supported_os Linux +_require_scratch +_require_test_program "t_ofd_locks" +_require_ofd_locks + +# real QA test starts here +_scratch_mkfs >> $seqres.full 2>&1 + +# prepare a 4k test file +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ + $SCRATCH_MNT/testfile >> $seqres.full 2>&1 + +do_test() +{ + local soptions="$1" + local goptions="$2" + rm -f fifo + echo $* >> $seqres.full 2>&1 + # -s : do setlk + src/t_ofd_locks -s $soptions $SCRATCH_MNT/testfile & + # -g : do getlk + src/t_ofd_locks -g $goptions $SCRATCH_MNT/testfile + wait +} + +# For the semaphore to sync getlk and getlk +touch ofd_key + +# Always setlk at range [0,9], getlk at range [0,9] or [20,29]. +# To open file RDONLY or RDWR should not break the locks. + +# -w : operate on F_WRLCK +# -r : operate on F_RDLCK +# -R : open file RDONLY +# -W : open file RDWR +# -o : file offset where the lock starts + +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck +do_test "-w" "-w" +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck +do_test "-w" "-w -o 20" +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck +do_test "-w" "-r" +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck +do_test "-w" "-r -o 20" +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck +do_test "-r -R" "-w -R" +do_test "-r" "-w" +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck +do_test "-r -R" "-w -o 20 -R" +do_test "-r" "-w -o 20" +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck +do_test "-r -R" "-r -R" +do_test "-r" "-r" +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck +do_test "-r -R" "-r -o 20 -R" +do_test "-r" "-r -o 20" + +# success, all done +status=0 +exit diff --git a/tests/generic/466.out b/tests/generic/466.out new file mode 100644 index 0000000..89c77fe --- /dev/null +++ b/tests/generic/466.out @@ -0,0 +1,13 @@ +QA output created by 466 +get wrlck +lock could be placed +get wrlck +lock could be placed +get rdlck +get rdlck +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed diff --git a/tests/generic/group b/tests/generic/group index fbe0a7f..b716440 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -468,3 +468,4 @@ 463 auto quick clone dangerous 464 auto rw 465 auto rw quick aio +466 auto quick -- 1.8.3.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3] generic: add OFD lock tests 2017-10-19 8:30 ` [PATCH v3] " Xiong Zhou @ 2017-10-24 8:16 ` Eryu Guan 2017-10-27 4:59 ` [PATCH v4] " Xiong Zhou 0 siblings, 1 reply; 11+ messages in thread From: Eryu Guan @ 2017-10-24 8:16 UTC (permalink / raw) To: Xiong Zhou; +Cc: fstests On Thu, Oct 19, 2017 at 04:30:36PM +0800, Xiong Zhou wrote: > The basic idea is placing a type of lock in one process, > eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl > getlk with a type of lock, eg RDLCK, in another process. > In the end, we check the returned flock.l_type by getlk > to see if the lock mechanism works fine. > > We can also test these situations: > that the two locks are conflicting or not; > that open testfile RDONLY or RDWR. > > Signed-off-by: Xiong Zhou <xzhou@redhat.com> > --- > > v3: > Use golden output to match test result; > Use semaphore to sync getlk/setlk; > Add option parse to the test programme; > Add a testrun code path for the helper; > Add more comments; > Skip the not stable /proc/locks check. v3 looks better, thanks! More comments inline. > > .gitignore | 1 + > common/rc | 11 +++ > src/Makefile | 3 +- > src/t_ofd_locks.c | 262 ++++++++++++++++++++++++++++++++++++++++++++++++++ > tests/generic/466 | 119 +++++++++++++++++++++++ > tests/generic/466.out | 13 +++ > tests/generic/group | 1 + > 7 files changed, 409 insertions(+), 1 deletion(-) > create mode 100644 src/t_ofd_locks.c > create mode 100755 tests/generic/466 > create mode 100644 tests/generic/466.out > > diff --git a/.gitignore b/.gitignore > index 2014c08..77acb42 100644 > --- a/.gitignore > +++ b/.gitignore > @@ -126,6 +126,7 @@ > /src/t_mmap_write_ro > /src/t_mmap_writev > /src/t_mtab > +/src/t_ofd_locks > /src/t_readdir_1 > /src/t_readdir_2 > /src/t_rename_overwrite > diff --git a/common/rc b/common/rc > index e2a8229..983a214 100644 > --- a/common/rc > +++ b/common/rc > @@ -3192,6 +3192,17 @@ _require_test_fcntl_advisory_locks() > _notrun "Require fcntl advisory locks support" > } > > +_require_ofd_locks() > +{ > + # Give a test run by setlk/getlk rdlck on testfile. > + # If the running kernel does not support OFD locks, > + # EINVAL will be returned. > + touch $TEST_DIR/ofd_testfile || \ > + _notrun "Something wrong with $TEST_DIR" > + src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1 > + [ $? -eq 22 ] && _notrun "Require OFD locks support" Perhaps we can move _require_test_program "t_ofd_locks" here too. > +} > + > _require_test_lsattr() > { > testio=$(lsattr -d $TEST_DIR 2>&1) > diff --git a/src/Makefile b/src/Makefile > index 3eb25b1..f37af74 100644 > --- a/src/Makefile > +++ b/src/Makefile > @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ > multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ > t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ > holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ > - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro > + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ > + t_ofd_locks > > LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ > preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ > diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c > new file mode 100644 > index 0000000..f87c9c5 > --- /dev/null > +++ b/src/t_ofd_locks.c > @@ -0,0 +1,262 @@ > +#ifndef _GNU_SOURCE > +#define _GNU_SOURCE > +#endif > +#include <unistd.h> > +#include <fcntl.h> > +#include <errno.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <string.h> > +#include <stdlib.h> > +#include <sys/stat.h> > +#include <sys/types.h> > +#include <sys/ipc.h> > +#include <sys/sem.h> > + > +/* > + * In distributions that do not have these macros ready in > + * glibc-headers, compilation fails. Adding them here to avoid > + * build errors, relevant tests would fail at the helper which > + * requires OFD locks support and notrun if the kernel does not > + * support OFD locks. If the kernel does support OFD locks, > + * we are good to go. > + * > + */ > +#ifndef F_OFD_GETLK > +#define F_OFD_GETLK 36 > +#endif > + > +#ifndef F_OFD_SETLK > +#define F_OFD_SETLK 37 > +#endif > + > +#ifndef F_OFD_SETLKW > +#define F_OFD_SETLKW 38 > +#endif > + > +/* This is required by semctl to set semaphore value */ > +union semun { > + int val; /* Value for SETVAL */ > + struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ > + unsigned short *array; /* Array for GETALL, SETALL */ > + struct seminfo *__buf; /* Buffer for IPC_INFO > + (Linux-specific) */ > +}; > + > +int fd; > + > +void err_exit(char *op, int errn) > +{ > + fprintf(stderr, "%s: %s\n", op, strerror(errn)); > + if (fd > 0) > + close(fd); > + exit(errn); > +} > + > +int main(int argc, char **argv) > +{ > + /* Five flags that used to specify operation details. > + * They can be specified via command line options. For multi-line comments, please use this format /* * Five flags that .. * Then can be ... */ > + * > + * option: -s/-g > + * lock_cmd : 1 <--> setlk > + * 0 <--> getlk > + * > + * option: -r/-w > + * lock_rw : 1 <--> set/get wrlck > + * 0 <--> set/get rdlck > + * > + * option: -o num > + * lock_start : l_start to getlk > + * > + * option: -R/-W > + * open_rw : 1 <--> open file RDWR > + * 0 <--> open file RDONLY > + * > + * This option is for _require_ofd_locks helper, just do > + * fcntl setlk then return errno. > + * option: -t > + * testrun : 1 <--> this is a testrun, return after setlk > + * 0 <--> this is not a testrun, run as usual > + */ I think this usage information should be in a usage() function, which gets called when error happens in option parsing. > + > + /* By default, we setlk WRLCK on [0,9], opening file RDWR. > + */ > + > + int lock_cmd = 1; > + int lock_rw = 1; > + int lock_start = 0; > + int open_rw = 1; > + int testrun = 0; > + > + /* We use semaphore to synchronize between getlk and setlk. > + * > + * Although we run getlk routine after running setlk routine > + * in background, getlk still can be executed before setlk > + * sometimes, which is invalid for our tests. > + * > + * In setlk routine, we wait getlk done, then exit, making sure > + * the lock is still there when doing getlk. > + * > + * In getlk routine, we wait till setlk done firstly, making > + * sure the lock is valid at that time, then we do getlk, > + * after that, we tell setlk we are done, then exit. > + */ > + key_t semkey; > + unsigned short vals[2]; > + union semun arg; > + int semid; > + struct sembuf sop; > + > + struct flock flk = { > + .l_whence = SEEK_SET, > + .l_start = 0, > + .l_len = 10, /* lock range [0,9] */ > + .l_type = F_RDLCK, > + .l_pid = 0, > + }; > + > + int opt; > + > + while((opt = getopt(argc, argv, "sgrwo:RWt")) != -1) { > + switch(opt) { > + case 's': > + lock_cmd = 1; > + break; > + case 'g': > + lock_cmd = 0; > + break; > + case 'r': > + lock_rw = 0; > + break; > + case 'w': > + lock_rw = 1; > + break; > + case 'o': > + lock_start = atoi(optarg); > + break; > + case 'R': > + open_rw = 0; > + break; > + case 'W': > + open_rw = 1; > + break; > + case 't': > + testrun = 1; > + break; > + default: > + printf("Usage: %s [-sgrwo:RW] filename\n", argv[0]); > + return -1; > + } > + } > + > + if (lock_rw == 1) > + flk.l_type = F_WRLCK; > + else > + flk.l_type = F_RDLCK; > + > + if (open_rw == 0) > + fd = open(argv[optind], O_RDONLY); > + else > + fd = open(argv[optind], O_RDWR); > + if (fd == -1) > + err_exit("open", errno); > + > + /* In a testun, we do a fcntl getlk call and exit > + * immediately no matter it succeeds or not. > + */ > + if (testrun == 1) { > + if (fcntl(fd, F_OFD_GETLK, &flk) < 0) > + err_exit("test_ofd_getlk", errno); > + if (fd > 0) > + close(fd); fd is always greater than 0 here, just close(fd) should be fine. Or use err_exit() unconditionally? e.g. fcntl(fd, ...); err_exit("test_ofd_getlk", errno); > + exit(0); > + } > + > + /* Init the semaphore, with key related to the same file. > + * If the sem set has not been created, we initialnize it. > + * If it exists, we semget again to get the exist one. > + * To make sure getlk routine and setlk routine are looking > + * at the same semaphore set in one single round of test. > + */ > + if((semkey = ftok("ofd_key", 255)) == -1) > + err_exit("ftok", errno); This uses a file called "ofd_key" in current working dir, usually $here in fstests, I don't think that's good idea, how about using the test file too? i.e. argv[optind]. > + semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL); > + if (semid < 0) { > + /* The sem exists or errer happens. */ > + if (errno != EEXIST) > + err_exit("semget0", errno); > + > + semid = semget(semkey, 2, 0); > + if (semid < 0) > + err_exit("semget1", errno); > + } else { > + /* Init both new sem to 1. */ > + vals[0] = 1; > + vals[1] = 1; > + arg.array = vals; > + if (semctl(semid, 2, SETALL, arg) == -1) > + err_exit("init sem", errno); > + } > + > + /* setlk */ > + if (lock_cmd == 1) { > + if (fcntl(fd, F_OFD_SETLKW, &flk) < 0) > + err_exit("ofd_setlkw", errno); > + > + /* set sem0 to 0 after setlk done */ > + arg.val = 0; > + if (semctl(semid, 0, SETVAL, arg) == -1) > + err_exit("set sem0 0", errno); > + > + /* wating sem 1 to be zero */ > + sop.sem_num = 1; > + sop.sem_op = 0; > + sop.sem_flg = 0; > + if (semop(semid, &sop, 1) == -1) > + err_exit("wait sem1 0", errno); > + > + /* remove sem set after one round of test */ > + if (semctl(semid, 2, IPC_RMID, arg) == -1) > + err_exit("rmid", errno); > + } > + > + /* getlck */ > + if (lock_cmd == 0) { > + flk.l_start = lock_start; > + > + /* wating sem 0 to be zero */ > + sop.sem_num = 0; > + sop.sem_op = 0; > + sop.sem_flg = 0; > + if (semop(semid, &sop, 1) == -1) > + err_exit("wait sem0 0", errno); > + > + if (fcntl(fd, F_OFD_GETLK, &flk) < 0) > + err_exit("ofd_getlk", errno); > + > + /* set sem1 to 0 after getlk done */ > + arg.val = 0; > + if (semctl(semid, 1, SETVAL, arg) == -1) > + err_exit("set sem1 0", errno); > + > + switch (flk.l_type) { > + case F_UNLCK: > + printf("lock could be placed\n"); > + break; > + case F_RDLCK: > + printf("get rdlck\n"); > + break; > + case F_WRLCK: > + printf("get wrlck\n"); > + break; > + default: > + printf("error\n"); Could be more verbose on error, e.g. "unknown lock type"? > + close(fd); > + return 0; > + } > + } > + > + close(fd); > + return 0; > +} > diff --git a/tests/generic/466 b/tests/generic/466 > new file mode 100755 > index 0000000..8903af4 > --- /dev/null > +++ b/tests/generic/466 > @@ -0,0 +1,119 @@ > +#! /bin/bash > +# FS QA Test 466 > +# > +# Test OFD locks, F_OFD_SETLK/F_OFD_GETLK > +# > +# The basic idea is placing a type of lock in one process, > +# eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl > +# getlk with a type of lock, eg RDLCK, in another process. > +# In the end, we check the returned flock.l_type by getlk > +# to see if the lock mechanism works fine. > +# > +# We can also test these situations: > +# that the two locks are conflicting or not; > +# that open testfile RDONLY or RDWR. > +# > +#----------------------------------------------------------------------- > +# Copyright (c) 2017 Red Hat Inc. 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() > +{ > + rm -f ofd_key Better not to pollute $here as mentioned above. > + cd / > + rm -f $tmp.* > +} > + > +# get standard environment, filters and checks > +. ./common/rc > +. ./common/filter > + > +# remove previous $seqres.full before test > +rm -f $seqres.full > + > +# Modify as appropriate. > +_supported_fs generic > +_supported_os Linux > +_require_scratch > +_require_test_program "t_ofd_locks" > +_require_ofd_locks > + > +# real QA test starts here > +_scratch_mkfs >> $seqres.full 2>&1 > + > +# prepare a 4k test file > +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ > + $SCRATCH_MNT/testfile >> $seqres.full 2>&1 > + > +do_test() > +{ > + local soptions="$1" > + local goptions="$2" > + rm -f fifo Unused fifo, this line can be removed. > + echo $* >> $seqres.full 2>&1 > + # -s : do setlk > + src/t_ofd_locks -s $soptions $SCRATCH_MNT/testfile & $here/src/t_ofd_locks, not a big issue though. > + # -g : do getlk > + src/t_ofd_locks -g $goptions $SCRATCH_MNT/testfile > + wait > +} > + > +# For the semaphore to sync getlk and getlk > +touch ofd_key Can be removed too if take use of $SCRATCH_MNT/testfile > + > +# Always setlk at range [0,9], getlk at range [0,9] or [20,29]. > +# To open file RDONLY or RDWR should not break the locks. > + > +# -w : operate on F_WRLCK > +# -r : operate on F_RDLCK > +# -R : open file RDONLY > +# -W : open file RDWR > +# -o : file offset where the lock starts > + > +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck > +do_test "-w" "-w" > +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck > +do_test "-w" "-w -o 20" > +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck > +do_test "-w" "-r" > +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck > +do_test "-w" "-r -o 20" > +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck > +do_test "-r -R" "-w -R" > +do_test "-r" "-w" > +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck > +do_test "-r -R" "-w -o 20 -R" > +do_test "-r" "-w -o 20" > +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck > +do_test "-r -R" "-r -R" > +do_test "-r" "-r" > +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck ^^^^^ [20,29] Thanks, Eryu > +do_test "-r -R" "-r -o 20 -R" > +do_test "-r" "-r -o 20" > + > +# success, all done > +status=0 > +exit > diff --git a/tests/generic/466.out b/tests/generic/466.out > new file mode 100644 > index 0000000..89c77fe > --- /dev/null > +++ b/tests/generic/466.out > @@ -0,0 +1,13 @@ > +QA output created by 466 > +get wrlck > +lock could be placed > +get wrlck > +lock could be placed > +get rdlck > +get rdlck > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > diff --git a/tests/generic/group b/tests/generic/group > index fbe0a7f..b716440 100644 > --- a/tests/generic/group > +++ b/tests/generic/group > @@ -468,3 +468,4 @@ > 463 auto quick clone dangerous > 464 auto rw > 465 auto rw quick aio > +466 auto quick > -- > 1.8.3.1 > > -- > To unsubscribe from this list: send the line "unsubscribe fstests" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4] generic: add OFD lock tests 2017-10-24 8:16 ` Eryu Guan @ 2017-10-27 4:59 ` Xiong Zhou 2017-10-28 9:29 ` Eryu Guan 0 siblings, 1 reply; 11+ messages in thread From: Xiong Zhou @ 2017-10-27 4:59 UTC (permalink / raw) To: fstests; +Cc: Xiong Zhou The basic idea is placing a type of lock in one process, eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl getlk with a type of lock, eg RDLCK, in another process. In the end, we check the returned flock.l_type by getlk to see if the lock mechanism works fine. We can also test these situations: that the two locks are conflicting or not; that open testfile RDONLY or RDWR. Signed-off-by: Xiong Zhou <xzhou@redhat.com> --- v4: add usage function to provide more info; move _require_test_program helper to _require_ofd_locks helper; simplify testrun routine; use testfile as token file; some minor fix. .gitignore | 1 + common/rc | 12 +++ src/Makefile | 3 +- src/t_ofd_locks.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/generic/466 | 113 +++++++++++++++++++++ tests/generic/466.out | 13 +++ tests/generic/group | 1 + 7 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 src/t_ofd_locks.c create mode 100755 tests/generic/466 create mode 100644 tests/generic/466.out diff --git a/.gitignore b/.gitignore index 2014c08..77acb42 100644 --- a/.gitignore +++ b/.gitignore @@ -126,6 +126,7 @@ /src/t_mmap_write_ro /src/t_mmap_writev /src/t_mtab +/src/t_ofd_locks /src/t_readdir_1 /src/t_readdir_2 /src/t_rename_overwrite diff --git a/common/rc b/common/rc index e2a8229..22346e4 100644 --- a/common/rc +++ b/common/rc @@ -3192,6 +3192,18 @@ _require_test_fcntl_advisory_locks() _notrun "Require fcntl advisory locks support" } +_require_ofd_locks() +{ + # Give a test run by setlk/getlk rdlck on testfile. + # If the running kernel does not support OFD locks, + # EINVAL will be returned. + _require_test_program "t_ofd_locks" + touch $TEST_DIR/ofd_testfile || \ + _notrun "Something wrong with $TEST_DIR" + src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1 + [ $? -eq 22 ] && _notrun "Require OFD locks support" +} + _require_test_lsattr() { testio=$(lsattr -d $TEST_DIR 2>&1) diff --git a/src/Makefile b/src/Makefile index 3eb25b1..f37af74 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ + t_ofd_locks LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c new file mode 100644 index 0000000..c8f0ad2 --- /dev/null +++ b/src/t_ofd_locks.c @@ -0,0 +1,276 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> + +/* + * In distributions that do not have these macros ready in + * glibc-headers, compilation fails. Adding them here to avoid + * build errors, relevant tests would fail at the helper which + * requires OFD locks support and notrun if the kernel does not + * support OFD locks. If the kernel does support OFD locks, + * we are good to go. + * + */ +#ifndef F_OFD_GETLK +#define F_OFD_GETLK 36 +#endif + +#ifndef F_OFD_SETLK +#define F_OFD_SETLK 37 +#endif + +#ifndef F_OFD_SETLKW +#define F_OFD_SETLKW 38 +#endif + +/* This is required by semctl to set semaphore value */ +union semun { + int val; /* Value for SETVAL */ + struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* Array for GETALL, SETALL */ + struct seminfo *__buf; /* Buffer for IPC_INFO + (Linux-specific) */ +}; + +int fd; + +static void err_exit(char *op, int errn) +{ + fprintf(stderr, "%s: %s\n", op, strerror(errn)); + if (fd > 0) + close(fd); + exit(errn); +} + +static void usage(char *arg) +{ + printf("Usage: %s [-sgrwo:RW] filename\n", arg); + printf("\t-s : setlk\n"); + printf("\t-g : getlk\n"); + printf("\t-r : set/get rdlck\n"); + printf("\t-w : set/get wrlck\n"); + printf("\t-o num : offset start to lock\n"); + printf("\t-R : open file RDONLY\n"); + printf("\t-W : open file RDWR\n"); + exit(0); +} + +int main(int argc, char **argv) +{ + /* Five flags that used to specify operation details. + * They can be specified via command line options. + * + * option: -s/-g + * lock_cmd : 1 <--> setlk + * 0 <--> getlk + * + * option: -r/-w + * lock_rw : 1 <--> set/get wrlck + * 0 <--> set/get rdlck + * + * option: -o num + * lock_start : l_start to getlk + * + * option: -R/-W + * open_rw : 1 <--> open file RDWR + * 0 <--> open file RDONLY + * + * This option is for _require_ofd_locks helper, just do + * fcntl setlk then return errno. + * option: -t + * testrun : 1 <--> this is a testrun, return after setlk + * 0 <--> this is not a testrun, run as usual + * + * By default, we setlk WRLCK on [0,9], opening file RDWR. + */ + + int lock_cmd = 1; + int lock_rw = 1; + int lock_start = 0; + int open_rw = 1; + int testrun = 0; + + /* + * We use semaphore to synchronize between getlk and setlk. + * + * Although we run getlk routine after running setlk routine + * in background, getlk still can be executed before setlk + * sometimes, which is invalid for our tests. + * + * In setlk routine, we wait getlk done, then exit, making sure + * the lock is still there when doing getlk. + * + * In getlk routine, we wait till setlk done firstly, making + * sure the lock is valid at that time, then we do getlk, + * after that, we tell setlk we are done, then exit. + */ + key_t semkey; + unsigned short vals[2]; + union semun arg; + int semid; + struct sembuf sop; + int opt; + + struct flock flk = { + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 10, /* lock range [0,9] */ + .l_type = F_RDLCK, + .l_pid = 0, + }; + + if (argc < 2) { + usage(argv[0]); + return -1; + } + + while((opt = getopt(argc, argv, "sgrwo:RWt")) != -1) { + switch(opt) { + case 's': + lock_cmd = 1; + break; + case 'g': + lock_cmd = 0; + break; + case 'r': + lock_rw = 0; + break; + case 'w': + lock_rw = 1; + break; + case 'o': + lock_start = atoi(optarg); + break; + case 'R': + open_rw = 0; + break; + case 'W': + open_rw = 1; + break; + case 't': + testrun = 1; + break; + default: + usage(argv[0]); + return -1; + } + } + + if (lock_rw == 1) + flk.l_type = F_WRLCK; + else + flk.l_type = F_RDLCK; + + if (open_rw == 0) + fd = open(argv[optind], O_RDONLY); + else + fd = open(argv[optind], O_RDWR); + if (fd == -1) + err_exit("open", errno); + + /* In a testun, we do a fcntl getlk call and exit + * immediately no matter it succeeds or not. + */ + if (testrun == 1) { + fcntl(fd, F_OFD_GETLK, &flk); + err_exit("test_ofd_getlk", errno); + } + + /* Init the semaphore, with key related to the same file. + * If the sem set has not been created, we initialnize it. + * If it exists, we semget again to get the exist one. + * To make sure getlk routine and setlk routine are looking + * at the same semaphore set in one single round of test. + */ + if((semkey = ftok(argv[optind], 255)) == -1) + err_exit("ftok", errno); + semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL); + if (semid < 0) { + /* The sem exists or errer happens. */ + if (errno != EEXIST) + err_exit("semget0", errno); + + semid = semget(semkey, 2, 0); + if (semid < 0) + err_exit("semget1", errno); + } else { + /* Init both new sem to 1. */ + vals[0] = 1; + vals[1] = 1; + arg.array = vals; + if (semctl(semid, 2, SETALL, arg) == -1) + err_exit("init sem", errno); + } + + /* setlk */ + if (lock_cmd == 1) { + if (fcntl(fd, F_OFD_SETLKW, &flk) < 0) + err_exit("ofd_setlkw", errno); + + /* set sem0 to 0 after setlk done */ + arg.val = 0; + if (semctl(semid, 0, SETVAL, arg) == -1) + err_exit("set sem0 0", errno); + + /* wating sem 1 to be zero */ + sop.sem_num = 1; + sop.sem_op = 0; + sop.sem_flg = 0; + if (semop(semid, &sop, 1) == -1) + err_exit("wait sem1 0", errno); + + /* remove sem set after one round of test */ + if (semctl(semid, 2, IPC_RMID, arg) == -1) + err_exit("rmid", errno); + } + + /* getlck */ + if (lock_cmd == 0) { + flk.l_start = lock_start; + + /* wating sem 0 to be zero */ + sop.sem_num = 0; + sop.sem_op = 0; + sop.sem_flg = 0; + if (semop(semid, &sop, 1) == -1) + err_exit("wait sem0 0", errno); + + if (fcntl(fd, F_OFD_GETLK, &flk) < 0) + err_exit("ofd_getlk", errno); + + /* set sem1 to 0 after getlk done */ + arg.val = 0; + if (semctl(semid, 1, SETVAL, arg) == -1) + err_exit("set sem1 0", errno); + + switch (flk.l_type) { + case F_UNLCK: + printf("lock could be placed\n"); + break; + case F_RDLCK: + printf("get rdlck\n"); + break; + case F_WRLCK: + printf("get wrlck\n"); + break; + default: + printf("unknown lock type\n"); + close(fd); + return 0; + } + } + + close(fd); + return 0; +} diff --git a/tests/generic/466 b/tests/generic/466 new file mode 100755 index 0000000..80a30a5 --- /dev/null +++ b/tests/generic/466 @@ -0,0 +1,113 @@ +#! /bin/bash +# FS QA Test 466 +# +# Test OFD locks, F_OFD_SETLK/F_OFD_GETLK +# +# The basic idea is placing a type of lock in one process, +# eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl +# getlk with a type of lock, eg RDLCK, in another process. +# In the end, we check the returned flock.l_type by getlk +# to see if the lock mechanism works fine. +# +# We can also test these situations: +# that the two locks are conflicting or not; +# that open testfile RDONLY or RDWR. +# +#----------------------------------------------------------------------- +# Copyright (c) 2017 Red Hat Inc. 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.* +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# remove previous $seqres.full before test +rm -f $seqres.full + +# Modify as appropriate. +_supported_fs generic +_supported_os Linux +_require_scratch +_require_ofd_locks + +# real QA test starts here +_scratch_mkfs >> $seqres.full 2>&1 + +# prepare a 4k test file +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ + $SCRATCH_MNT/testfile >> $seqres.full 2>&1 + +do_test() +{ + local soptions="$1" + local goptions="$2" + echo $* >> $seqres.full 2>&1 + # -s : do setlk + $here/src/t_ofd_locks -s $soptions $SCRATCH_MNT/testfile & + # -g : do getlk + $here/src/t_ofd_locks -g $goptions $SCRATCH_MNT/testfile + wait +} + +# Always setlk at range [0,9], getlk at range [0,9] or [20,29]. +# To open file RDONLY or RDWR should not break the locks. + +# -w : operate on F_WRLCK +# -r : operate on F_RDLCK +# -R : open file RDONLY +# -W : open file RDWR +# -o : file offset where the lock starts + +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck +do_test "-w" "-w" +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck +do_test "-w" "-w -o 20" +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck +do_test "-w" "-r" +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck +do_test "-w" "-r -o 20" +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck +do_test "-r -R" "-w -R" +do_test "-r" "-w" +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck +do_test "-r -R" "-w -o 20 -R" +do_test "-r" "-w -o 20" +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck +do_test "-r -R" "-r -R" +do_test "-r" "-r" +# setlk rdlck [0,9], getlk rdlck [20,29], expect unlck +do_test "-r -R" "-r -o 20 -R" +do_test "-r" "-r -o 20" + +# success, all done +status=0 +exit diff --git a/tests/generic/466.out b/tests/generic/466.out new file mode 100644 index 0000000..89c77fe --- /dev/null +++ b/tests/generic/466.out @@ -0,0 +1,13 @@ +QA output created by 466 +get wrlck +lock could be placed +get wrlck +lock could be placed +get rdlck +get rdlck +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed diff --git a/tests/generic/group b/tests/generic/group index fbe0a7f..b716440 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -468,3 +468,4 @@ 463 auto quick clone dangerous 464 auto rw 465 auto rw quick aio +466 auto quick -- 1.8.3.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v4] generic: add OFD lock tests 2017-10-27 4:59 ` [PATCH v4] " Xiong Zhou @ 2017-10-28 9:29 ` Eryu Guan 2017-10-29 2:34 ` Xiong Zhou 2017-11-10 2:46 ` [PATCH v5] " Xiong Zhou 0 siblings, 2 replies; 11+ messages in thread From: Eryu Guan @ 2017-10-28 9:29 UTC (permalink / raw) To: Xiong Zhou; +Cc: fstests On Fri, Oct 27, 2017 at 12:59:23PM +0800, Xiong Zhou wrote: > The basic idea is placing a type of lock in one process, > eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl > getlk with a type of lock, eg RDLCK, in another process. > In the end, we check the returned flock.l_type by getlk > to see if the lock mechanism works fine. > > We can also test these situations: > that the two locks are conflicting or not; > that open testfile RDONLY or RDWR. > > Signed-off-by: Xiong Zhou <xzhou@redhat.com> > --- > > v4: > add usage function to provide more info; > move _require_test_program helper to _require_ofd_locks helper; > simplify testrun routine; > use testfile as token file; > some minor fix. Thanks for the update! But I still see unexpected failures in release testing, for more details please see below inline comments, along with other new comments.. > > .gitignore | 1 + > common/rc | 12 +++ > src/Makefile | 3 +- > src/t_ofd_locks.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++++++ > tests/generic/466 | 113 +++++++++++++++++++++ > tests/generic/466.out | 13 +++ > tests/generic/group | 1 + > 7 files changed, 418 insertions(+), 1 deletion(-) > create mode 100644 src/t_ofd_locks.c > create mode 100755 tests/generic/466 > create mode 100644 tests/generic/466.out > > diff --git a/.gitignore b/.gitignore > index 2014c08..77acb42 100644 > --- a/.gitignore > +++ b/.gitignore > @@ -126,6 +126,7 @@ > /src/t_mmap_write_ro > /src/t_mmap_writev > /src/t_mtab > +/src/t_ofd_locks > /src/t_readdir_1 > /src/t_readdir_2 > /src/t_rename_overwrite > diff --git a/common/rc b/common/rc > index e2a8229..22346e4 100644 > --- a/common/rc > +++ b/common/rc > @@ -3192,6 +3192,18 @@ _require_test_fcntl_advisory_locks() > _notrun "Require fcntl advisory locks support" > } > > +_require_ofd_locks() > +{ > + # Give a test run by setlk/getlk rdlck on testfile. > + # If the running kernel does not support OFD locks, > + # EINVAL will be returned. > + _require_test_program "t_ofd_locks" > + touch $TEST_DIR/ofd_testfile || \ > + _notrun "Something wrong with $TEST_DIR" I don't think it's necessary to check touch succeed or not, if we can't touch a new file in $TEST_DIR, the test will fail in some other ways. At least, it should be a _fail not _notrun. > + src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1 > + [ $? -eq 22 ] && _notrun "Require OFD locks support" > +} > + > _require_test_lsattr() > { > testio=$(lsattr -d $TEST_DIR 2>&1) > diff --git a/src/Makefile b/src/Makefile > index 3eb25b1..f37af74 100644 > --- a/src/Makefile > +++ b/src/Makefile > @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ > multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ > t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ > holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ > - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro > + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ > + t_ofd_locks > > LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ > preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ > diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c > new file mode 100644 > index 0000000..c8f0ad2 > --- /dev/null > +++ b/src/t_ofd_locks.c > @@ -0,0 +1,276 @@ > +#ifndef _GNU_SOURCE > +#define _GNU_SOURCE > +#endif > +#include <unistd.h> > +#include <fcntl.h> > +#include <errno.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <string.h> > +#include <stdlib.h> > +#include <sys/stat.h> > +#include <sys/types.h> > +#include <sys/ipc.h> > +#include <sys/sem.h> > + > +/* > + * In distributions that do not have these macros ready in > + * glibc-headers, compilation fails. Adding them here to avoid > + * build errors, relevant tests would fail at the helper which > + * requires OFD locks support and notrun if the kernel does not > + * support OFD locks. If the kernel does support OFD locks, > + * we are good to go. > + * > + */ > +#ifndef F_OFD_GETLK > +#define F_OFD_GETLK 36 > +#endif > + > +#ifndef F_OFD_SETLK > +#define F_OFD_SETLK 37 > +#endif > + > +#ifndef F_OFD_SETLKW > +#define F_OFD_SETLKW 38 > +#endif > + > +/* This is required by semctl to set semaphore value */ > +union semun { > + int val; /* Value for SETVAL */ > + struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ > + unsigned short *array; /* Array for GETALL, SETALL */ > + struct seminfo *__buf; /* Buffer for IPC_INFO > + (Linux-specific) */ > +}; > + > +int fd; > + > +static void err_exit(char *op, int errn) > +{ > + fprintf(stderr, "%s: %s\n", op, strerror(errn)); > + if (fd > 0) > + close(fd); > + exit(errn); > +} > + > +static void usage(char *arg) > +{ > + printf("Usage: %s [-sgrwo:RW] filename\n", arg); > + printf("\t-s : setlk\n"); > + printf("\t-g : getlk\n"); > + printf("\t-r : set/get rdlck\n"); > + printf("\t-w : set/get wrlck\n"); > + printf("\t-o num : offset start to lock\n"); > + printf("\t-R : open file RDONLY\n"); > + printf("\t-W : open file RDWR\n"); > + exit(0); Better to mention that the expected usage is run -s/-g in pair, e.g. a '-s' run in background followed by a '-g' run, otherwise the test program just hangs there. > +} > + > +int main(int argc, char **argv) > +{ > + /* Five flags that used to specify operation details. Comment format issue I mentioned in my last review, and other multi-line comments have the issue too. > + * They can be specified via command line options. > + * > + * option: -s/-g > + * lock_cmd : 1 <--> setlk > + * 0 <--> getlk > + * > + * option: -r/-w > + * lock_rw : 1 <--> set/get wrlck > + * 0 <--> set/get rdlck > + * > + * option: -o num > + * lock_start : l_start to getlk > + * > + * option: -R/-W > + * open_rw : 1 <--> open file RDWR > + * 0 <--> open file RDONLY > + * > + * This option is for _require_ofd_locks helper, just do > + * fcntl setlk then return errno. > + * option: -t > + * testrun : 1 <--> this is a testrun, return after setlk > + * 0 <--> this is not a testrun, run as usual > + * > + * By default, we setlk WRLCK on [0,9], opening file RDWR. > + */ > + > + int lock_cmd = 1; > + int lock_rw = 1; > + int lock_start = 0; > + int open_rw = 1; > + int testrun = 0; > + > + /* > + * We use semaphore to synchronize between getlk and setlk. > + * > + * Although we run getlk routine after running setlk routine > + * in background, getlk still can be executed before setlk > + * sometimes, which is invalid for our tests. > + * > + * In setlk routine, we wait getlk done, then exit, making sure > + * the lock is still there when doing getlk. > + * > + * In getlk routine, we wait till setlk done firstly, making > + * sure the lock is valid at that time, then we do getlk, > + * after that, we tell setlk we are done, then exit. > + */ > + key_t semkey; > + unsigned short vals[2]; > + union semun arg; > + int semid; > + struct sembuf sop; > + int opt; > + > + struct flock flk = { > + .l_whence = SEEK_SET, > + .l_start = 0, > + .l_len = 10, /* lock range [0,9] */ Better to always specify the lock len from commandline, e.g. t_ofd_locks -l 10 ... t_ofd_locks -o 20 -l 10 ... > + .l_type = F_RDLCK, > + .l_pid = 0, > + }; > + > + if (argc < 2) { > + usage(argv[0]); > + return -1; > + } > + > + while((opt = getopt(argc, argv, "sgrwo:RWt")) != -1) { > + switch(opt) { > + case 's': > + lock_cmd = 1; > + break; > + case 'g': > + lock_cmd = 0; > + break; > + case 'r': > + lock_rw = 0; > + break; > + case 'w': > + lock_rw = 1; > + break; > + case 'o': > + lock_start = atoi(optarg); > + break; > + case 'R': > + open_rw = 0; > + break; > + case 'W': > + open_rw = 1; > + break; > + case 't': > + testrun = 1; > + break; > + default: > + usage(argv[0]); > + return -1; > + } > + } > + > + if (lock_rw == 1) > + flk.l_type = F_WRLCK; > + else > + flk.l_type = F_RDLCK; > + > + if (open_rw == 0) > + fd = open(argv[optind], O_RDONLY); > + else > + fd = open(argv[optind], O_RDWR); > + if (fd == -1) > + err_exit("open", errno); > + > + /* In a testun, we do a fcntl getlk call and exit > + * immediately no matter it succeeds or not. > + */ > + if (testrun == 1) { > + fcntl(fd, F_OFD_GETLK, &flk); > + err_exit("test_ofd_getlk", errno); > + } > + > + /* Init the semaphore, with key related to the same file. > + * If the sem set has not been created, we initialnize it. > + * If it exists, we semget again to get the exist one. > + * To make sure getlk routine and setlk routine are looking > + * at the same semaphore set in one single round of test. > + */ > + if((semkey = ftok(argv[optind], 255)) == -1) > + err_exit("ftok", errno); > + semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL); > + if (semid < 0) { > + /* The sem exists or errer happens. */ > + if (errno != EEXIST) > + err_exit("semget0", errno); > + > + semid = semget(semkey, 2, 0); > + if (semid < 0) > + err_exit("semget1", errno); There's a race between creating the sem (semget) and initializing the sem (semctl), and the sem value after creating but before initializing is undefined, but it can be zero. So it's possible that a '-s' run creates the sem then stops and a '-g' run sees the existing but uninitialized sem which happens to be zero, and doest the getlock test and sees no lock was placed wrongly. I've seen failures like this multiple times in my release testing: QA output created by 466 -get wrlck +lock could be placed semget(2) manpage documents the race under "Semaphore initialization" section and suggests a solution too, "checking for a nonzero sem_otime in the associated data structure retrieved by a semctl(2) IPC_STAT operation can be used to avoid races." But I don't see why initialize the sem in a '-g' run too, if we always initialize sem in '-s' run, there's no such race at all. > + } else { > + /* Init both new sem to 1. */ > + vals[0] = 1; > + vals[1] = 1; > + arg.array = vals; > + if (semctl(semid, 2, SETALL, arg) == -1) > + err_exit("init sem", errno); > + } > + > + /* setlk */ > + if (lock_cmd == 1) { > + if (fcntl(fd, F_OFD_SETLKW, &flk) < 0) > + err_exit("ofd_setlkw", errno); > + > + /* set sem0 to 0 after setlk done */ > + arg.val = 0; > + if (semctl(semid, 0, SETVAL, arg) == -1) > + err_exit("set sem0 0", errno); > + > + /* wating sem 1 to be zero */ > + sop.sem_num = 1; > + sop.sem_op = 0; > + sop.sem_flg = 0; > + if (semop(semid, &sop, 1) == -1) > + err_exit("wait sem1 0", errno); How about using semtimedop and set a timeout value? So we don't wait forever if something goes wrong. > + > + /* remove sem set after one round of test */ > + if (semctl(semid, 2, IPC_RMID, arg) == -1) > + err_exit("rmid", errno); > + } > + > + /* getlck */ > + if (lock_cmd == 0) { > + flk.l_start = lock_start; > + > + /* wating sem 0 to be zero */ > + sop.sem_num = 0; > + sop.sem_op = 0; > + sop.sem_flg = 0; > + if (semop(semid, &sop, 1) == -1) > + err_exit("wait sem0 0", errno); > + > + if (fcntl(fd, F_OFD_GETLK, &flk) < 0) > + err_exit("ofd_getlk", errno); > + > + /* set sem1 to 0 after getlk done */ > + arg.val = 0; > + if (semctl(semid, 1, SETVAL, arg) == -1) > + err_exit("set sem1 0", errno); > + > + switch (flk.l_type) { > + case F_UNLCK: > + printf("lock could be placed\n"); > + break; > + case F_RDLCK: > + printf("get rdlck\n"); > + break; > + case F_WRLCK: > + printf("get wrlck\n"); > + break; > + default: > + printf("unknown lock type\n"); > + close(fd); > + return 0; Just print "unknown lock type" and a break. > + } > + } > + > + close(fd); > + return 0; > +} > diff --git a/tests/generic/466 b/tests/generic/466 > new file mode 100755 > index 0000000..80a30a5 > --- /dev/null > +++ b/tests/generic/466 > @@ -0,0 +1,113 @@ > +#! /bin/bash > +# FS QA Test 466 > +# > +# Test OFD locks, F_OFD_SETLK/F_OFD_GETLK > +# > +# The basic idea is placing a type of lock in one process, > +# eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl > +# getlk with a type of lock, eg RDLCK, in another process. > +# In the end, we check the returned flock.l_type by getlk > +# to see if the lock mechanism works fine. > +# > +# We can also test these situations: > +# that the two locks are conflicting or not; > +# that open testfile RDONLY or RDWR. > +# > +#----------------------------------------------------------------------- > +# Copyright (c) 2017 Red Hat Inc. 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.* > +} > + > +# get standard environment, filters and checks > +. ./common/rc > +. ./common/filter > + > +# remove previous $seqres.full before test > +rm -f $seqres.full > + > +# Modify as appropriate. > +_supported_fs generic > +_supported_os Linux > +_require_scratch We can test in $TEST_DIR directly, don't need SCRATCH_DEV at all. Thanks, Eryu > +_require_ofd_locks > + > +# real QA test starts here > +_scratch_mkfs >> $seqres.full 2>&1 > + > +# prepare a 4k test file > +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ > + $SCRATCH_MNT/testfile >> $seqres.full 2>&1 > + > +do_test() > +{ > + local soptions="$1" > + local goptions="$2" > + echo $* >> $seqres.full 2>&1 > + # -s : do setlk > + $here/src/t_ofd_locks -s $soptions $SCRATCH_MNT/testfile & > + # -g : do getlk > + $here/src/t_ofd_locks -g $goptions $SCRATCH_MNT/testfile > + wait > +} > + > +# Always setlk at range [0,9], getlk at range [0,9] or [20,29]. > +# To open file RDONLY or RDWR should not break the locks. > + > +# -w : operate on F_WRLCK > +# -r : operate on F_RDLCK > +# -R : open file RDONLY > +# -W : open file RDWR > +# -o : file offset where the lock starts > + > +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck > +do_test "-w" "-w" > +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck > +do_test "-w" "-w -o 20" > +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck > +do_test "-w" "-r" > +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck > +do_test "-w" "-r -o 20" > +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck > +do_test "-r -R" "-w -R" > +do_test "-r" "-w" > +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck > +do_test "-r -R" "-w -o 20 -R" > +do_test "-r" "-w -o 20" > +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck > +do_test "-r -R" "-r -R" > +do_test "-r" "-r" > +# setlk rdlck [0,9], getlk rdlck [20,29], expect unlck > +do_test "-r -R" "-r -o 20 -R" > +do_test "-r" "-r -o 20" > + > +# success, all done > +status=0 > +exit > diff --git a/tests/generic/466.out b/tests/generic/466.out > new file mode 100644 > index 0000000..89c77fe > --- /dev/null > +++ b/tests/generic/466.out > @@ -0,0 +1,13 @@ > +QA output created by 466 > +get wrlck > +lock could be placed > +get wrlck > +lock could be placed > +get rdlck > +get rdlck > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > diff --git a/tests/generic/group b/tests/generic/group > index fbe0a7f..b716440 100644 > --- a/tests/generic/group > +++ b/tests/generic/group > @@ -468,3 +468,4 @@ > 463 auto quick clone dangerous > 464 auto rw > 465 auto rw quick aio > +466 auto quick > -- > 1.8.3.1 > > -- > To unsubscribe from this list: send the line "unsubscribe fstests" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4] generic: add OFD lock tests 2017-10-28 9:29 ` Eryu Guan @ 2017-10-29 2:34 ` Xiong Zhou 2017-11-10 2:46 ` [PATCH v5] " Xiong Zhou 1 sibling, 0 replies; 11+ messages in thread From: Xiong Zhou @ 2017-10-29 2:34 UTC (permalink / raw) To: Eryu Guan; +Cc: Xiong Zhou, fstests On Sat, Oct 28, 2017 at 05:29:55PM +0800, Eryu Guan wrote: > On Fri, Oct 27, 2017 at 12:59:23PM +0800, Xiong Zhou wrote: > > The basic idea is placing a type of lock in one process, > > eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl > > getlk with a type of lock, eg RDLCK, in another process. > > In the end, we check the returned flock.l_type by getlk > > to see if the lock mechanism works fine. > > > > We can also test these situations: > > that the two locks are conflicting or not; > > that open testfile RDONLY or RDWR. > > > > Signed-off-by: Xiong Zhou <xzhou@redhat.com> > > --- > > > > v4: > > add usage function to provide more info; > > move _require_test_program helper to _require_ofd_locks helper; > > simplify testrun routine; > > use testfile as token file; > > some minor fix. > > Thanks for the update! But I still see unexpected failures in release > testing, for more details please see below inline comments, along with > other new comments.. > > > > > .gitignore | 1 + > > common/rc | 12 +++ > > src/Makefile | 3 +- > > src/t_ofd_locks.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++++++ > > tests/generic/466 | 113 +++++++++++++++++++++ > > tests/generic/466.out | 13 +++ > > tests/generic/group | 1 + > > 7 files changed, 418 insertions(+), 1 deletion(-) > > create mode 100644 src/t_ofd_locks.c > > create mode 100755 tests/generic/466 > > create mode 100644 tests/generic/466.out > > > > diff --git a/.gitignore b/.gitignore > > index 2014c08..77acb42 100644 > > --- a/.gitignore > > +++ b/.gitignore > > @@ -126,6 +126,7 @@ > > /src/t_mmap_write_ro > > /src/t_mmap_writev > > /src/t_mtab > > +/src/t_ofd_locks > > /src/t_readdir_1 > > /src/t_readdir_2 > > /src/t_rename_overwrite > > diff --git a/common/rc b/common/rc > > index e2a8229..22346e4 100644 > > --- a/common/rc > > +++ b/common/rc > > @@ -3192,6 +3192,18 @@ _require_test_fcntl_advisory_locks() > > _notrun "Require fcntl advisory locks support" > > } > > > > +_require_ofd_locks() > > +{ > > + # Give a test run by setlk/getlk rdlck on testfile. > > + # If the running kernel does not support OFD locks, > > + # EINVAL will be returned. > > + _require_test_program "t_ofd_locks" > > + touch $TEST_DIR/ofd_testfile || \ > > + _notrun "Something wrong with $TEST_DIR" > > I don't think it's necessary to check touch succeed or not, if we can't > touch a new file in $TEST_DIR, the test will fail in some other ways. At > least, it should be a _fail not _notrun. This could block the test when testing on NFS in my test. Ya, _fail is better. > > > + src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1 > > + [ $? -eq 22 ] && _notrun "Require OFD locks support" > > +} > > + > > _require_test_lsattr() > > { > > testio=$(lsattr -d $TEST_DIR 2>&1) > > diff --git a/src/Makefile b/src/Makefile > > index 3eb25b1..f37af74 100644 > > --- a/src/Makefile > > +++ b/src/Makefile > > @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ > > multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ > > t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ > > holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ > > - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro > > + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ > > + t_ofd_locks > > > > LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ > > preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ > > diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c > > new file mode 100644 > > index 0000000..c8f0ad2 > > --- /dev/null > > +++ b/src/t_ofd_locks.c > > @@ -0,0 +1,276 @@ > > +#ifndef _GNU_SOURCE > > +#define _GNU_SOURCE > > +#endif > > +#include <unistd.h> > > +#include <fcntl.h> > > +#include <errno.h> > > +#include <stdio.h> > > +#include <unistd.h> > > +#include <string.h> > > +#include <stdlib.h> > > +#include <sys/stat.h> > > +#include <sys/types.h> > > +#include <sys/ipc.h> > > +#include <sys/sem.h> > > + > > +/* > > + * In distributions that do not have these macros ready in > > + * glibc-headers, compilation fails. Adding them here to avoid > > + * build errors, relevant tests would fail at the helper which > > + * requires OFD locks support and notrun if the kernel does not > > + * support OFD locks. If the kernel does support OFD locks, > > + * we are good to go. > > + * > > + */ > > +#ifndef F_OFD_GETLK > > +#define F_OFD_GETLK 36 > > +#endif > > + > > +#ifndef F_OFD_SETLK > > +#define F_OFD_SETLK 37 > > +#endif > > + > > +#ifndef F_OFD_SETLKW > > +#define F_OFD_SETLKW 38 > > +#endif > > + > > +/* This is required by semctl to set semaphore value */ > > +union semun { > > + int val; /* Value for SETVAL */ > > + struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ > > + unsigned short *array; /* Array for GETALL, SETALL */ > > + struct seminfo *__buf; /* Buffer for IPC_INFO > > + (Linux-specific) */ > > +}; > > + > > +int fd; > > + > > +static void err_exit(char *op, int errn) > > +{ > > + fprintf(stderr, "%s: %s\n", op, strerror(errn)); > > + if (fd > 0) > > + close(fd); > > + exit(errn); > > +} > > + > > +static void usage(char *arg) > > +{ > > + printf("Usage: %s [-sgrwo:RW] filename\n", arg); > > + printf("\t-s : setlk\n"); > > + printf("\t-g : getlk\n"); > > + printf("\t-r : set/get rdlck\n"); > > + printf("\t-w : set/get wrlck\n"); > > + printf("\t-o num : offset start to lock\n"); > > + printf("\t-R : open file RDONLY\n"); > > + printf("\t-W : open file RDWR\n"); > > + exit(0); > > Better to mention that the expected usage is run -s/-g in pair, e.g. a > '-s' run in background followed by a '-g' run, otherwise the test > program just hangs there. OK. > > > +} > > + > > +int main(int argc, char **argv) > > +{ > > + /* Five flags that used to specify operation details. > > Comment format issue I mentioned in my last review, and other multi-line > comments have the issue too. Sorry! I remember this was fixed. I must have missed it during test. > > > + * They can be specified via command line options. > > + * > > + * option: -s/-g > > + * lock_cmd : 1 <--> setlk > > + * 0 <--> getlk > > + * > > + * option: -r/-w > > + * lock_rw : 1 <--> set/get wrlck > > + * 0 <--> set/get rdlck > > + * > > + * option: -o num > > + * lock_start : l_start to getlk > > + * > > + * option: -R/-W > > + * open_rw : 1 <--> open file RDWR > > + * 0 <--> open file RDONLY > > + * > > + * This option is for _require_ofd_locks helper, just do > > + * fcntl setlk then return errno. > > + * option: -t > > + * testrun : 1 <--> this is a testrun, return after setlk > > + * 0 <--> this is not a testrun, run as usual > > + * > > + * By default, we setlk WRLCK on [0,9], opening file RDWR. > > + */ > > + > > + int lock_cmd = 1; > > + int lock_rw = 1; > > + int lock_start = 0; > > + int open_rw = 1; > > + int testrun = 0; > > + > > + /* > > + * We use semaphore to synchronize between getlk and setlk. > > + * > > + * Although we run getlk routine after running setlk routine > > + * in background, getlk still can be executed before setlk > > + * sometimes, which is invalid for our tests. > > + * > > + * In setlk routine, we wait getlk done, then exit, making sure > > + * the lock is still there when doing getlk. > > + * > > + * In getlk routine, we wait till setlk done firstly, making > > + * sure the lock is valid at that time, then we do getlk, > > + * after that, we tell setlk we are done, then exit. > > + */ > > + key_t semkey; > > + unsigned short vals[2]; > > + union semun arg; > > + int semid; > > + struct sembuf sop; > > + int opt; > > + > > + struct flock flk = { > > + .l_whence = SEEK_SET, > > + .l_start = 0, > > + .l_len = 10, /* lock range [0,9] */ > > Better to always specify the lock len from commandline, e.g. > > t_ofd_locks -l 10 ... > t_ofd_locks -o 20 -l 10 ... OK. > > > + .l_type = F_RDLCK, > > + .l_pid = 0, > > + }; > > + > > + if (argc < 2) { > > + usage(argv[0]); > > + return -1; > > + } > > + > > + while((opt = getopt(argc, argv, "sgrwo:RWt")) != -1) { > > + switch(opt) { > > + case 's': > > + lock_cmd = 1; > > + break; > > + case 'g': > > + lock_cmd = 0; > > + break; > > + case 'r': > > + lock_rw = 0; > > + break; > > + case 'w': > > + lock_rw = 1; > > + break; > > + case 'o': > > + lock_start = atoi(optarg); > > + break; > > + case 'R': > > + open_rw = 0; > > + break; > > + case 'W': > > + open_rw = 1; > > + break; > > + case 't': > > + testrun = 1; > > + break; > > + default: > > + usage(argv[0]); > > + return -1; > > + } > > + } > > + > > + if (lock_rw == 1) > > + flk.l_type = F_WRLCK; > > + else > > + flk.l_type = F_RDLCK; > > + > > + if (open_rw == 0) > > + fd = open(argv[optind], O_RDONLY); > > + else > > + fd = open(argv[optind], O_RDWR); > > + if (fd == -1) > > + err_exit("open", errno); > > + > > + /* In a testun, we do a fcntl getlk call and exit > > + * immediately no matter it succeeds or not. > > + */ > > + if (testrun == 1) { > > + fcntl(fd, F_OFD_GETLK, &flk); > > + err_exit("test_ofd_getlk", errno); > > + } > > + > > + /* Init the semaphore, with key related to the same file. > > + * If the sem set has not been created, we initialnize it. > > + * If it exists, we semget again to get the exist one. > > + * To make sure getlk routine and setlk routine are looking > > + * at the same semaphore set in one single round of test. > > + */ > > + if((semkey = ftok(argv[optind], 255)) == -1) > > + err_exit("ftok", errno); > > + semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL); > > + if (semid < 0) { > > + /* The sem exists or errer happens. */ > > + if (errno != EEXIST) > > + err_exit("semget0", errno); > > + > > + semid = semget(semkey, 2, 0); > > + if (semid < 0) > > + err_exit("semget1", errno); > > There's a race between creating the sem (semget) and initializing the > sem (semctl), and the sem value after creating but before initializing > is undefined, but it can be zero. So it's possible that a '-s' run > creates the sem then stops and a '-g' run sees the existing but > uninitialized sem which happens to be zero, and doest the getlock test > and sees no lock was placed wrongly. I've seen failures like this > multiple times in my release testing: > > QA output created by 466 > -get wrlck > +lock could be placed > > semget(2) manpage documents the race under "Semaphore initialization" > section and suggests a solution too, "checking for a nonzero sem_otime > in the associated data structure retrieved by a semctl(2) IPC_STAT > operation can be used to avoid races." Good catch! I didn't hit this. Thank you very much! > > But I don't see why initialize the sem in a '-g' run too, if we always > initialize sem in '-s' run, there's no such race at all. Because '-g' run could get executed before '-s' run. > > > + } else { > > + /* Init both new sem to 1. */ > > + vals[0] = 1; > > + vals[1] = 1; > > + arg.array = vals; > > + if (semctl(semid, 2, SETALL, arg) == -1) > > + err_exit("init sem", errno); > > + } > > + > > + /* setlk */ > > + if (lock_cmd == 1) { > > + if (fcntl(fd, F_OFD_SETLKW, &flk) < 0) > > + err_exit("ofd_setlkw", errno); > > + > > + /* set sem0 to 0 after setlk done */ > > + arg.val = 0; > > + if (semctl(semid, 0, SETVAL, arg) == -1) > > + err_exit("set sem0 0", errno); > > + > > + /* wating sem 1 to be zero */ > > + sop.sem_num = 1; > > + sop.sem_op = 0; > > + sop.sem_flg = 0; > > + if (semop(semid, &sop, 1) == -1) > > + err_exit("wait sem1 0", errno); > > How about using semtimedop and set a timeout value? So we don't wait > forever if something goes wrong. Good idea! > > > + > > + /* remove sem set after one round of test */ > > + if (semctl(semid, 2, IPC_RMID, arg) == -1) > > + err_exit("rmid", errno); > > + } > > + > > + /* getlck */ > > + if (lock_cmd == 0) { > > + flk.l_start = lock_start; > > + > > + /* wating sem 0 to be zero */ > > + sop.sem_num = 0; > > + sop.sem_op = 0; > > + sop.sem_flg = 0; > > + if (semop(semid, &sop, 1) == -1) > > + err_exit("wait sem0 0", errno); > > + > > + if (fcntl(fd, F_OFD_GETLK, &flk) < 0) > > + err_exit("ofd_getlk", errno); > > + > > + /* set sem1 to 0 after getlk done */ > > + arg.val = 0; > > + if (semctl(semid, 1, SETVAL, arg) == -1) > > + err_exit("set sem1 0", errno); > > + > > + switch (flk.l_type) { > > + case F_UNLCK: > > + printf("lock could be placed\n"); > > + break; > > + case F_RDLCK: > > + printf("get rdlck\n"); > > + break; > > + case F_WRLCK: > > + printf("get wrlck\n"); > > + break; > > + default: > > + printf("unknown lock type\n"); > > + close(fd); > > + return 0; > > Just print "unknown lock type" and a break. > > > + } > > + } > > + > > + close(fd); > > + return 0; > > +} > > diff --git a/tests/generic/466 b/tests/generic/466 > > new file mode 100755 > > index 0000000..80a30a5 > > --- /dev/null > > +++ b/tests/generic/466 > > @@ -0,0 +1,113 @@ > > +#! /bin/bash > > +# FS QA Test 466 > > +# > > +# Test OFD locks, F_OFD_SETLK/F_OFD_GETLK > > +# > > +# The basic idea is placing a type of lock in one process, > > +# eg WRLCK, on a testfile in SCRATCH_MNT, then do fcntl > > +# getlk with a type of lock, eg RDLCK, in another process. > > +# In the end, we check the returned flock.l_type by getlk > > +# to see if the lock mechanism works fine. > > +# > > +# We can also test these situations: > > +# that the two locks are conflicting or not; > > +# that open testfile RDONLY or RDWR. > > +# > > +#----------------------------------------------------------------------- > > +# Copyright (c) 2017 Red Hat Inc. 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.* > > +} > > + > > +# get standard environment, filters and checks > > +. ./common/rc > > +. ./common/filter > > + > > +# remove previous $seqres.full before test > > +rm -f $seqres.full > > + > > +# Modify as appropriate. > > +_supported_fs generic > > +_supported_os Linux > > +_require_scratch > > We can test in $TEST_DIR directly, don't need SCRATCH_DEV at all. OK. Thanks very much for the review! Xiong > > Thanks, > Eryu > > > +_require_ofd_locks > > + > > +# real QA test starts here > > +_scratch_mkfs >> $seqres.full 2>&1 > > + > > +# prepare a 4k test file > > +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ > > + $SCRATCH_MNT/testfile >> $seqres.full 2>&1 > > + > > +do_test() > > +{ > > + local soptions="$1" > > + local goptions="$2" > > + echo $* >> $seqres.full 2>&1 > > + # -s : do setlk > > + $here/src/t_ofd_locks -s $soptions $SCRATCH_MNT/testfile & > > + # -g : do getlk > > + $here/src/t_ofd_locks -g $goptions $SCRATCH_MNT/testfile > > + wait > > +} > > + > > +# Always setlk at range [0,9], getlk at range [0,9] or [20,29]. > > +# To open file RDONLY or RDWR should not break the locks. > > + > > +# -w : operate on F_WRLCK > > +# -r : operate on F_RDLCK > > +# -R : open file RDONLY > > +# -W : open file RDWR > > +# -o : file offset where the lock starts > > + > > +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck > > +do_test "-w" "-w" > > +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck > > +do_test "-w" "-w -o 20" > > +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck > > +do_test "-w" "-r" > > +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck > > +do_test "-w" "-r -o 20" > > +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck > > +do_test "-r -R" "-w -R" > > +do_test "-r" "-w" > > +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck > > +do_test "-r -R" "-w -o 20 -R" > > +do_test "-r" "-w -o 20" > > +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck > > +do_test "-r -R" "-r -R" > > +do_test "-r" "-r" > > +# setlk rdlck [0,9], getlk rdlck [20,29], expect unlck > > +do_test "-r -R" "-r -o 20 -R" > > +do_test "-r" "-r -o 20" > > + > > +# success, all done > > +status=0 > > +exit > > diff --git a/tests/generic/466.out b/tests/generic/466.out > > new file mode 100644 > > index 0000000..89c77fe > > --- /dev/null > > +++ b/tests/generic/466.out > > @@ -0,0 +1,13 @@ > > +QA output created by 466 > > +get wrlck > > +lock could be placed > > +get wrlck > > +lock could be placed > > +get rdlck > > +get rdlck > > +lock could be placed > > +lock could be placed > > +lock could be placed > > +lock could be placed > > +lock could be placed > > +lock could be placed > > diff --git a/tests/generic/group b/tests/generic/group > > index fbe0a7f..b716440 100644 > > --- a/tests/generic/group > > +++ b/tests/generic/group > > @@ -468,3 +468,4 @@ > > 463 auto quick clone dangerous > > 464 auto rw > > 465 auto rw quick aio > > +466 auto quick > > -- > > 1.8.3.1 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe fstests" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5] generic: add OFD lock tests 2017-10-28 9:29 ` Eryu Guan 2017-10-29 2:34 ` Xiong Zhou @ 2017-11-10 2:46 ` Xiong Zhou 2017-11-15 9:13 ` Eryu Guan 1 sibling, 1 reply; 11+ messages in thread From: Xiong Zhou @ 2017-11-10 2:46 UTC (permalink / raw) To: fstests; +Cc: eguan, Xiong Zhou Test OFD locks. Use fcntl F_OFD_SETLK/F_OFD_GETLK, to verify we are being given correct advices through getlk by kernel. The basic idea is placing lock L0 on an opened testfile in process P0, then fork(), so this lock is inherited into its child process P1 if it is OFD lock. In parent P0, we close the fd then tell getlk to go. In child P1, we keep holding the lock till getlk done. In another process P2, do fcntl getlk with lock L1 after P0 has closed the fd and while P1 is holding the lock. If we set a POSIX lock, it will be released completely in both P0 and P1 by closing that fd. In the end, we check the returned flock.l_type by getlk to see if the lock mechanism works fine. We can test different combainations by specify different lock types, lock range, POSIX or OFD lock, and these situations: + that the two locks are conflicting or not; + one OFD lock and one POSIX lock; + that open testfile RDONLY or RDWR. Signed-off-by: Xiong Zhou <xzhou@redhat.com> --- v5: Add fork() and close fd in setlk routine; Add test racing with posix lock; Only init the sem set in setlk routine; Add checking for sem set is newly created; Add checking for sem_otime to avoid the race; Wait setlk routine pid in test script; More comments. .gitignore | 1 + common/rc | 11 ++ src/Makefile | 3 +- src/t_ofd_locks.c | 419 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/generic/467 | 209 +++++++++++++++++++++++++ tests/generic/467.out | 31 ++++ tests/generic/group | 1 + 7 files changed, 674 insertions(+), 1 deletion(-) create mode 100644 src/t_ofd_locks.c create mode 100755 tests/generic/467 create mode 100644 tests/generic/467.out diff --git a/.gitignore b/.gitignore index 2014c08..77acb42 100644 --- a/.gitignore +++ b/.gitignore @@ -126,6 +126,7 @@ /src/t_mmap_write_ro /src/t_mmap_writev /src/t_mtab +/src/t_ofd_locks /src/t_readdir_1 /src/t_readdir_2 /src/t_rename_overwrite diff --git a/common/rc b/common/rc index 0de1db4..f90ceb9 100644 --- a/common/rc +++ b/common/rc @@ -3195,6 +3195,17 @@ _require_test_fcntl_advisory_locks() _notrun "Require fcntl advisory locks support" } +_require_ofd_locks() +{ + # Give a test run by getlk wrlck on testfile. + # If the running kernel does not support OFD locks, + # EINVAL will be returned. + _require_test_program "t_ofd_locks" + touch $TEST_DIR/ofd_testfile + src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1 + [ $? -eq 22 ] && _notrun "Require OFD locks support" +} + _require_test_lsattr() { testio=$(lsattr -d $TEST_DIR 2>&1) diff --git a/src/Makefile b/src/Makefile index 3eb25b1..f37af74 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ + t_ofd_locks LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c new file mode 100644 index 0000000..9133047 --- /dev/null +++ b/src/t_ofd_locks.c @@ -0,0 +1,419 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/ipc.h> +#include <sys/sem.h> + +/* + * In distributions that do not have these macros ready in + * glibc-headers, compilation fails. Adding them here to avoid + * build errors, relevant tests would fail at the helper which + * requires OFD locks support and notrun if the kernel does not + * support OFD locks. If the kernel does support OFD locks, + * we are good to go. + * + */ +#ifndef F_OFD_GETLK +#define F_OFD_GETLK 36 +#endif + +#ifndef F_OFD_SETLK +#define F_OFD_SETLK 37 +#endif + +#ifndef F_OFD_SETLKW +#define F_OFD_SETLKW 38 +#endif + +/* + * Usually we run getlk routine after running setlk routine + * in background. However, getlk could be executed before setlk + * sometimes, which is invalid for our tests. So we use semaphore + * to synchronize between getlk and setlk. + * + * When set OFD lock, it will be held after close fd. + * + * When set POSIX lock, it will be released after close fd. + * + * setlk routine: * getlk routine: + * * + * start * start + * | * | + * open file * open file + * | * | + * init sem * | + * | * | + * wait init sem done * wait init sem done + * | * | + * setlk * | + * | * | + * |------------fork()---------| * | + * | | * | + * |(child) (parent)| * | + * | | * | + * | close fd * | + * | | * | + * | set sem0=0 * wait sem0==0 + * | | * | + * | | * getlk + * | | * | + * wait sem1==0 | * set sem1=0 + * | | * | + * exit wait child * | + * | * check result + * cleanup * | + * | * | + * exit * exit + */ + +/* This is required by semctl to set semaphore value */ +union semun { + int val; /* Value for SETVAL */ + struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* Array for GETALL, SETALL */ + struct seminfo *__buf; /* Buffer for IPC_INFO + (Linux-specific) */ +}; + +int fd; +int semid; + +static void err_exit(char *op, int errn) +{ + fprintf(stderr, "%s: %s\n", op, strerror(errn)); + if (fd > 0) + close(fd); + if (semid > 0 && semctl(semid, 2, IPC_RMID) == -1) + perror("exit rmid"); + exit(errn); +} + +/* + * Six flags that used to specify operation details. + * They can be specified via command line options. + * + * option: -P + * posix : 1 <--> test posix lock + * 0 <--> test OFD lock (default) + * + * option: -s/-g + * lock_cmd : 1 <--> setlk (default) + * 0 <--> getlk + * + * option: -r/-w + * lock_rw : 1 <--> set/get wrlck (default) + * 0 <--> set/get rdlck + * + * option: -o num + * lock_start : l_start to getlk + * + * option: -R/-W + * open_rw : 1 <--> open file RDWR (default) + * 0 <--> open file RDONLY + * + * This option is for _require_ofd_locks helper, just do + * fcntl setlk then return errno. + * option: -t + * testrun : 1 <--> this is a testrun, return after setlk + * 0 <--> this is not a testrun, run as usual + * + */ +static void usage(char *arg0) +{ + printf("Usage: %s [-sgrwo:l:RWPt] filename\n", arg0); + printf("\t-s/-g : to setlk or to getlk\n"); + printf("\t-P : POSIX locks\n"); + printf("\t-r/-w : set/get rdlck/wrlck\n"); + printf("\t-o num : offset start to lock, default 0\n"); + printf("\t-l num : lock length, default 10\n"); + printf("\t-R/-W : open file RDONLY/RDWR\n\n"); + printf("\tUsually we run a setlk routine in background and then\n"); + printf("\trun a getlk routine to check. They must be paired, or\n"); + printf("\ttest will hang.\n\n"); + exit(0); +} + +int main(int argc, char **argv) +{ + pid_t pid; + int posix = 0; + int lock_cmd = 1; + int lock_rw = 1; + int lock_start = 0; + int lock_l = 10; + int open_rw = 1; + int testrun = 0; + int setlk_macro = F_OFD_SETLKW; + int getlk_macro = F_OFD_GETLK; + struct timespec ts; + + key_t semkey; + unsigned short vals[2]; + union semun semu; + struct semid_ds sem_ds; + struct sembuf sop; + int opt, ret, retry; + + while((opt = getopt(argc, argv, "sgrwo:l:PRWt")) != -1) { + switch(opt) { + case 's': + lock_cmd = 1; + break; + case 'g': + lock_cmd = 0; + break; + case 'r': + lock_rw = 0; + break; + case 'w': + lock_rw = 1; + break; + case 'o': + lock_start = atoi(optarg); + break; + case 'l': + lock_l = atoi(optarg); + break; + case 'P': + posix = 1; + break; + case 'R': + open_rw = 0; + break; + case 'W': + open_rw = 1; + break; + case 't': + testrun = 1; + break; + default: + usage(argv[0]); + return -1; + } + } + + struct flock flk = { + .l_whence = SEEK_SET, + .l_start = lock_start, + .l_len = lock_l, /* lock range [0,9] */ + .l_type = F_RDLCK, + }; + + if (optind >= argc) { + usage(argv[0]); + return -1; + } + + if (posix == 0) { + flk.l_pid = 0; + setlk_macro = F_OFD_SETLKW; + getlk_macro = F_OFD_GETLK; + } else { + setlk_macro = F_SETLKW; + getlk_macro = F_GETLK; + } + + if (lock_rw == 1) + flk.l_type = F_WRLCK; + else + flk.l_type = F_RDLCK; + + if (open_rw == 0) + fd = open(argv[optind], O_RDONLY); + else + fd = open(argv[optind], O_RDWR); + if (fd == -1) + err_exit("open", errno); + + /* + * In a testun, we do a fcntl getlk call and exit + * immediately no matter it succeeds or not. + */ + if (testrun == 1) { + fcntl(fd, F_OFD_GETLK, &flk); + err_exit("test_ofd_getlk", errno); + } + + if((semkey = ftok(argv[optind], 255)) == -1) + err_exit("ftok", errno); + + /* setlk */ + if (lock_cmd == 1) { + /* + * Init the semaphore, with a key related to the + * testfile. getlk routine will wait untill this sem + * has been created and iniialized. + * + * We must make sure the semaphore set is newly created, + * rather then the one left from last run. In which case + * getlk will exit immediately and left setlk routine + * waiting forever. Also because newly created semaphore + * has zero sem_otime, which is used here to sync with + * getlk routine. + */ + retry = 0; + do { + semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL); + if (semid < 0 && errno == EEXIST) { + /* remove sem set after one round of test */ + if (semctl(semid, 2, IPC_RMID, semu) == -1) + err_exit("rmid 0", errno); + retry++; + } else if (semid < 0) + err_exit("semget", errno); + else + retry = 10; + } while (retry < 5); + /* We can't create a new semaphore set in 5 tries */ + if (retry == 5) + err_exit("semget", errno); + + /* Init both new sem to 1 */ + vals[0] = 1; + vals[1] = 1; + semu.array = vals; + if (semctl(semid, 2, SETALL, semu) == -1) + err_exit("init sem", errno); + + /* Inc both new sem to 2 */ + sop.sem_num = 0; + sop.sem_op = 1; + sop.sem_flg = 0; + ts.tv_sec = 15; + ts.tv_nsec = 0; + if (semtimedop(semid, &sop, 1, &ts) == -1) + err_exit("inc sem0 2", errno); + + sop.sem_num = 1; + sop.sem_op = 1; + sop.sem_flg = 0; + ts.tv_sec = 15; + ts.tv_nsec = 0; + if (semtimedop(semid, &sop, 1, &ts) == -1) + err_exit("inc sem1 2", errno); + + /* + * Wait initialization complete. semctl(2) only update + * sem_ctime, semop(2) will update sem_otime. + */ + ret = -1; + do { + memset(&sem_ds, 0, sizeof(sem_ds)); + semu.buf = &sem_ds; + ret = semctl(semid, 0, IPC_STAT, semu); + } while (!(ret == 0 && sem_ds.sem_otime != 0)); + + if (fcntl(fd, setlk_macro, &flk) < 0) + err_exit("setlkw", errno); + + + /* + * fork from here after sems have been initialized, + * and lock has been placed. + * + * Then, in parent we close the fd then tell getlk to go; + * in child we keep holding the lock till getlk done. + * + */ + pid = fork(); + if (pid > 0) { + /* + * Release posix lock completely in parent and child. + * OFD lock is still held by opened file in child. + */ + if (close(fd) == -1) + perror("close"); + + /* set sem0 = 0 (setlk and close fd done) */ + semu.val = 0; + if (semctl(semid, 0, SETVAL, semu) == -1) + err_exit("set sem0 0", errno); + + /* wait child done */ + int status; + waitpid(pid, &status, 0); + exit(0); + } + + /* hold lock and wait sem1 == 0 (getlk done) */ + sop.sem_num = 1; + sop.sem_op = 0; + sop.sem_flg = 0; + ts.tv_sec = 15; + ts.tv_nsec = 0; + if (semtimedop(semid, &sop, 1, &ts) == -1) + err_exit("wait sem1 0", errno); + + /* remove sem set after one round of test */ + if (semctl(semid, 2, IPC_RMID, semu) == -1) + err_exit("rmid", errno); + + close(fd); + exit(0); + } + + /* getlck */ + if (lock_cmd == 0) { + /* wait sem created and initialized */ + do { + semid = semget(semkey, 2, 0); + if (semid != -1) + break; + if (errno == ENOENT) + continue; + else + err_exit("getlk_semget", errno); + } while (1); + + do { + memset(&sem_ds, 0, sizeof(sem_ds)); + semu.buf = &sem_ds; + ret = semctl(semid, 0, IPC_STAT, semu); + } while (!(ret == 0 && sem_ds.sem_otime != 0)); + + /* wait sem0 == 0 (setlk and close fd done) */ + sop.sem_num = 0; + sop.sem_op = 0; + sop.sem_flg = 0; + ts.tv_sec = 15; + ts.tv_nsec = 0; + if (semtimedop(semid, &sop, 1, &ts) == -1) + err_exit("wait sem0 0", errno); + + if (fcntl(fd, getlk_macro, &flk) < 0) + err_exit("getlk", errno); + + /* set sem1 = 0 (getlk done) */ + semu.val = 0; + if (semctl(semid, 1, SETVAL, semu) == -1) + err_exit("set sem1 0", errno); + + /* check result */ + switch (flk.l_type) { + case F_UNLCK: + printf("lock could be placed\n"); + break; + case F_RDLCK: + printf("get rdlck\n"); + break; + case F_WRLCK: + printf("get wrlck\n"); + break; + default: + printf("unknown lock type\n"); + break; + } + close(fd); + } + return 0; +} diff --git a/tests/generic/467 b/tests/generic/467 new file mode 100755 index 0000000..972c40e --- /dev/null +++ b/tests/generic/467 @@ -0,0 +1,209 @@ +#! /bin/bash +# FS QA Test 467 +# +# Test OFD locks. Use fcntl F_OFD_SETLK/F_OFD_GETLK, to verify +# we are being given correct advices through getlk by kernel. +# +# The basic idea is placing lock L0 on an opened testfile +# in process P0, then fork(), so this lock is inherited into +# its child process P1 if it is an OFD lock. + +# In parent P0, we close the fd then tell getlk to go. +# +# In child P1, we keep holding the lock till getlk done. +# +# In another process P2, do fcntl getlk with lock L1 after +# P0 has closed the fd and while P1 is holding the lock. +# +# If we set a POSIX lock, it will be released completely in +# both P0 and P1 by closing that fd. +# +# In the end, we check the returned flock.l_type by getlk +# to see if the lock mechanism works fine. +# +# setlk routine: * getlk routine: +# start * start +# | * | +# open file * open file +# | * | +# init sem * | +# | * | +# wait init sem done * wait init sem done +# | * | +# setlk * | +# | * | +# |---------fork()---------| * | +# | | * | +# |(child P1) (parent P0)| * | (P2) +# | | * | +# | close fd * | +# | | * | +# | set sem0=0 * wait sem0==0 +# | | * | +# | | * getlk +# | | * | +# wait sem1==0 | * set sem1=0 +# | | * | +# exit wait child * | +# | * check result +# cleanup * | +# | * | +# exit * exit +# +# We can test different combainations by specify different lock +# types, lock range, POSIX or OFD lock, and these situations: +# + that the two locks are conflicting or not; +# + one OFD lock and one POSIX lock, +# OFD lock is inherited through fork +# POSIX lock is NOT inherited through fork +# OFD lock will NOT be released by close fd in P0 +# POSIX lock will be released by close fd in P0; +# + that open testfile RDONLY or RDWR. +# +#----------------------------------------------------------------------- +# Copyright (c) 2017 Red Hat Inc. 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.* +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# remove previous $seqres.full before test +rm -f $seqres.full + +# Modify as appropriate. +_supported_fs generic +_supported_os Linux +_require_ofd_locks + +# real QA test starts here +# prepare a 4k testfile in TEST_DIR +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ + $TEST_DIR/testfile >> $seqres.full 2>&1 + +do_test() +{ + local soptions="$1" + local goptions="$2" + # add options and getlk output for debug + echo $* >> $seqres.full 2>&1 + # -s : do setlk + $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & + # -g : do getlk + $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ + tee -a $seqres.full + wait $! +} + +# Always setlk at range [0,9], getlk at range [0,9] [5,24] or [20,29]. +# To open file RDONLY or RDWR should not break the locks. +# POSIX locks should be released after closed fd, so it wont conflict +# with other locks in tests + +# -P : operate posix lock +# -w : operate on F_WRLCK +# -r : operate on F_RDLCK +# -R : open file RDONLY +# -W : open file RDWR +# -o : file offset where the lock starts +# -l : lock length + +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck +do_test "-s -w -o 0 -l 10 -W" "-g -w -o 0 -l 10 -W" "wrlck" +# setlk wrlck [0,9], getlk posix wrlck [5,24], expect wrlck +do_test "-s -w -o 0 -l 10 -W" "-g -w -o 5 -l 20 -W -P" "wrlck" +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck +do_test "-s -w -o 0 -l 10 -W" "-g -w -o 20 -l 10 -W" "unlck" +# setlk posix wrlck [0,9], getlk wrlck [5,24], expect unlck +do_test "-s -w -o 0 -l 10 -W -P" "-g -w -o 5 -l 20 -W" "unlck" +# setlk posix wrlck [0,9], getlk wrlck [20,29], expect unlck +do_test "-s -w -o 0 -l 10 -W -P" "-g -w -o 20 -l 10 -W" "unlck" + +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck +do_test "-s -w -o 0 -l 10 -W" "-g -r -o 0 -l 10 -W" "wrlck" +# setlk wrlck [0,9], getlk posix rdlck [5,24], expect wrlck +do_test "-s -w -o 0 -l 10" "-g -r -o 5 -l 20 -P" "wrlck" +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck +do_test "-s -w -o 0 -l 10" "-g -r -o 20 -l 10" "unlck" +# setlk posix wrlck [0,9], getlk rdlck [5,24], expect unlck +do_test "-s -w -o 0 -l 10 -P" "-g -r -o 5 -l 20" "unlck" +# setlk posix wrlck [0,9], getlk rdlck [20,29], expect unlck +do_test "-s -w -o 0 -l 10 -P" "-g -r -o 20 -l 10" "unlck" + +# setlk rdlck [0,9], getlk wrlck [0,9], open RDONLY, expect rdlck +do_test "-s -r -o 0 -l 10 -R" "-g -w -o 0 -l 10 -R" "rdlck" +# setlk rdlck [0,9], getlk wrlck [5,24], open RDONLY, expect rdlck +do_test "-s -r -o 0 -l 10 -R" "-g -w -o 5 -l 20 -R -P" "rdlck" +# setlk posix rdlck [0,9], getlk wrlck [5,24], open RDONLY, expect unlck +do_test "-s -r -o 0 -l 10 -R -P" "-g -w -o 5 -l 20 -R" "unlck" + +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck +do_test "-s -r -o 0 -l 10" "-g -w -o 0 -l 10" "rdlck" +# setlk rdlck [0,9], getlk posix wrlck [5,24], expect rdlck +do_test "-s -r -o 0 -l 10" "-g -w -o 5 -l 20 -P" "rdlck" +# setlk posix rdlck [0,9], getlk wrlck [5,24], expect unlck +do_test "-s -r -o 0 -l 10 -P" "-g -w -o 5 -l 20" "unlck" + +# setlk rdlck [0,9], getlk wrlck [20,29], open RDONLY, expect unlck +do_test "-s -r -o 0 -l 10 -R" "-g -w -o 20 -l 10 -R" "unlck" +# setlk posix rdlck [0,9], getlk wrlck [20,29], open RDONLY, expect unlck +do_test "-s -r -o 0 -l 10 -R -P" "-g -w -o 20 -l 10 -R" "unlck" +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck +do_test "-s -r -o 0 -l 10" "-g -w -o 20 -l 10" "unlck" +# setlk posix rdlck [0,9], getlk wrlck [20,29], expect unlck +do_test "-s -r -o 0 -l 10 -P" "-g -w -o 20 -l 10" "unlck" + +# setlk rdlck [0,9], getlk rdlck [0,9], open RDONLY, expect unlck +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 0 -l 10 -R" "unlck" +# setlk rdlck [0,9], getlk posix rdlck [0,9], open RDONLY, expect unlck +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 0 -l 10 -R -P" "unlck" +# setlk posix rdlck [0,9], getlk rdlck [0,9], open RDONLY, expect unlck +do_test "-s -r -o 0 -l 10 -R -P" "-g -r -o 0 -l 10 -R" "unlck" +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck +do_test "-s -r -o 0 -l 10" "-g -r -o 0 -l 10" "unlck" +# setlk posix rdlck [0,9], getlk rdlck [0,9], expect unlck +do_test "-s -r -o 0 -l 10 -P" "-g -r -o 0 -l 10" "unlck" + +# setlk rdlck [0,9], getlk rdlck [20,29], open RDONLY, expect unlck +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 20 -l 10 -R" "unlck" +# setlk rdlck [0,9], getlk posix rdlck [20,29], open RDONLY, expect unlck +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 20 -l 10 -R -P" "unlck" +# setlk posix rdlck [0,9], getlk rdlck [20,29], open RDONLY, expect unlck +do_test "-s -r -o 0 -l 10 -R -P" "-g -r -o 20 -l 10 -R" "unlck" +# setlk rdlck [0,9], getlk rdlck [20,29], expect unlck +do_test "-s -r -o 0 -l 10" "-g -r -o 20 -l 10" "unlck" +# setlk posix rdlck [0,9], getlk rdlck [20,29], expect unlck +do_test "-s -r -o 0 -l 10 -P" "-g -r -o 20 -l 10" "unlck" + +# success, all done +status=0 +exit diff --git a/tests/generic/467.out b/tests/generic/467.out new file mode 100644 index 0000000..577cfe2 --- /dev/null +++ b/tests/generic/467.out @@ -0,0 +1,31 @@ +QA output created by 467 +get wrlck +get wrlck +lock could be placed +lock could be placed +lock could be placed +get wrlck +get wrlck +lock could be placed +lock could be placed +lock could be placed +get rdlck +get rdlck +lock could be placed +get rdlck +get rdlck +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed diff --git a/tests/generic/group b/tests/generic/group index abaa9db..9bea64c 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -469,3 +469,4 @@ 464 auto rw 465 auto rw quick aio 466 auto quick rw +467 auto quick -- 1.8.3.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v5] generic: add OFD lock tests 2017-11-10 2:46 ` [PATCH v5] " Xiong Zhou @ 2017-11-15 9:13 ` Eryu Guan 2018-02-13 14:10 ` [PATCH v6] " Xiong Zhou 0 siblings, 1 reply; 11+ messages in thread From: Eryu Guan @ 2017-11-15 9:13 UTC (permalink / raw) To: Xiong Zhou; +Cc: fstests On Fri, Nov 10, 2017 at 10:46:55AM +0800, Xiong Zhou wrote: > Test OFD locks. Use fcntl F_OFD_SETLK/F_OFD_GETLK, to verify > we are being given correct advices through getlk by kernel. > > The basic idea is placing lock L0 on an opened testfile > in process P0, then fork(), so this lock is inherited into > its child process P1 if it is OFD lock. > > In parent P0, we close the fd then tell getlk to go. > In child P1, we keep holding the lock till getlk done. > > In another process P2, do fcntl getlk with lock L1 after > P0 has closed the fd and while P1 is holding the lock. > > If we set a POSIX lock, it will be released completely in > both P0 and P1 by closing that fd. This doesn't look correct, according to fcntl(2) POSIX locks are not inherited across fork(2), so closing the fd in one process won't affect the lock in another process. Actually this is true if you open file & take POSIX lock & open file again & close just opened fd, all in one process, the lock will be released even if the first fd is still open. And that's one of the problems OFD lock tries to solve. As we talked offline, we need to re-visit this test and summarize all the sub-tests first, and maybe test them in different tests if necessary. Thanks, Eryu > > In the end, we check the returned flock.l_type by getlk > to see if the lock mechanism works fine. > > We can test different combainations by specify different lock > types, lock range, POSIX or OFD lock, and these situations: > + that the two locks are conflicting or not; > + one OFD lock and one POSIX lock; > + that open testfile RDONLY or RDWR. > > Signed-off-by: Xiong Zhou <xzhou@redhat.com> > --- > > v5: > Add fork() and close fd in setlk routine; > Add test racing with posix lock; > Only init the sem set in setlk routine; > Add checking for sem set is newly created; > Add checking for sem_otime to avoid the race; > Wait setlk routine pid in test script; > More comments. > > > .gitignore | 1 + > common/rc | 11 ++ > src/Makefile | 3 +- > src/t_ofd_locks.c | 419 ++++++++++++++++++++++++++++++++++++++++++++++++++ > tests/generic/467 | 209 +++++++++++++++++++++++++ > tests/generic/467.out | 31 ++++ > tests/generic/group | 1 + > 7 files changed, 674 insertions(+), 1 deletion(-) > create mode 100644 src/t_ofd_locks.c > create mode 100755 tests/generic/467 > create mode 100644 tests/generic/467.out > > diff --git a/.gitignore b/.gitignore > index 2014c08..77acb42 100644 > --- a/.gitignore > +++ b/.gitignore > @@ -126,6 +126,7 @@ > /src/t_mmap_write_ro > /src/t_mmap_writev > /src/t_mtab > +/src/t_ofd_locks > /src/t_readdir_1 > /src/t_readdir_2 > /src/t_rename_overwrite > diff --git a/common/rc b/common/rc > index 0de1db4..f90ceb9 100644 > --- a/common/rc > +++ b/common/rc > @@ -3195,6 +3195,17 @@ _require_test_fcntl_advisory_locks() > _notrun "Require fcntl advisory locks support" > } > > +_require_ofd_locks() > +{ > + # Give a test run by getlk wrlck on testfile. > + # If the running kernel does not support OFD locks, > + # EINVAL will be returned. > + _require_test_program "t_ofd_locks" > + touch $TEST_DIR/ofd_testfile > + src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1 > + [ $? -eq 22 ] && _notrun "Require OFD locks support" > +} > + > _require_test_lsattr() > { > testio=$(lsattr -d $TEST_DIR 2>&1) > diff --git a/src/Makefile b/src/Makefile > index 3eb25b1..f37af74 100644 > --- a/src/Makefile > +++ b/src/Makefile > @@ -13,7 +13,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ > multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \ > t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \ > holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ > - t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro > + t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ > + t_ofd_locks > > LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ > preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ > diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c > new file mode 100644 > index 0000000..9133047 > --- /dev/null > +++ b/src/t_ofd_locks.c > @@ -0,0 +1,419 @@ > +#ifndef _GNU_SOURCE > +#define _GNU_SOURCE > +#endif > +#include <unistd.h> > +#include <fcntl.h> > +#include <errno.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <string.h> > +#include <stdlib.h> > +#include <sys/stat.h> > +#include <sys/types.h> > +#include <sys/wait.h> > +#include <sys/ipc.h> > +#include <sys/sem.h> > + > +/* > + * In distributions that do not have these macros ready in > + * glibc-headers, compilation fails. Adding them here to avoid > + * build errors, relevant tests would fail at the helper which > + * requires OFD locks support and notrun if the kernel does not > + * support OFD locks. If the kernel does support OFD locks, > + * we are good to go. > + * > + */ > +#ifndef F_OFD_GETLK > +#define F_OFD_GETLK 36 > +#endif > + > +#ifndef F_OFD_SETLK > +#define F_OFD_SETLK 37 > +#endif > + > +#ifndef F_OFD_SETLKW > +#define F_OFD_SETLKW 38 > +#endif > + > +/* > + * Usually we run getlk routine after running setlk routine > + * in background. However, getlk could be executed before setlk > + * sometimes, which is invalid for our tests. So we use semaphore > + * to synchronize between getlk and setlk. > + * > + * When set OFD lock, it will be held after close fd. > + * > + * When set POSIX lock, it will be released after close fd. > + * > + * setlk routine: * getlk routine: > + * * > + * start * start > + * | * | > + * open file * open file > + * | * | > + * init sem * | > + * | * | > + * wait init sem done * wait init sem done > + * | * | > + * setlk * | > + * | * | > + * |------------fork()---------| * | > + * | | * | > + * |(child) (parent)| * | > + * | | * | > + * | close fd * | > + * | | * | > + * | set sem0=0 * wait sem0==0 > + * | | * | > + * | | * getlk > + * | | * | > + * wait sem1==0 | * set sem1=0 > + * | | * | > + * exit wait child * | > + * | * check result > + * cleanup * | > + * | * | > + * exit * exit > + */ > + > +/* This is required by semctl to set semaphore value */ > +union semun { > + int val; /* Value for SETVAL */ > + struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ > + unsigned short *array; /* Array for GETALL, SETALL */ > + struct seminfo *__buf; /* Buffer for IPC_INFO > + (Linux-specific) */ > +}; > + > +int fd; > +int semid; > + > +static void err_exit(char *op, int errn) > +{ > + fprintf(stderr, "%s: %s\n", op, strerror(errn)); > + if (fd > 0) > + close(fd); > + if (semid > 0 && semctl(semid, 2, IPC_RMID) == -1) > + perror("exit rmid"); > + exit(errn); > +} > + > +/* > + * Six flags that used to specify operation details. > + * They can be specified via command line options. > + * > + * option: -P > + * posix : 1 <--> test posix lock > + * 0 <--> test OFD lock (default) > + * > + * option: -s/-g > + * lock_cmd : 1 <--> setlk (default) > + * 0 <--> getlk > + * > + * option: -r/-w > + * lock_rw : 1 <--> set/get wrlck (default) > + * 0 <--> set/get rdlck > + * > + * option: -o num > + * lock_start : l_start to getlk > + * > + * option: -R/-W > + * open_rw : 1 <--> open file RDWR (default) > + * 0 <--> open file RDONLY > + * > + * This option is for _require_ofd_locks helper, just do > + * fcntl setlk then return errno. > + * option: -t > + * testrun : 1 <--> this is a testrun, return after setlk > + * 0 <--> this is not a testrun, run as usual > + * > + */ > +static void usage(char *arg0) > +{ > + printf("Usage: %s [-sgrwo:l:RWPt] filename\n", arg0); > + printf("\t-s/-g : to setlk or to getlk\n"); > + printf("\t-P : POSIX locks\n"); > + printf("\t-r/-w : set/get rdlck/wrlck\n"); > + printf("\t-o num : offset start to lock, default 0\n"); > + printf("\t-l num : lock length, default 10\n"); > + printf("\t-R/-W : open file RDONLY/RDWR\n\n"); > + printf("\tUsually we run a setlk routine in background and then\n"); > + printf("\trun a getlk routine to check. They must be paired, or\n"); > + printf("\ttest will hang.\n\n"); > + exit(0); > +} > + > +int main(int argc, char **argv) > +{ > + pid_t pid; > + int posix = 0; > + int lock_cmd = 1; > + int lock_rw = 1; > + int lock_start = 0; > + int lock_l = 10; > + int open_rw = 1; > + int testrun = 0; > + int setlk_macro = F_OFD_SETLKW; > + int getlk_macro = F_OFD_GETLK; > + struct timespec ts; > + > + key_t semkey; > + unsigned short vals[2]; > + union semun semu; > + struct semid_ds sem_ds; > + struct sembuf sop; > + int opt, ret, retry; > + > + while((opt = getopt(argc, argv, "sgrwo:l:PRWt")) != -1) { > + switch(opt) { > + case 's': > + lock_cmd = 1; > + break; > + case 'g': > + lock_cmd = 0; > + break; > + case 'r': > + lock_rw = 0; > + break; > + case 'w': > + lock_rw = 1; > + break; > + case 'o': > + lock_start = atoi(optarg); > + break; > + case 'l': > + lock_l = atoi(optarg); > + break; > + case 'P': > + posix = 1; > + break; > + case 'R': > + open_rw = 0; > + break; > + case 'W': > + open_rw = 1; > + break; > + case 't': > + testrun = 1; > + break; > + default: > + usage(argv[0]); > + return -1; > + } > + } > + > + struct flock flk = { > + .l_whence = SEEK_SET, > + .l_start = lock_start, > + .l_len = lock_l, /* lock range [0,9] */ > + .l_type = F_RDLCK, > + }; > + > + if (optind >= argc) { > + usage(argv[0]); > + return -1; > + } > + > + if (posix == 0) { > + flk.l_pid = 0; > + setlk_macro = F_OFD_SETLKW; > + getlk_macro = F_OFD_GETLK; > + } else { > + setlk_macro = F_SETLKW; > + getlk_macro = F_GETLK; > + } > + > + if (lock_rw == 1) > + flk.l_type = F_WRLCK; > + else > + flk.l_type = F_RDLCK; > + > + if (open_rw == 0) > + fd = open(argv[optind], O_RDONLY); > + else > + fd = open(argv[optind], O_RDWR); > + if (fd == -1) > + err_exit("open", errno); > + > + /* > + * In a testun, we do a fcntl getlk call and exit > + * immediately no matter it succeeds or not. > + */ > + if (testrun == 1) { > + fcntl(fd, F_OFD_GETLK, &flk); > + err_exit("test_ofd_getlk", errno); > + } > + > + if((semkey = ftok(argv[optind], 255)) == -1) > + err_exit("ftok", errno); > + > + /* setlk */ > + if (lock_cmd == 1) { > + /* > + * Init the semaphore, with a key related to the > + * testfile. getlk routine will wait untill this sem > + * has been created and iniialized. > + * > + * We must make sure the semaphore set is newly created, > + * rather then the one left from last run. In which case > + * getlk will exit immediately and left setlk routine > + * waiting forever. Also because newly created semaphore > + * has zero sem_otime, which is used here to sync with > + * getlk routine. > + */ > + retry = 0; > + do { > + semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL); > + if (semid < 0 && errno == EEXIST) { > + /* remove sem set after one round of test */ > + if (semctl(semid, 2, IPC_RMID, semu) == -1) > + err_exit("rmid 0", errno); > + retry++; > + } else if (semid < 0) > + err_exit("semget", errno); > + else > + retry = 10; > + } while (retry < 5); > + /* We can't create a new semaphore set in 5 tries */ > + if (retry == 5) > + err_exit("semget", errno); > + > + /* Init both new sem to 1 */ > + vals[0] = 1; > + vals[1] = 1; > + semu.array = vals; > + if (semctl(semid, 2, SETALL, semu) == -1) > + err_exit("init sem", errno); > + > + /* Inc both new sem to 2 */ > + sop.sem_num = 0; > + sop.sem_op = 1; > + sop.sem_flg = 0; > + ts.tv_sec = 15; > + ts.tv_nsec = 0; > + if (semtimedop(semid, &sop, 1, &ts) == -1) > + err_exit("inc sem0 2", errno); > + > + sop.sem_num = 1; > + sop.sem_op = 1; > + sop.sem_flg = 0; > + ts.tv_sec = 15; > + ts.tv_nsec = 0; > + if (semtimedop(semid, &sop, 1, &ts) == -1) > + err_exit("inc sem1 2", errno); > + > + /* > + * Wait initialization complete. semctl(2) only update > + * sem_ctime, semop(2) will update sem_otime. > + */ > + ret = -1; > + do { > + memset(&sem_ds, 0, sizeof(sem_ds)); > + semu.buf = &sem_ds; > + ret = semctl(semid, 0, IPC_STAT, semu); > + } while (!(ret == 0 && sem_ds.sem_otime != 0)); > + > + if (fcntl(fd, setlk_macro, &flk) < 0) > + err_exit("setlkw", errno); > + > + > + /* > + * fork from here after sems have been initialized, > + * and lock has been placed. > + * > + * Then, in parent we close the fd then tell getlk to go; > + * in child we keep holding the lock till getlk done. > + * > + */ > + pid = fork(); > + if (pid > 0) { > + /* > + * Release posix lock completely in parent and child. > + * OFD lock is still held by opened file in child. > + */ > + if (close(fd) == -1) > + perror("close"); > + > + /* set sem0 = 0 (setlk and close fd done) */ > + semu.val = 0; > + if (semctl(semid, 0, SETVAL, semu) == -1) > + err_exit("set sem0 0", errno); > + > + /* wait child done */ > + int status; > + waitpid(pid, &status, 0); > + exit(0); > + } > + > + /* hold lock and wait sem1 == 0 (getlk done) */ > + sop.sem_num = 1; > + sop.sem_op = 0; > + sop.sem_flg = 0; > + ts.tv_sec = 15; > + ts.tv_nsec = 0; > + if (semtimedop(semid, &sop, 1, &ts) == -1) > + err_exit("wait sem1 0", errno); > + > + /* remove sem set after one round of test */ > + if (semctl(semid, 2, IPC_RMID, semu) == -1) > + err_exit("rmid", errno); > + > + close(fd); > + exit(0); > + } > + > + /* getlck */ > + if (lock_cmd == 0) { > + /* wait sem created and initialized */ > + do { > + semid = semget(semkey, 2, 0); > + if (semid != -1) > + break; > + if (errno == ENOENT) > + continue; > + else > + err_exit("getlk_semget", errno); > + } while (1); > + > + do { > + memset(&sem_ds, 0, sizeof(sem_ds)); > + semu.buf = &sem_ds; > + ret = semctl(semid, 0, IPC_STAT, semu); > + } while (!(ret == 0 && sem_ds.sem_otime != 0)); > + > + /* wait sem0 == 0 (setlk and close fd done) */ > + sop.sem_num = 0; > + sop.sem_op = 0; > + sop.sem_flg = 0; > + ts.tv_sec = 15; > + ts.tv_nsec = 0; > + if (semtimedop(semid, &sop, 1, &ts) == -1) > + err_exit("wait sem0 0", errno); > + > + if (fcntl(fd, getlk_macro, &flk) < 0) > + err_exit("getlk", errno); > + > + /* set sem1 = 0 (getlk done) */ > + semu.val = 0; > + if (semctl(semid, 1, SETVAL, semu) == -1) > + err_exit("set sem1 0", errno); > + > + /* check result */ > + switch (flk.l_type) { > + case F_UNLCK: > + printf("lock could be placed\n"); > + break; > + case F_RDLCK: > + printf("get rdlck\n"); > + break; > + case F_WRLCK: > + printf("get wrlck\n"); > + break; > + default: > + printf("unknown lock type\n"); > + break; > + } > + close(fd); > + } > + return 0; > +} > diff --git a/tests/generic/467 b/tests/generic/467 > new file mode 100755 > index 0000000..972c40e > --- /dev/null > +++ b/tests/generic/467 > @@ -0,0 +1,209 @@ > +#! /bin/bash > +# FS QA Test 467 > +# > +# Test OFD locks. Use fcntl F_OFD_SETLK/F_OFD_GETLK, to verify > +# we are being given correct advices through getlk by kernel. > +# > +# The basic idea is placing lock L0 on an opened testfile > +# in process P0, then fork(), so this lock is inherited into > +# its child process P1 if it is an OFD lock. > + > +# In parent P0, we close the fd then tell getlk to go. > +# > +# In child P1, we keep holding the lock till getlk done. > +# > +# In another process P2, do fcntl getlk with lock L1 after > +# P0 has closed the fd and while P1 is holding the lock. > +# > +# If we set a POSIX lock, it will be released completely in > +# both P0 and P1 by closing that fd. > +# > +# In the end, we check the returned flock.l_type by getlk > +# to see if the lock mechanism works fine. > +# > +# setlk routine: * getlk routine: > +# start * start > +# | * | > +# open file * open file > +# | * | > +# init sem * | > +# | * | > +# wait init sem done * wait init sem done > +# | * | > +# setlk * | > +# | * | > +# |---------fork()---------| * | > +# | | * | > +# |(child P1) (parent P0)| * | (P2) > +# | | * | > +# | close fd * | > +# | | * | > +# | set sem0=0 * wait sem0==0 > +# | | * | > +# | | * getlk > +# | | * | > +# wait sem1==0 | * set sem1=0 > +# | | * | > +# exit wait child * | > +# | * check result > +# cleanup * | > +# | * | > +# exit * exit > +# > +# We can test different combainations by specify different lock > +# types, lock range, POSIX or OFD lock, and these situations: > +# + that the two locks are conflicting or not; > +# + one OFD lock and one POSIX lock, > +# OFD lock is inherited through fork > +# POSIX lock is NOT inherited through fork > +# OFD lock will NOT be released by close fd in P0 > +# POSIX lock will be released by close fd in P0; > +# + that open testfile RDONLY or RDWR. > +# > +#----------------------------------------------------------------------- > +# Copyright (c) 2017 Red Hat Inc. 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.* > +} > + > +# get standard environment, filters and checks > +. ./common/rc > +. ./common/filter > + > +# remove previous $seqres.full before test > +rm -f $seqres.full > + > +# Modify as appropriate. > +_supported_fs generic > +_supported_os Linux > +_require_ofd_locks > + > +# real QA test starts here > +# prepare a 4k testfile in TEST_DIR > +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ > + $TEST_DIR/testfile >> $seqres.full 2>&1 > + > +do_test() > +{ > + local soptions="$1" > + local goptions="$2" > + # add options and getlk output for debug > + echo $* >> $seqres.full 2>&1 > + # -s : do setlk > + $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & > + # -g : do getlk > + $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ > + tee -a $seqres.full > + wait $! > +} > + > +# Always setlk at range [0,9], getlk at range [0,9] [5,24] or [20,29]. > +# To open file RDONLY or RDWR should not break the locks. > +# POSIX locks should be released after closed fd, so it wont conflict > +# with other locks in tests > + > +# -P : operate posix lock > +# -w : operate on F_WRLCK > +# -r : operate on F_RDLCK > +# -R : open file RDONLY > +# -W : open file RDWR > +# -o : file offset where the lock starts > +# -l : lock length > + > +# setlk wrlck [0,9], getlk wrlck [0,9], expect wrlck > +do_test "-s -w -o 0 -l 10 -W" "-g -w -o 0 -l 10 -W" "wrlck" > +# setlk wrlck [0,9], getlk posix wrlck [5,24], expect wrlck > +do_test "-s -w -o 0 -l 10 -W" "-g -w -o 5 -l 20 -W -P" "wrlck" > +# setlk wrlck [0,9], getlk wrlck [20,29], expect unlck > +do_test "-s -w -o 0 -l 10 -W" "-g -w -o 20 -l 10 -W" "unlck" > +# setlk posix wrlck [0,9], getlk wrlck [5,24], expect unlck > +do_test "-s -w -o 0 -l 10 -W -P" "-g -w -o 5 -l 20 -W" "unlck" > +# setlk posix wrlck [0,9], getlk wrlck [20,29], expect unlck > +do_test "-s -w -o 0 -l 10 -W -P" "-g -w -o 20 -l 10 -W" "unlck" > + > +# setlk wrlck [0,9], getlk rdlck [0,9], expect wrlck > +do_test "-s -w -o 0 -l 10 -W" "-g -r -o 0 -l 10 -W" "wrlck" > +# setlk wrlck [0,9], getlk posix rdlck [5,24], expect wrlck > +do_test "-s -w -o 0 -l 10" "-g -r -o 5 -l 20 -P" "wrlck" > +# setlk wrlck [0,9], getlk rdlck [20,29], expect unlck > +do_test "-s -w -o 0 -l 10" "-g -r -o 20 -l 10" "unlck" > +# setlk posix wrlck [0,9], getlk rdlck [5,24], expect unlck > +do_test "-s -w -o 0 -l 10 -P" "-g -r -o 5 -l 20" "unlck" > +# setlk posix wrlck [0,9], getlk rdlck [20,29], expect unlck > +do_test "-s -w -o 0 -l 10 -P" "-g -r -o 20 -l 10" "unlck" > + > +# setlk rdlck [0,9], getlk wrlck [0,9], open RDONLY, expect rdlck > +do_test "-s -r -o 0 -l 10 -R" "-g -w -o 0 -l 10 -R" "rdlck" > +# setlk rdlck [0,9], getlk wrlck [5,24], open RDONLY, expect rdlck > +do_test "-s -r -o 0 -l 10 -R" "-g -w -o 5 -l 20 -R -P" "rdlck" > +# setlk posix rdlck [0,9], getlk wrlck [5,24], open RDONLY, expect unlck > +do_test "-s -r -o 0 -l 10 -R -P" "-g -w -o 5 -l 20 -R" "unlck" > + > +# setlk rdlck [0,9], getlk wrlck [0,9], expect rdlck > +do_test "-s -r -o 0 -l 10" "-g -w -o 0 -l 10" "rdlck" > +# setlk rdlck [0,9], getlk posix wrlck [5,24], expect rdlck > +do_test "-s -r -o 0 -l 10" "-g -w -o 5 -l 20 -P" "rdlck" > +# setlk posix rdlck [0,9], getlk wrlck [5,24], expect unlck > +do_test "-s -r -o 0 -l 10 -P" "-g -w -o 5 -l 20" "unlck" > + > +# setlk rdlck [0,9], getlk wrlck [20,29], open RDONLY, expect unlck > +do_test "-s -r -o 0 -l 10 -R" "-g -w -o 20 -l 10 -R" "unlck" > +# setlk posix rdlck [0,9], getlk wrlck [20,29], open RDONLY, expect unlck > +do_test "-s -r -o 0 -l 10 -R -P" "-g -w -o 20 -l 10 -R" "unlck" > +# setlk rdlck [0,9], getlk wrlck [20,29], expect unlck > +do_test "-s -r -o 0 -l 10" "-g -w -o 20 -l 10" "unlck" > +# setlk posix rdlck [0,9], getlk wrlck [20,29], expect unlck > +do_test "-s -r -o 0 -l 10 -P" "-g -w -o 20 -l 10" "unlck" > + > +# setlk rdlck [0,9], getlk rdlck [0,9], open RDONLY, expect unlck > +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 0 -l 10 -R" "unlck" > +# setlk rdlck [0,9], getlk posix rdlck [0,9], open RDONLY, expect unlck > +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 0 -l 10 -R -P" "unlck" > +# setlk posix rdlck [0,9], getlk rdlck [0,9], open RDONLY, expect unlck > +do_test "-s -r -o 0 -l 10 -R -P" "-g -r -o 0 -l 10 -R" "unlck" > +# setlk rdlck [0,9], getlk rdlck [0,9], expect unlck > +do_test "-s -r -o 0 -l 10" "-g -r -o 0 -l 10" "unlck" > +# setlk posix rdlck [0,9], getlk rdlck [0,9], expect unlck > +do_test "-s -r -o 0 -l 10 -P" "-g -r -o 0 -l 10" "unlck" > + > +# setlk rdlck [0,9], getlk rdlck [20,29], open RDONLY, expect unlck > +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 20 -l 10 -R" "unlck" > +# setlk rdlck [0,9], getlk posix rdlck [20,29], open RDONLY, expect unlck > +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 20 -l 10 -R -P" "unlck" > +# setlk posix rdlck [0,9], getlk rdlck [20,29], open RDONLY, expect unlck > +do_test "-s -r -o 0 -l 10 -R -P" "-g -r -o 20 -l 10 -R" "unlck" > +# setlk rdlck [0,9], getlk rdlck [20,29], expect unlck > +do_test "-s -r -o 0 -l 10" "-g -r -o 20 -l 10" "unlck" > +# setlk posix rdlck [0,9], getlk rdlck [20,29], expect unlck > +do_test "-s -r -o 0 -l 10 -P" "-g -r -o 20 -l 10" "unlck" > + > +# success, all done > +status=0 > +exit > diff --git a/tests/generic/467.out b/tests/generic/467.out > new file mode 100644 > index 0000000..577cfe2 > --- /dev/null > +++ b/tests/generic/467.out > @@ -0,0 +1,31 @@ > +QA output created by 467 > +get wrlck > +get wrlck > +lock could be placed > +lock could be placed > +lock could be placed > +get wrlck > +get wrlck > +lock could be placed > +lock could be placed > +lock could be placed > +get rdlck > +get rdlck > +lock could be placed > +get rdlck > +get rdlck > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > +lock could be placed > diff --git a/tests/generic/group b/tests/generic/group > index abaa9db..9bea64c 100644 > --- a/tests/generic/group > +++ b/tests/generic/group > @@ -469,3 +469,4 @@ > 464 auto rw > 465 auto rw quick aio > 466 auto quick rw > +467 auto quick > -- > 1.8.3.1 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v6] generic: add OFD lock tests 2017-11-15 9:13 ` Eryu Guan @ 2018-02-13 14:10 ` Xiong Zhou 0 siblings, 0 replies; 11+ messages in thread From: Xiong Zhou @ 2018-02-13 14:10 UTC (permalink / raw) To: fstests; +Cc: eguan, Xiong Zhou Test OFD locks. Use fcntl F_OFD_SETLK/F_OFD_GETLK, to verify we are being given correct advices through getlk by kernel. The basic idea is one setlk routine setting locks via fcntl *_SETLK, followed by operations like clone, dup then close fd; another routine getlk getting locks via fcntl *_GETLK. Firstly in setlk routine process P0, place a lock L0 on an opened testfile, then do clone or dup and close relative fd. In getlk process P2, do fcntl *_GETLK with lock L1 after get notified by setlk routine. In the end, getlk routine check the returned struct flock.l_type to see if the lock mechanism works fine. Test combainations of: + shared or exclusive lock + these locks are conflicting or not + one OFD lock and one POSIX lock + that open testfile RDONLY or RDWR + clone with CLONE_FILES or not + dup and close newfd Signed-off-by: Xiong Zhou <xzhou@redhat.com> --- v6: use clone(2) instead of fork(2) close fd in child not parent when clone add dup & close newfd to affect lock rebase to post fsnotify stress case fix comments .gitignore | 1 + common/rc | 11 ++ src/Makefile | 2 +- src/t_ofd_locks.c | 425 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/generic/479 | 249 +++++++++++++++++++++++++++++ tests/generic/479.out | 91 +++++++++++ tests/generic/group | 1 + 7 files changed, 779 insertions(+), 1 deletion(-) create mode 100644 src/t_ofd_locks.c create mode 100755 tests/generic/479 create mode 100644 tests/generic/479.out diff --git a/.gitignore b/.gitignore index ae01ed3..4da2a18 100644 --- a/.gitignore +++ b/.gitignore @@ -129,6 +129,7 @@ /src/t_mmap_write_ro /src/t_mmap_writev /src/t_mtab +/src/t_ofd_locks /src/t_readdir_1 /src/t_readdir_2 /src/t_rename_overwrite diff --git a/common/rc b/common/rc index 4ad59b1..a6caca9 100644 --- a/common/rc +++ b/common/rc @@ -3308,6 +3308,17 @@ _require_test_fcntl_advisory_locks() _notrun "Require fcntl advisory locks support" } +_require_ofd_locks() +{ + # Give a test run by getlk wrlck on testfile. + # If the running kernel does not support OFD locks, + # EINVAL will be returned. + _require_test_program "t_ofd_locks" + touch $TEST_DIR/ofd_testfile + src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1 + [ $? -eq 22 ] && _notrun "Require OFD locks support" +} + _require_test_lsattr() { testio=$(lsattr -d $TEST_DIR 2>&1) diff --git a/src/Makefile b/src/Makefile index 6b9f296..c63c80c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -15,7 +15,7 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \ t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \ t_ext4_dax_journal_corruption t_ext4_dax_inline_corruption \ - fsnotify_stress + fsnotify_stress t_ofd_locks LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c new file mode 100644 index 0000000..fa5ccfe --- /dev/null +++ b/src/t_ofd_locks.c @@ -0,0 +1,425 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sched.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/ipc.h> +#include <sys/sem.h> + +/* + * In distributions that do not have these macros ready in + * glibc-headers, compilation fails. Adding them here to avoid + * build errors, relevant tests would fail at the helper which + * requires OFD locks support and notrun if the kernel does not + * support OFD locks. If the kernel does support OFD locks, + * we are good to go. + * + */ +#ifndef F_OFD_GETLK +#define F_OFD_GETLK 36 +#endif + +#ifndef F_OFD_SETLK +#define F_OFD_SETLK 37 +#endif + +#ifndef F_OFD_SETLKW +#define F_OFD_SETLKW 38 +#endif + +/* + * Usually we run getlk routine after running setlk routine + * in background. However, getlk could be executed before setlk + * sometimes, which is invalid for our tests. So we use semaphore + * to synchronize between getlk and setlk. + * + * setlk routine: * getlk routine: + * * + * start * start + * | * | + * open file * open file + * | * | + * init sem * | + * | * | + * wait init sem done * wait init sem done + * | * | + * setlk * | + * | * | + * |------------clone()--------| * | + * | | * | + * |(parent) (child)| * | + * | | * | + * | close fd * | + * | | * | + * | set sem0=0 * wait sem0==0 + * | | * | + * | | * getlk + * | | * | + * wait sem1==0 | * set sem1=0 + * | | * | + * wait child | * | + * | | * check result + * | | * | + * exit exit * exit + */ + +/* This is required by semctl to set semaphore value */ +union semun { + int val; /* Value for SETVAL */ + struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* Array for GETALL, SETALL */ + struct seminfo *__buf; /* Buffer for IPC_INFO + (Linux-specific) */ +}; +static int fd; +static int semid; +static void err_exit(char *op, int errn) +{ + fprintf(stderr, "%s: %s\n", op, strerror(errn)); + if (fd > 0) + close(fd); + if (semid > 0 && semctl(semid, 2, IPC_RMID) == -1) + perror("exit rmid"); + exit(errn); +} +/* + * Flags that used to specify operation details. + * They can be specified via command line options. + * + * option: -P + * posix : 1 <--> test posix lock + * 0 <--> test OFD lock (default) + * + * option: -s/-g + * lock_cmd : 1 <--> setlk (default) + * 0 <--> getlk + * + * option: -r/-w + * lock_rw : 1 <--> set/get wrlck (default) + * 0 <--> set/get rdlck + * + * option: -o num + * lock_start : l_start to getlk + * + * option: -F + * clone_fs : clone with CLONE_FILES + * + * option: -d + * use_dup : dup and close to setup condition in setlk + * + * option: -R/-W + * open_rw : 1 <--> open file RDWR (default) + * 0 <--> open file RDONLY + * + * This option is for _require_ofd_locks helper, just do + * fcntl setlk then return errno. + * option: -t + * testrun : 1 <--> this is a testrun, return after setlk + * 0 <--> this is not a testrun, run as usual + * + */ +static void usage(char *arg0) +{ + printf("Usage: %s [-sgrwo:l:RWPtFd] filename\n", arg0); + printf("\t-s/-g : to setlk or to getlk\n"); + printf("\t-P : POSIX locks\n"); + printf("\t-F : clone with CLONE_FILES in setlk to setup test condition\n"); + printf("\t-d : dup and close in setlk\n"); + printf("\twithout both -F/d, use clone without CLONE_FILES\n"); + printf("\t-r/-w : set/get rdlck/wrlck\n"); + printf("\t-o num : offset start to lock, default 0\n"); + printf("\t-l num : lock length, default 10\n"); + printf("\t-R/-W : open file RDONLY/RDWR\n\n"); + printf("\tUsually we run a setlk routine in background and then\n"); + printf("\trun a getlk routine to check. They must be paired, or\n"); + printf("\ttest will hang.\n\n"); + exit(0); +} + +static char child_stack[1048576]; +static int child_fn(void* p) +{ + union semun semu; + int cfd = *(int *)p; + /* + * close relative fd + */ + if (cfd > 0 && close(cfd) == -1) + perror("c-close"); + /* set sem0 = 0 (setlk and close fd done) */ + semu.val = 0; + if (semctl(semid, 0, SETVAL, semu) == -1) + err_exit("set sem0 0", errno); + return 0; +} + +int main(int argc, char **argv) +{ + int posix = 0; + int lock_cmd = 1; + int lock_rw = 1; + int lock_start = 0; + int lock_l = 10; + int open_rw = 1; + int clone_fs = 0; + int use_dup = 0; + int testrun = 0; + int setlk_macro = F_OFD_SETLKW; + int getlk_macro = F_OFD_GETLK; + struct timespec ts; + key_t semkey; + unsigned short vals[2]; + union semun semu; + struct semid_ds sem_ds; + struct sembuf sop; + int opt, ret, retry; + + while((opt = getopt(argc, argv, "sgrwo:l:PRWtFd")) != -1) { + switch(opt) { + case 's': + lock_cmd = 1; + break; + case 'g': + lock_cmd = 0; + break; + case 'r': + lock_rw = 0; + break; + case 'w': + lock_rw = 1; + break; + case 'o': + lock_start = atoi(optarg); + break; + case 'l': + lock_l = atoi(optarg); + break; + case 'P': + posix = 1; + break; + case 'R': + open_rw = 0; + break; + case 'W': + open_rw = 1; + break; + case 't': + testrun = 1; + break; + case 'F': + clone_fs = 1; + break; + case 'd': + use_dup = 1; + break; + default: + usage(argv[0]); + return -1; + } + } + struct flock flk = { + .l_whence = SEEK_SET, + .l_start = lock_start, + .l_len = lock_l, /* lock range [0,9] */ + .l_type = F_RDLCK, + }; + if (optind >= argc) { + usage(argv[0]); + return -1; + } + if (posix == 0) { + flk.l_pid = 0; + setlk_macro = F_OFD_SETLKW; + getlk_macro = F_OFD_GETLK; + } else { + setlk_macro = F_SETLKW; + getlk_macro = F_GETLK; + } + if (lock_rw == 1) + flk.l_type = F_WRLCK; + else + flk.l_type = F_RDLCK; + if (open_rw == 0) + fd = open(argv[optind], O_RDONLY); + else + fd = open(argv[optind], O_RDWR); + if (fd == -1) + err_exit("open", errno); + /* + * In a testun, we do a fcntl getlk call and exit + * immediately no matter it succeeds or not. + */ + if (testrun == 1) { + fcntl(fd, F_OFD_GETLK, &flk); + err_exit("test_ofd_getlk", errno); + } + if((semkey = ftok(argv[optind], 255)) == -1) + err_exit("ftok", errno); + + /* setlk init the semaphore */ + if (lock_cmd == 1) { + /* + * Init the semaphore, with a key related to the + * testfile. getlk routine will wait untill this sem + * has been created and iniialized. + * + * We must make sure the semaphore set is newly created, + * rather then the one left from last run. In which case + * getlk will exit immediately and left setlk routine + * waiting forever. Also because newly created semaphore + * has zero sem_otime, which is used here to sync with + * getlk routine. + */ + retry = 0; + do { + semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL); + if (semid < 0 && errno == EEXIST) { + /* remove sem set after one round of test */ + if (semctl(semid, 2, IPC_RMID, semu) == -1) + err_exit("rmid 0", errno); + retry++; + } else if (semid < 0) + err_exit("semget", errno); + else + retry = 10; + } while (retry < 5); + /* We can't create a new semaphore set in 5 tries */ + if (retry == 5) + err_exit("semget", errno); + /* Init both new sem to 1 */ + vals[0] = 1; + vals[1] = 1; + semu.array = vals; + if (semctl(semid, 2, SETALL, semu) == -1) + err_exit("init sem", errno); + /* Inc both new sem to 2 */ + sop.sem_num = 0; + sop.sem_op = 1; + sop.sem_flg = 0; + ts.tv_sec = 15; + ts.tv_nsec = 0; + if (semtimedop(semid, &sop, 1, &ts) == -1) + err_exit("inc sem0 2", errno); + sop.sem_num = 1; + sop.sem_op = 1; + sop.sem_flg = 0; + ts.tv_sec = 15; + ts.tv_nsec = 0; + if (semtimedop(semid, &sop, 1, &ts) == -1) + err_exit("inc sem1 2", errno); + /* + * Wait initialization complete. semctl(2) only update + * sem_ctime, semop(2) will update sem_otime. + */ + ret = -1; + do { + memset(&sem_ds, 0, sizeof(sem_ds)); + semu.buf = &sem_ds; + ret = semctl(semid, 0, IPC_STAT, semu); + } while (!(ret == 0 && sem_ds.sem_otime != 0)); + /* place the lock */ + if (fcntl(fd, setlk_macro, &flk) < 0) + err_exit("setlkw", errno); + } + if (lock_cmd == 1 && use_dup == 1) { + /* dup fd and close the newfd */ + int dfd = dup(fd); + if (dfd == -1) + err_exit("dup", errno); + close(dfd); + /* set sem0 = 0 (setlk and close fd done) */ + semu.val = 0; + if (semctl(semid, 0, SETVAL, semu) == -1) + err_exit("set sem0 0", errno); + } + if (lock_cmd == 1 && use_dup == 0) { + /* + * clone a child to close the fd then tell getlk to go; + * in parent we keep holding the lock till getlk done. + */ + pid_t child_pid = 0; + if (clone_fs) + child_pid = clone(child_fn, child_stack+1048576, + CLONE_FILES|CLONE_SYSVSEM|SIGCHLD, &fd); + else + child_pid = clone(child_fn, child_stack+1048576, + CLONE_SYSVSEM|SIGCHLD, &fd); + if (child_pid == -1) + err_exit("clone", errno); + /* wait child done */ + waitpid(child_pid, NULL, 0); + } + if (lock_cmd == 1) { + /* "hold" lock and wait sem1 == 0 (getlk done) */ + sop.sem_num = 1; + sop.sem_op = 0; + sop.sem_flg = 0; + ts.tv_sec = 15; + ts.tv_nsec = 0; + if (semtimedop(semid, &sop, 1, &ts) == -1) + err_exit("wait sem1 0", errno); + /* remove sem set after one round of test */ + if (semctl(semid, 2, IPC_RMID, semu) == -1) + err_exit("rmid", errno); + close(fd); + exit(0); + } + /* getlck */ + if (lock_cmd == 0) { + /* wait sem created and initialized */ + do { + semid = semget(semkey, 2, 0); + if (semid != -1) + break; + if (errno == ENOENT) + continue; + else + err_exit("getlk_semget", errno); + } while (1); + do { + memset(&sem_ds, 0, sizeof(sem_ds)); + semu.buf = &sem_ds; + ret = semctl(semid, 0, IPC_STAT, semu); + } while (!(ret == 0 && sem_ds.sem_otime != 0)); + /* wait sem0 == 0 (setlk and close fd done) */ + sop.sem_num = 0; + sop.sem_op = 0; + sop.sem_flg = 0; + ts.tv_sec = 15; + ts.tv_nsec = 0; + if (semtimedop(semid, &sop, 1, &ts) == -1) + err_exit("wait sem0 0", errno); + if (fcntl(fd, getlk_macro, &flk) < 0) + err_exit("getlk", errno); + /* set sem1 = 0 (getlk done) */ + semu.val = 0; + if (semctl(semid, 1, SETVAL, semu) == -1) + err_exit("set sem1 0", errno); + /* check result */ + switch (flk.l_type) { + case F_UNLCK: + printf("lock could be placed\n"); + break; + case F_RDLCK: + printf("get rdlck\n"); + break; + case F_WRLCK: + printf("get wrlck\n"); + break; + default: + printf("unknown lock type\n"); + break; + } + close(fd); + } + return 0; +} diff --git a/tests/generic/479 b/tests/generic/479 new file mode 100755 index 0000000..bd2bdeb --- /dev/null +++ b/tests/generic/479 @@ -0,0 +1,249 @@ +#! /bin/bash +# FS QA Test 479 +# +# Test OFD lock. fcntl F_OFD_SETLK to set lock, then F_OFD_GETLK +# to verify we are being given correct advice by kernel. +# +# OFD lock combines POSIX lock and BSD flock: +# + does not share between threads +# + byte granularity +# (both tested by LTP/fcntl3{4,6}) +# + only release automatically after all open fd closed +# +# This test target the third one and expand a little bit. +# +# The basic idea is one setlk routine setting locks via fcntl +# *_SETLK, followed by operations like clone, dup then close fd; +# another routine getlk getting locks via fcntl *_GETLK. +# +# Firstly in setlk routine process P0, place a lock L0 on an +# opened testfile, then +# +# + clone() a child P1 to close the fd then tell getlk to go, +# | parent P0 wait getlk done then close fd. +# or +# + dup() fd to a newfd then close newfd then tell getlk to go, +# then wait getlk done then close fd. +# +# In getlk process P2, do fcntl *_GETLK with lock L1 after get +# notified by setlk routine. +# +# In the end, getlk routine check the returned struct flock.l_type +# to see if the lock mechanism works fine. +# +# When testing with clone, +# + CLONE_FILES set, close releases all locks; +# + CLONE_FILES not set, locks remain in P0; +# +# If L0 is a POSIX lock, +# + it is not inherited into P1 +# + it is released after dup & close +# +# If L0 is a OFD lock, +# + it is inherited into P1 +# + it is not released after dup & close +# +# setlk routine: * getlk routine: +# start * start +# | * | +# open file * open file +# | * | +# init sem * | +# | * | +# wait init sem done * wait init sem done +# | * | +# setlk L0 * | +# | * | +# |---------clone()--------| * | +# | | * | +# |(child P1) (parent P0)| * | (P2) +# | | * | +# | close fd * | +# | | * | +# | set sem0=0 * wait sem0==0 +# | | * | +# | | * getlk L1 +# | | * | +# wait sem1==0 | * set sem1=0 +# | | * | +# exit wait child * | +# | * check result +# cleanup * | +# | * | +# exit * exit +# +# We can test combainations of: +# + shared or exclusive lock +# + these locks are conflicting or not +# + one OFD lock and one POSIX lock +# + that open testfile RDONLY or RDWR +# + clone with CLONE_FILES or not +# + dup and close newfd +# +#----------------------------------------------------------------------- +# Copyright (c) 2018 Red Hat Inc. 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.* +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# remove previous $seqres.full before test +rm -f $seqres.full + +# Modify as appropriate. +_supported_fs generic +_supported_os Linux +_require_ofd_locks + +# real QA test starts here +# prepare a 4k testfile in TEST_DIR +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ + $TEST_DIR/testfile >> $seqres.full 2>&1 + +do_test() +{ + local soptions="$1" + local goptions="$2" + # print options and getlk output for debug + echo $* >> $seqres.full 2>&1 + # -s : do setlk + $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & + # -g : do getlk + $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ + tee -a $seqres.full + wait $! + + # add -F to clone with CLONE_FILES + soptions="$1 -F" + # with -F, new locks are always file to place + $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & + $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ + tee -a $seqres.full + wait $! + + # add -d to dup and close + soptions="$1 -d" + $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & + $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ + tee -a $seqres.full + wait $! +} + +# Always setlk at range [0,9], getlk at range [0,9] [5,24] or [20,29]. +# To open file RDONLY or RDWR should not break the locks. +# POSIX locks should be released after closed fd, so it wont conflict +# with other locks in tests + +# -P : operate posix lock +# -w : operate on F_WRLCK +# -r : operate on F_RDLCK +# -R : open file RDONLY +# -W : open file RDWR +# -o : file offset where the lock starts +# -l : lock length +# -F : clone with CLONE_FILES in setlk +# -d : dup and close in setlk + +# setlk wrlck [0,9], getlk wrlck [0,9], expect +# + wrlck when CLONE_FILES not set +# + unlck when CLONE_FILES set +# + wrlck when dup & close +do_test "-s -w -o 0 -l 10 -W" "-g -w -o 0 -l 10 -W" "wrlck" "unlck" "wrlck" +# setlk wrlck [0,9], getlk posix wrlck [5,24] +do_test "-s -w -o 0 -l 10 -W" "-g -w -o 5 -l 20 -W -P" "wrlck" "unlck" "wrlck" +# setlk wrlck [0,9], getlk wrlck [20,29] +do_test "-s -w -o 0 -l 10 -W" "-g -w -o 20 -l 10 -W" "unlck" "unlck" "unlck" +# setlk posix wrlck [0,9], getlk wrlck [5,24] +do_test "-s -w -o 0 -l 10 -W -P" "-g -w -o 5 -l 20 -W" "wrlck" "unlck" "unlck" +# setlk posix wrlck [0,9], getlk wrlck [20,29] +do_test "-s -w -o 0 -l 10 -W -P" "-g -w -o 20 -l 10 -W" "unlck" "unlck" "unlck" + +# setlk wrlck [0,9], getlk rdlck [0,9] +do_test "-s -w -o 0 -l 10 -W" "-g -r -o 0 -l 10 -W" "wrlck" "unlck" "wrlck" +# setlk wrlck [0,9], getlk posix rdlck [5,24] +do_test "-s -w -o 0 -l 10" "-g -r -o 5 -l 20 -P" "wrlck" "unlck" "wrlck" +# setlk wrlck [0,9], getlk rdlck [20,29] +do_test "-s -w -o 0 -l 10" "-g -r -o 20 -l 10" "unlck" "unlck" "unlck" +# setlk posix wrlck [0,9], getlk rdlck [5,24] +do_test "-s -w -o 0 -l 10 -P" "-g -r -o 5 -l 20" "wrlck" "unlck" "unlck" +# setlk posix wrlck [0,9], getlk rdlck [20,29] +do_test "-s -w -o 0 -l 10 -P" "-g -r -o 20 -l 10" "unlck" "unlck" "unlck" + +# setlk rdlck [0,9], getlk wrlck [0,9], open RDONLY +do_test "-s -r -o 0 -l 10 -R" "-g -w -o 0 -l 10 -R" "rdlck" "unlck" "rdlck" +# setlk rdlck [0,9], getlk wrlck [5,24], open RDONLY +do_test "-s -r -o 0 -l 10 -R" "-g -w -o 5 -l 20 -R -P" "rdlck" "unlck" "rdlck" +# setlk posix rdlck [0,9], getlk wrlck [5,24], open RDONLY +do_test "-s -r -o 0 -l 10 -R -P" "-g -w -o 5 -l 20 -R" "rdlck" "unlck" "unlck" + +# setlk rdlck [0,9], getlk wrlck [0,9] +do_test "-s -r -o 0 -l 10" "-g -w -o 0 -l 10" "rdlck" "unlck" "rdlck" +# setlk rdlck [0,9], getlk posix wrlck [5,24] +do_test "-s -r -o 0 -l 10" "-g -w -o 5 -l 20 -P" "rdlck" "unlck" "rdlck" +# setlk posix rdlck [0,9], getlk wrlck [5,24] +do_test "-s -r -o 0 -l 10 -P" "-g -w -o 5 -l 20" "rdlck" "unlck" "unlck" + +# setlk rdlck [0,9], getlk wrlck [20,29], open RDONLY +do_test "-s -r -o 0 -l 10 -R" "-g -w -o 20 -l 10 -R" "unlck" "unlck" "unlck" +# setlk posix rdlck [0,9], getlk wrlck [20,29], open RDONLY +do_test "-s -r -o 0 -l 10 -R -P" "-g -w -o 20 -l 10 -R" "unlck" "unlck" "unlck" +# setlk rdlck [0,9], getlk wrlck [20,29] +do_test "-s -r -o 0 -l 10" "-g -w -o 20 -l 10" "unlck" "unlck" "unlck" +# setlk posix rdlck [0,9], getlk wrlck [20,29] +do_test "-s -r -o 0 -l 10 -P" "-g -w -o 20 -l 10" "unlck" "unlck" "unlck" + +# setlk rdlck [0,9], getlk rdlck [0,9], open RDONLY +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 0 -l 10 -R" "unlck" "unlck" "unlck" +# setlk rdlck [0,9], getlk posix rdlck [0,9], open RDONLY +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 0 -l 10 -R -P" "unlck" "unlck" "unlck" +# setlk posix rdlck [0,9], getlk rdlck [0,9], open RDONLY +do_test "-s -r -o 0 -l 10 -R -P" "-g -r -o 0 -l 10 -R" "unlck" "unlck" "unlck" +# setlk rdlck [0,9], getlk rdlck [0,9] +do_test "-s -r -o 0 -l 10" "-g -r -o 0 -l 10" "unlck" "unlck" "unlck" +# setlk posix rdlck [0,9], getlk rdlck [0,9] +do_test "-s -r -o 0 -l 10 -P" "-g -r -o 0 -l 10" "unlck" "unlck" "unlck" + +# setlk rdlck [0,9], getlk rdlck [20,29], open RDONLY +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 20 -l 10 -R" "unlck" "unlck" "unlck" +# setlk rdlck [0,9], getlk posix rdlck [20,29], open RDONLY +do_test "-s -r -o 0 -l 10 -R" "-g -r -o 20 -l 10 -R -P" "unlck" "unlck" "unlck" +# setlk posix rdlck [0,9], getlk rdlck [20,29], open RDONLY +do_test "-s -r -o 0 -l 10 -R -P" "-g -r -o 20 -l 10 -R" "unlck" "unlck" "unlck" +# setlk rdlck [0,9], getlk rdlck [20,29] +do_test "-s -r -o 0 -l 10" "-g -r -o 20 -l 10" "unlck" "unlck" "unlck" +# setlk posix rdlck [0,9], getlk rdlck [20,29] +do_test "-s -r -o 0 -l 10 -P" "-g -r -o 20 -l 10" "unlck" "unlck" "unlck" + +# success, all done +status=0 +exit diff --git a/tests/generic/479.out b/tests/generic/479.out new file mode 100644 index 0000000..6381294 --- /dev/null +++ b/tests/generic/479.out @@ -0,0 +1,91 @@ +QA output created by 479 +get wrlck +lock could be placed +get wrlck +get wrlck +lock could be placed +get wrlck +lock could be placed +lock could be placed +lock could be placed +get wrlck +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +get wrlck +lock could be placed +get wrlck +get wrlck +lock could be placed +get wrlck +lock could be placed +lock could be placed +lock could be placed +get wrlck +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +get rdlck +lock could be placed +get rdlck +get rdlck +lock could be placed +get rdlck +get rdlck +lock could be placed +lock could be placed +get rdlck +lock could be placed +get rdlck +get rdlck +lock could be placed +get rdlck +get rdlck +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed +lock could be placed diff --git a/tests/generic/group b/tests/generic/group index 8416957..a578696 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -481,3 +481,4 @@ 476 auto rw 477 auto quick exportfs 478 auto stress dangerous fsnotify +479 auto quick -- 1.8.3.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2018-02-13 14:10 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-10-11 8:42 [PATCH] generic: add OFD lock tests Xiong Zhou 2017-10-13 9:13 ` [PATCH v2] " Xiong Zhou 2017-10-16 10:28 ` Eryu Guan 2017-10-19 8:30 ` [PATCH v3] " Xiong Zhou 2017-10-24 8:16 ` Eryu Guan 2017-10-27 4:59 ` [PATCH v4] " Xiong Zhou 2017-10-28 9:29 ` Eryu Guan 2017-10-29 2:34 ` Xiong Zhou 2017-11-10 2:46 ` [PATCH v5] " Xiong Zhou 2017-11-15 9:13 ` Eryu Guan 2018-02-13 14:10 ` [PATCH v6] " Xiong Zhou
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox