From: Jeff Layton <jlayton@kernel.org>
To: Stas Sergeev <stsp2@yandex.ru>, fstests@vger.kernel.org
Cc: Murphy Zhou <xzhou@redhat.com>, Zorro Lang <zlang@redhat.com>
Subject: Re: [PATCH] generic/732: add F_OFD_GETLK/F_UNLCK testing
Date: Wed, 04 Oct 2023 13:43:39 -0400 [thread overview]
Message-ID: <1185704fb150f69fd6a1fea006cc80e50927584b.camel@kernel.org> (raw)
In-Reply-To: <20231004173042.222644-1-stsp2@yandex.ru>
On Wed, 2023-10-04 at 22:30 +0500, Stas Sergeev wrote:
> F_UNLCK can be used with F_OFD_GETLK to get the locks from
> particular fd. This patch adds a new test, generic/732,
> to check that functionality.
>
> The following options are added to t_ofd_locks:
> -u selects F_UNLCK for testing
> -G sets the unix socket name for fd receive
> -S sets the unix socket name for fd send
>
> Test works as follows:
> - locker process sets the lock and sends an fd via unix socket
> - lock-tester process receives an fd and uses F_UNLCK to check the lock
>
> CC: fstests@vger.kernel.org
> CC: Murphy Zhou <xzhou@redhat.com>
> CC: Jeff Layton <jlayton@kernel.org>
> CC: Zorro Lang <zlang@redhat.com>
>
> Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
> ---
> common/ofdlk | 61 +++++++++++++++
> common/rc | 4 +-
> src/t_ofd_locks.c | 169 +++++++++++++++++++++++++++++++++++++++---
> tests/generic/478 | 59 +--------------
> tests/generic/732 | 60 +++++++++++++++
> tests/generic/732.out | 16 ++++
> 6 files changed, 298 insertions(+), 71 deletions(-)
> create mode 100644 common/ofdlk
> create mode 100755 tests/generic/732
> create mode 100644 tests/generic/732.out
>
> diff --git a/common/ofdlk b/common/ofdlk
> new file mode 100644
> index 00000000..0cdd2250
> --- /dev/null
> +++ b/common/ofdlk
> @@ -0,0 +1,61 @@
> +#
> +# Common functions for OFD lock tests
> +#
> +
> +mk_sem()
These are too generically named for functions that sit in common. Can
you rename these to something more distinct?
> +{
> + SEMID=$(ipcmk -S 2 | cut -d ":" -f 2 | tr -d '[:space:]')
> + if [ -z "$SEMID" ]; then
> + echo "ipcmk failed"
> + exit 1
> + fi
> + SEMKEY=$(ipcs -s | grep $SEMID | cut -d " " -f 1)
> + if [ -z "$SEMKEY" ]; then
> + echo "ipcs failed"
> + exit 1
> + fi
> +}
> +
> +rm_sem()
> +{
> + ipcrm -s $SEMID 2>/dev/null
> +}
> +
> +do_test()
> +{
> + local soptions
> + local goptions
> + # print options and getlk output for debug
> + echo $* >> $seqres.full 2>&1
> + mk_sem
> + soptions="$1 -K $SEMKEY"
> + goptions="$2 -K $SEMKEY"
> + # -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 $!
> + rm_sem
> +
> + mk_sem
> + # add -F to clone with CLONE_FILES
> + soptions="$1 -F -K $SEMKEY"
> + goptions="$2 -K $SEMKEY"
> + # 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 $!
> + rm_sem
> +
> + mk_sem
> + # add -d to dup and close
> + soptions="$1 -d -K $SEMKEY"
> + goptions="$2 -K $SEMKEY"
> + $here/src/t_ofd_locks $soptions $TEST_DIR/testfile &
> + $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \
> + tee -a $seqres.full
> + wait $!
> + rm_sem
> +}
> diff --git a/common/rc b/common/rc
> index 1618ded5..1111fc12 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -4222,8 +4222,8 @@ _require_ofd_locks()
> # EINVAL will be returned.
> _require_test_program "t_ofd_locks"
> touch $TEST_DIR/ofd_testfile
> - $here/src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1
> - [ $? -eq 22 ] && _notrun "Require OFD locks support"
> + $here/src/t_ofd_locks -t $1 $TEST_DIR/ofd_testfile > /dev/null 2>&1
> + [ $? -eq 22 ] && _notrun "Require OFD locks support $2"
> }
>
>
Instead of adding extra parameters to this function, it would be better
to just add a new _require_ofd_getlk_unlck() function that does what's
needed for this test. There may be other testcases that require that
functionality and its more self-documenting for later users.
>
> _require_test_lsattr()
> diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c
> index 58cb0959..8d63b80b 100644
> --- a/src/t_ofd_locks.c
> +++ b/src/t_ofd_locks.c
> @@ -14,6 +14,8 @@
> #include <sys/wait.h>
> #include <sys/ipc.h>
> #include <sys/sem.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
>
> /*
> * In distributions that do not have these macros ready in glibc-headers,
> @@ -134,10 +136,13 @@ static void usage(char *arg0)
> 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-r/-w/-u : set/get rdlck/wrlck/unlck\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("\t-G path : unix socket path for fd reception\n\n");
> + printf("\t-S path : unix socket path for fd sending\n\n");
> + printf("\t-t : test run, try to perform lock and report\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");
> @@ -164,6 +169,117 @@ static int child_fn(void* p)
> return 0;
> }
>
> +static int connect_unix(const char *sname)
> +{
> + struct sockaddr_un addr = { .sun_family = AF_UNIX };
> +
> + int unix_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
> + if (unix_sock == -1)
> + return -1;
> +
> + strncpy(addr.sun_path, sname, sizeof(addr.sun_path) - 1);
> + if (connect(unix_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
> + {
> + close(unix_sock);
> + perror("connect()");
> + return -1;
> + }
> +
> + return unix_sock;
> +}
> +
> +static int send_fd(const char *sname, int fd)
> +{
> + int ret;
> + int unix_sock;
> + struct iovec iov = {.iov_base = ":)", // Must send at least one byte
> + .iov_len = 2};
> +
> + union {
> + char buf[CMSG_SPACE(sizeof(fd))];
> + struct cmsghdr align;
> + } u;
> +
> + struct msghdr msg = {.msg_iov = &iov,
> + .msg_iovlen = 1,
> + .msg_control = u.buf,
> + .msg_controllen = sizeof(u.buf)};
> +
> + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
> + *cmsg = (struct cmsghdr){.cmsg_level = SOL_SOCKET,
> + .cmsg_type = SCM_RIGHTS,
> + .cmsg_len = CMSG_LEN(sizeof(fd))};
> +
> + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
> +
> + unix_sock = connect_unix(sname);
> + if (unix_sock == -1) {
> + perror("sendmsg()");
> + return -1;
> + }
> + ret = sendmsg(unix_sock, &msg, 0);
> + close(unix_sock);
> + if (ret == -1)
> + perror("sendmsg()");
> + return ret;
> +}
> +
> +static int bind_unix(const char *sname)
> +{
> + struct sockaddr_storage storage = {};
> + struct sockaddr_un *addr;
> + int sock;
> +
> + sock = socket(AF_UNIX, SOCK_DGRAM, 0);
> + if (sock == -1)
> + return -1;
> + addr = (struct sockaddr_un *)&storage;
> + addr->sun_family = AF_UNIX;
> + strncpy(addr->sun_path, sname, sizeof(addr->sun_path) - 1);
> + unlink(sname);
> + if (bind(sock, (struct sockaddr *)addr, SUN_LEN(addr)) == -1) {
> + perror("bind()");
> + close(sock);
> + return -1;
> + }
> + return sock;
> +}
> +
> +static int recv_fd(int sock)
> +{
> + struct msghdr msg;
> + struct cmsghdr *cmsghdr;
> + struct iovec iov;
> + ssize_t nbytes;
> + int *p;
> + char buf[CMSG_SPACE(sizeof(int))], c[16];
> + struct cmsghdr *cmsgp;
> +
> + iov.iov_base = &c;
> + iov.iov_len = sizeof(c);
> + cmsghdr = (struct cmsghdr *)buf;
> + cmsghdr->cmsg_len = CMSG_LEN(sizeof(int));
> + cmsghdr->cmsg_level = SOL_SOCKET;
> + cmsghdr->cmsg_type = SCM_RIGHTS;
> + msg.msg_name = NULL;
> + msg.msg_namelen = 0;
> + msg.msg_iov = &iov;
> + msg.msg_iovlen = 1;
> + msg.msg_control = cmsghdr;
> + msg.msg_controllen = CMSG_LEN(sizeof(int));
> + msg.msg_flags = 0;
> +
> + nbytes = recvmsg(sock, &msg, 0);
> + if (nbytes == -1)
> + return -1;
> +
> + cmsgp = CMSG_FIRSTHDR(&msg);
> + p = (int *)CMSG_DATA(cmsgp);
> + if (!p)
> + return -1;
> + return *p;
> +}
> +
> int main(int argc, char **argv)
> {
> int posix = 0;
> @@ -184,10 +300,12 @@ int main(int argc, char **argv)
> struct semid_ds sem_ds;
> struct sembuf sop;
> int opt, ret;
> + int rcv_sock = -1;
> + const char *snd_sock_name = NULL;
>
> //avoid libcap errno bug
> errno = 0;
> - while((opt = getopt(argc, argv, "sgrwo:l:PRWtFdK:")) != -1) {
> + while((opt = getopt(argc, argv, "sgrwuo:l:PRWtFdK:G:S:")) != -1) {
> switch(opt) {
> case 's':
> lock_cmd = 1;
> @@ -201,6 +319,9 @@ int main(int argc, char **argv)
> case 'w':
> lock_rw = 1;
> break;
> + case 'u':
> + lock_rw = 2;
> + break;
> case 'o':
> lock_start = atoi(optarg);
> break;
> @@ -228,6 +349,12 @@ int main(int argc, char **argv)
> case 'K':
> semkey = strtol(optarg, NULL, 16);
> break;
> + case 'G':
> + rcv_sock = bind_unix(optarg);
> + break;
> + case 'S':
> + snd_sock_name = optarg;
> + break;
> default:
> usage(argv[0]);
> return -1;
> @@ -256,17 +383,27 @@ int main(int argc, char **argv)
> getlk_macro = F_GETLK;
> }
>
> - if (lock_rw == 1)
> - flk.l_type = F_WRLCK;
> - else
> + switch (lock_rw) {
> + case 0:
> flk.l_type = F_RDLCK;
> + break;
> + case 1:
> + flk.l_type = F_WRLCK;
> + break;
> + case 2:
> + flk.l_type = F_UNLCK;
> + break;
> + }
>
> - if (open_rw == 0)
> - fd = open(argv[optind], O_RDONLY);
> - else
> - fd = open(argv[optind], O_RDWR);
> - if (fd == -1)
> - err_exit("open", errno);
> + if (rcv_sock == -1) {
> + 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
> @@ -322,6 +459,9 @@ int main(int argc, char **argv)
> if (fcntl(fd, setlk_macro, &flk) < 0)
> err_exit("setlkw", errno);
>
> + if (snd_sock_name)
> + send_fd(snd_sock_name, fd);
> +
> if (use_dup == 1) {
> /* dup fd and close the newfd */
> int dfd = dup(fd);
> @@ -400,6 +540,13 @@ int main(int argc, char **argv)
> if (semtimedop(semid, &sop, 1, &ts) == -1)
> err_exit("wait sem0 0", errno);
>
> + if (rcv_sock != -1) {
> + fd = recv_fd(rcv_sock);
> + close(rcv_sock);
> + if (fd == -1)
> + err_exit("SCM_RIGHTS", errno);
> + }
> +
> if (fcntl(fd, getlk_macro, &flk) < 0)
> err_exit("getlk", errno);
>
> diff --git a/tests/generic/478 b/tests/generic/478
> index 419acc94..580c260e 100755
> --- a/tests/generic/478
> +++ b/tests/generic/478
> @@ -88,6 +88,7 @@ _begin_fstest auto quick
>
> # Import common functions.
> . ./common/filter
> +. ./common/ofdlk
>
> # Modify as appropriate.
> _supported_fs generic
> @@ -99,64 +100,6 @@ _require_ofd_locks
> $XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \
> $TEST_DIR/testfile >> $seqres.full 2>&1
>
> -mk_sem()
> -{
> - SEMID=$(ipcmk -S 2 | cut -d ":" -f 2 | tr -d '[:space:]')
> - if [ -z "$SEMID" ]; then
> - echo "ipcmk failed"
> - exit 1
> - fi
> - SEMKEY=$(ipcs -s | grep $SEMID | cut -d " " -f 1)
> - if [ -z "$SEMKEY" ]; then
> - echo "ipcs failed"
> - exit 1
> - fi
> -}
> -
> -rm_sem()
> -{
> - ipcrm -s $SEMID 2>/dev/null
> -}
> -
> -do_test()
> -{
> - local soptions
> - local goptions
> - # print options and getlk output for debug
> - echo $* >> $seqres.full 2>&1
> - mk_sem
> - soptions="$1 -K $SEMKEY"
> - goptions="$2 -K $SEMKEY"
> - # -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 $!
> - rm_sem
> -
> - mk_sem
> - # add -F to clone with CLONE_FILES
> - soptions="$1 -F -K $SEMKEY"
> - goptions="$2 -K $SEMKEY"
> - # 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 $!
> - rm_sem
> -
> - mk_sem
> - # add -d to dup and close
> - soptions="$1 -d -K $SEMKEY"
> - goptions="$2 -K $SEMKEY"
> - $here/src/t_ofd_locks $soptions $TEST_DIR/testfile &
> - $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \
> - tee -a $seqres.full
> - wait $!
> - rm_sem
> -}
> -
> # 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
> diff --git a/tests/generic/732 b/tests/generic/732
> new file mode 100755
> index 00000000..78f5bd44
> --- /dev/null
> +++ b/tests/generic/732
> @@ -0,0 +1,60 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2023 stsp2@yandex.ru
> +#
> +# FS QA Test 732
> +#
> +# Test OFD lock's F_UNLCK extension.
> +#
> +. ./common/preamble
> +_begin_fstest auto quick
> +
> +# Import common functions.
> +. ./common/filter
> +. ./common/ofdlk
> +
> +# Modify as appropriate.
> +_supported_fs generic
> +_require_test
> +_require_ofd_locks -gu "F_OFD_GETLK/F_UNLCK extension"
> +
> +# 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
> +
> +# 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
> +
> +# setlk wrlck [0,9], getlk unlck [0,9]
> +do_test "-s -w -o 0 -l 10 -W -S /tmp/fstests.sock" "-g -u -o 0 -l 10 -W -G /tmp/fstests.sock" "wrlck" "wrlck" "wrlck"
> +# setlk wrlck [0,9], getlk unlck [20,29]
> +do_test "-s -w -o 0 -l 10 -S /tmp/fstests.sock" "-g -u -o 20 -l 10 -G /tmp/fstests.sock" "unlck" "unlck" "unlck"
> +
> +# setlk rdlck [0,9], getlk unlck [0,9], open RDONLY
> +do_test "-s -r -o 0 -l 10 -R -S /tmp/fstests.sock" "-g -u -o 0 -l 10 -R -G /tmp/fstests.sock" "rdlck" "rdlck" "rdlck"
> +# setlk posix rdlck [0,9], getlk unlck [5,24], open RDONLY
> +do_test "-s -r -o 0 -l 10 -R -S /tmp/fstests.sock" "-g -u -o 5 -l 20 -R -G /tmp/fstests.sock" "rdlck" "rdlck" "rdlck"
> +# setlk rdlck [0,9], getlk unlck [20,29], open RDONLY
> +do_test "-s -r -o 0 -l 10 -R -S /tmp/fstests.sock" "-g -u -o 20 -l 10 -R -G /tmp/fstests.sock" "unlck" "unlck" "unlck"
> +
> +# success, all done
> +status=0
> +exit
> diff --git a/tests/generic/732.out b/tests/generic/732.out
> new file mode 100644
> index 00000000..98996904
> --- /dev/null
> +++ b/tests/generic/732.out
> @@ -0,0 +1,16 @@
> +QA output created by 732
> +get wrlck
> +get wrlck
> +get wrlck
> +lock could be placed
> +lock could be placed
> +lock could be placed
> +get rdlck
> +get rdlck
> +get rdlck
> +get rdlck
> +get rdlck
> +get rdlck
> +lock could be placed
> +lock could be placed
> +lock could be placed
I think the rest looks good though. Thanks for doing this.
Cheers,
--
Jeff Layton <jlayton@kernel.org>
prev parent reply other threads:[~2023-10-04 17:43 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-04 17:30 [PATCH] generic/732: add F_OFD_GETLK/F_UNLCK testing Stas Sergeev
2023-10-04 17:43 ` Jeff Layton [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1185704fb150f69fd6a1fea006cc80e50927584b.camel@kernel.org \
--to=jlayton@kernel.org \
--cc=fstests@vger.kernel.org \
--cc=stsp2@yandex.ru \
--cc=xzhou@redhat.com \
--cc=zlang@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox