All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [PATCH] syscalls/fcntl36: add tests for OFD locks
@ 2017-08-11  3:14 Xiong Zhou
  2017-08-14 16:02 ` Cyril Hrubis
  0 siblings, 1 reply; 17+ messages in thread
From: Xiong Zhou @ 2017-08-11  3:14 UTC (permalink / raw)
  To: ltp

Signed-off-by: Xiong Zhou <xzhou@redhat.com>
---
 testcases/kernel/syscalls/fcntl/Makefile  |   3 +
 testcases/kernel/syscalls/fcntl/fcntl36.c | 315 ++++++++++++++++++++++++++++++
 2 files changed, 318 insertions(+)
 create mode 100644 testcases/kernel/syscalls/fcntl/fcntl36.c

diff --git a/testcases/kernel/syscalls/fcntl/Makefile b/testcases/kernel/syscalls/fcntl/Makefile
index d78dd72..ae37214 100644
--- a/testcases/kernel/syscalls/fcntl/Makefile
+++ b/testcases/kernel/syscalls/fcntl/Makefile
@@ -24,6 +24,9 @@ fcntl33_64: LDLIBS+=-lrt
 fcntl34: LDLIBS += -lpthread
 fcntl34_64: LDLIBS += -lpthread
 
+fcntl36: LDLIBS += -lpthread
+fcntl36_64: LDLIBS += -lpthread
+
 include $(top_srcdir)/include/mk/testcases.mk
 include $(abs_srcdir)/../utils/newer_64.mk
 
diff --git a/testcases/kernel/syscalls/fcntl/fcntl36.c b/testcases/kernel/syscalls/fcntl/fcntl36.c
new file mode 100644
index 0000000..4c55279
--- /dev/null
+++ b/testcases/kernel/syscalls/fcntl/fcntl36.c
@@ -0,0 +1,315 @@
+/*
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Xiong Zhou <xzhou@redhat.com>
+ *
+ * This is testing OFD locks racing with POSIX locks:
+ *
+ *	OFD read  lock   vs   OFD   write lock
+ *	OFD read  lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX read  lock
+ *	OFD write lock   vs   OFD   write lock
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "lapi/fcntl.h"
+#include "tst_safe_pthread.h"
+#include "tst_test.h"
+
+static int thread_cnt;
+static const int max_thread_cnt = 32;
+static const char fname[] = "tst_ofd_locks";
+static int write_flag_cnt;
+const int write_size = 4096;
+
+static void setup(void)
+{
+	thread_cnt = tst_ncpus_conf() * 3;
+	if (thread_cnt > max_thread_cnt)
+		thread_cnt = max_thread_cnt;
+}
+
+static void spawn_threads(pthread_t *id, void *(*thread_fn)(void *))
+{
+	intptr_t i;
+
+	for (i = 0; i < thread_cnt; ++i)
+		SAFE_PTHREAD_CREATE(id + i, NULL, thread_fn, (void *)i);
+}
+
+static void wait_threads(pthread_t *id)
+{
+	int i;
+
+	for (i = 0; i < thread_cnt; ++i)
+		SAFE_PTHREAD_JOIN(id[i], NULL);
+}
+
+/* Loop OFD wlock writing data*/
+static void *thread_fn_ofd_wlock(void *arg)
+{
+	unsigned char buf[write_size];
+	long start = (intptr_t)arg * write_size;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	int i = 0;
+
+	/* write with half writesize offset in the second spawning */
+	start += (write_flag_cnt % 2) * write_size / 2;
+	memset(buf, 'o', write_size);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = start,
+		.l_len    = write_size,
+		.l_pid    = 0,
+	};
+
+	struct timespec ts = {
+               .tv_sec = 0,
+               .tv_nsec = 50000000,
+	};
+
+	for (; i < thread_cnt; i++) {
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, start, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, write_size);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		nanosleep(&ts, NULL);
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+
+	return NULL;
+}
+
+/* Loop POSIX wlock writing data*/
+static void *thread_fn_posix_wlock(void *arg)
+{
+	unsigned char buf[write_size];
+	long start = (intptr_t)arg * write_size;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	int i = 0;
+
+	memset(buf, 'p', write_size);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = start,
+		.l_len    = write_size,
+	};
+
+	for (; i < thread_cnt; i++) {
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, start, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, write_size);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+
+	return NULL;
+}
+
+/* Loop OFD rlock reading data*/
+static void *thread_fn_ofd_rlock(void *arg)
+{
+	unsigned char buf[write_size];
+	long start = (intptr_t)arg * write_size;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	int i = 0;
+
+	memset(buf, (intptr_t)arg, write_size);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = start,
+		.l_len    = write_size,
+		.l_pid    = 0,
+	};
+
+	for (; i < thread_cnt; i++) {
+
+		lck.l_type = F_RDLCK;
+		if (fcntl(fd, F_OFD_SETLK, &lck) == -1) {
+			/* can only fail with EAGAIN */
+			if (errno != EAGAIN)
+				tst_brk(TBROK, "errno not equal to EAGAIN");
+		} else {
+
+			/* rlock acquired */
+			SAFE_LSEEK(fd, start, SEEK_SET);
+			SAFE_READ(0, fd, buf, write_size);
+
+			lck.l_type = F_UNLCK;
+			SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
+		}
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+
+	return NULL;
+}
+
+/* Loop POSIX rlock reading data */
+static void *thread_fn_posix_rlock(void *arg)
+{
+	unsigned char buf[write_size];
+	long start = (intptr_t)arg * write_size;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	int i = 0;
+
+	memset(buf, (intptr_t)arg, write_size);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = start,
+		.l_len    = write_size,
+	};
+
+	for (; i < thread_cnt; i++) {
+
+		lck.l_type = F_RDLCK;
+		if (fcntl(fd, F_SETLK, &lck) == -1) {
+			/* can only fail with EAGAIN */
+			if (errno != EAGAIN)
+				tst_brk(TBROK, "errno not equal to EAGAIN");
+		} else {
+
+			/* rlock acquired */
+			SAFE_LSEEK(fd, start, SEEK_SET);
+			SAFE_READ(0, fd, buf, write_size);
+
+			lck.l_type = F_UNLCK;
+			SAFE_FCNTL(fd, F_SETLK, &lck);
+		}
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+
+	return NULL;
+}
+
+static int test_fn(void *f0(void *), void *f1(void *))
+{
+	intptr_t i;
+	int k;
+	pthread_t id0[thread_cnt];
+	pthread_t id1[thread_cnt];
+	unsigned char buf[write_size];
+
+	int fd = SAFE_OPEN(fname, O_CREAT | O_TRUNC | O_RDWR, 0600);
+
+	spawn_threads(id0, f0);
+	write_flag_cnt++;
+	spawn_threads(id1, f1);
+	wait_threads(id0);
+	wait_threads(id1);
+
+	tst_res(TINFO, "Verifying file's data");
+	SAFE_LSEEK(fd, 0, SEEK_SET);
+
+	for (i = 0; i <= thread_cnt; ++i) {
+
+		SAFE_READ(1, fd, buf, write_size/2);
+
+		if (buf[0] != 'o' && buf[0] != 'p') {
+			tst_res(TFAIL, "unexpected data read");
+			return -1;
+		}
+
+		for (k = 1; k < write_size/2; ++k) {
+			if (buf[k] != buf[0]) {
+				tst_res(TFAIL, "unexpected block read");
+				return -1;
+			}
+		}
+	}
+
+	SAFE_CLOSE(fd);
+
+	return 0;
+}
+
+static void tests(void)
+{
+	int ret = 0;
+
+	tst_res(TINFO, "OFD read locks vs OFD write locks");
+	ret = test_fn(thread_fn_ofd_wlock, thread_fn_ofd_rlock);
+	if (ret == 0)
+		tst_res(TPASS, "access between threads synchronized");
+
+	ret = 0;
+	write_flag_cnt = 0;
+	tst_res(TINFO, "OFD write locks vs POSIX write locks");
+	ret = test_fn(thread_fn_ofd_wlock, thread_fn_posix_wlock);
+	if (ret == 0)
+		tst_res(TPASS, "access between threads synchronized");
+
+	ret = 0;
+	write_flag_cnt = 0;
+	tst_res(TINFO, "OFD read locks vs POSIX write locks");
+	ret = test_fn(thread_fn_ofd_rlock, thread_fn_posix_wlock);
+	if (ret == 0)
+		tst_res(TPASS, "access between threads synchronized");
+
+	ret = 0;
+	write_flag_cnt = 0;
+	tst_res(TINFO, "OFD write locks vs POSIX read locks");
+	ret = test_fn(thread_fn_ofd_wlock, thread_fn_posix_rlock);
+	if (ret == 0)
+		tst_res(TPASS, "access between threads synchronized");
+
+	ret = 0;
+	write_flag_cnt = 0;
+	tst_res(TINFO, "OFD write locks vs OFD write locks");
+	ret = test_fn(thread_fn_ofd_wlock, thread_fn_ofd_wlock);
+	if (ret == 0)
+		tst_res(TPASS, "access between threads synchronized");
+}
+
+static struct tst_test test = {
+	.min_kver = "3.15.0",
+	.needs_tmpdir = 1,
+	.test_all = tests,
+	.setup = setup
+};
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [LTP] [PATCH] syscalls/fcntl36: add tests for OFD locks
  2017-08-11  3:14 [LTP] [PATCH] syscalls/fcntl36: add tests for OFD locks Xiong Zhou
@ 2017-08-14 16:02 ` Cyril Hrubis
  2017-08-21 11:17   ` [LTP] [PATCH v2] " Xiong Zhou
  0 siblings, 1 reply; 17+ messages in thread
From: Cyril Hrubis @ 2017-08-14 16:02 UTC (permalink / raw)
  To: ltp

Hi!
>  testcases/kernel/syscalls/fcntl/Makefile  |   3 +
>  testcases/kernel/syscalls/fcntl/fcntl36.c | 315 ++++++++++++++++++++++++++++++

You have to add a record to the corresponding runtest file as well.

> diff --git a/testcases/kernel/syscalls/fcntl/Makefile b/testcases/kernel/syscalls/fcntl/Makefile
> index d78dd72..ae37214 100644
> --- a/testcases/kernel/syscalls/fcntl/Makefile
> +++ b/testcases/kernel/syscalls/fcntl/Makefile
> @@ -24,6 +24,9 @@ fcntl33_64: LDLIBS+=-lrt
>  fcntl34: LDLIBS += -lpthread
>  fcntl34_64: LDLIBS += -lpthread
>  
> +fcntl36: LDLIBS += -lpthread
> +fcntl36_64: LDLIBS += -lpthread
> +
>  include $(top_srcdir)/include/mk/testcases.mk
>  include $(abs_srcdir)/../utils/newer_64.mk
>  
> diff --git a/testcases/kernel/syscalls/fcntl/fcntl36.c b/testcases/kernel/syscalls/fcntl/fcntl36.c
> new file mode 100644
> index 0000000..4c55279
> --- /dev/null
> +++ b/testcases/kernel/syscalls/fcntl/fcntl36.c
> @@ -0,0 +1,315 @@
> +/*
> + * 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; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * 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, see <http://www.gnu.org/licenses/>.
> + *
> + * Author: Xiong Zhou <xzhou@redhat.com>
> + *
> + * This is testing OFD locks racing with POSIX locks:
> + *
> + *	OFD read  lock   vs   OFD   write lock
> + *	OFD read  lock   vs   POSIX write lock
> + *	OFD write lock   vs   POSIX write lock
> + *	OFD write lock   vs   POSIX read  lock
> + *	OFD write lock   vs   OFD   write lock

What exactly are we trying to test here? This description should be more
verbose.

> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <stdio.h>
> +#include <fcntl.h>
> +#include <pthread.h>
> +#include <sched.h>
> +#include <errno.h>
> +
> +#include "lapi/fcntl.h"
> +#include "tst_safe_pthread.h"
> +#include "tst_test.h"
> +
> +static int thread_cnt;
> +static const int max_thread_cnt = 32;
> +static const char fname[] = "tst_ofd_locks";
> +static int write_flag_cnt;
> +const int write_size = 4096;
> +
> +static void setup(void)
> +{
> +	thread_cnt = tst_ncpus_conf() * 3;
> +	if (thread_cnt > max_thread_cnt)
> +		thread_cnt = max_thread_cnt;
> +}
> +
> +static void spawn_threads(pthread_t *id, void *(*thread_fn)(void *))
> +{
> +	intptr_t i;
> +
> +	for (i = 0; i < thread_cnt; ++i)
> +		SAFE_PTHREAD_CREATE(id + i, NULL, thread_fn, (void *)i);
> +}
> +
> +static void wait_threads(pthread_t *id)
> +{
> +	int i;
> +
> +	for (i = 0; i < thread_cnt; ++i)
> +		SAFE_PTHREAD_JOIN(id[i], NULL);
> +}

Now these two functions are a bit unconsitent since the thread_cnt is
passed in global variable while the id is passed as a parameter. It
would be a bit cleaner if we passed both as a parameter.

> +/* Loop OFD wlock writing data*/
> +static void *thread_fn_ofd_wlock(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	long start = (intptr_t)arg * write_size;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +	int i = 0;
> +
> +	/* write with half writesize offset in the second spawning */
> +	start += (write_flag_cnt % 2) * write_size / 2;

Why do we do this?

I guess that it's for the case where we run OFD wlocks concurently and
we want them to overlap.

However there are a few problems with this approach, one is that
propagating pthread parameters via global variables is not safe. There
is no guarantee that all the threads has already finished setting the
start variable when we change it in the main thread. The clean solution
to this problem is to pack the paramters into a structure and pass it
to this function as a paramter, which would in turn mean that we have
to prepare an array of these structures in the main thread, we can do
that on the stack in the test function since we wait all the threads
before we leave it (otherwise we would end up using part of stack that
will be rewritten later).

If we move the loop that creates the threads to the test function we can
do something as:

struct param {
	long offset;
	long length;
};

...
void do_test(...)
{
	...

	struct param params[threads_cnt];

	for (i = 0; i < threads_cnt; i++) {
		params[i].offset = ...;
		params[i].lenght = ...;
		SAFE_PTHREAD_CREATE(id + i, NULL, fn, params);
	}

	...

	for (i = 0; i < threads_cnt; i++)
		SAFE_PTHREAD_JOIN(id[i]);

	...
}

> +	memset(buf, 'o', write_size);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = start,
> +		.l_len    = write_size,
> +		.l_pid    = 0,
> +	};
> +
> +	struct timespec ts = {
> +               .tv_sec = 0,
> +               .tv_nsec = 50000000,
> +	};
> +
> +	for (; i < thread_cnt; i++) {
> +
> +		lck.l_type = F_WRLCK;
> +		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
> +
> +		SAFE_LSEEK(fd, start, SEEK_SET);
> +		SAFE_WRITE(1, fd, buf, write_size);
> +
> +		lck.l_type = F_UNLCK;
> +		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
> +
> +		nanosleep(&ts, NULL);
> +		sched_yield();
> +	}

I do not get why we loop thread_cnt times here at all and why do we call
the nanosleep() between the locking attempts.

> +	SAFE_CLOSE(fd);
> +
> +	return NULL;
> +}
> +
> +/* Loop POSIX wlock writing data*/
> +static void *thread_fn_posix_wlock(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	long start = (intptr_t)arg * write_size;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +	int i = 0;
> +
> +	memset(buf, 'p', write_size);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = start,
> +		.l_len    = write_size,
> +	};
> +
> +	for (; i < thread_cnt; i++) {
> +
> +		lck.l_type = F_WRLCK;
> +		SAFE_FCNTL(fd, F_SETLKW, &lck);
> +
> +		SAFE_LSEEK(fd, start, SEEK_SET);
> +		SAFE_WRITE(1, fd, buf, write_size);
> +
> +		lck.l_type = F_UNLCK;
> +		SAFE_FCNTL(fd, F_SETLKW, &lck);
> +
> +		sched_yield();
> +	}
> +
> +	SAFE_CLOSE(fd);
> +
> +	return NULL;
> +}
> +
> +/* Loop OFD rlock reading data*/
> +static void *thread_fn_ofd_rlock(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	long start = (intptr_t)arg * write_size;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +	int i = 0;
> +
> +	memset(buf, (intptr_t)arg, write_size);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = start,
> +		.l_len    = write_size,
> +		.l_pid    = 0,
> +	};
> +
> +	for (; i < thread_cnt; i++) {
> +
> +		lck.l_type = F_RDLCK;
> +		if (fcntl(fd, F_OFD_SETLK, &lck) == -1) {
> +			/* can only fail with EAGAIN */
> +			if (errno != EAGAIN)
> +				tst_brk(TBROK, "errno not equal to EAGAIN");

In which case do we get EAGAIN?

Also the tst_brk should include TERRNO here.

> +		} else {
> +
> +			/* rlock acquired */
> +			SAFE_LSEEK(fd, start, SEEK_SET);
> +			SAFE_READ(0, fd, buf, write_size);


Shouldn't we check here that the whole block has the same value?

Since in the combination where we run writers vs readers the check that
the file has consistent content at the end of the run does not really
check anything.

Also each of the writers should write a random character into its block
and the value should change on each iteration. Then we can check that
the readers see only consistently filled blocks.

> +			lck.l_type = F_UNLCK;
> +			SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
> +		}
> +		sched_yield();
> +	}
> +
> +	SAFE_CLOSE(fd);
> +
> +	return NULL;
> +}
> +
> +/* Loop POSIX rlock reading data */
> +static void *thread_fn_posix_rlock(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	long start = (intptr_t)arg * write_size;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +	int i = 0;
> +
> +	memset(buf, (intptr_t)arg, write_size);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = start,
> +		.l_len    = write_size,
> +	};
> +
> +	for (; i < thread_cnt; i++) {
> +
> +		lck.l_type = F_RDLCK;
> +		if (fcntl(fd, F_SETLK, &lck) == -1) {
> +			/* can only fail with EAGAIN */
> +			if (errno != EAGAIN)
> +				tst_brk(TBROK, "errno not equal to EAGAIN");
> +		} else {
> +
> +			/* rlock acquired */
> +			SAFE_LSEEK(fd, start, SEEK_SET);
> +			SAFE_READ(0, fd, buf, write_size);

And here as well.

> +			lck.l_type = F_UNLCK;
> +			SAFE_FCNTL(fd, F_SETLK, &lck);
> +		}
> +		sched_yield();
> +	}
> +
> +	SAFE_CLOSE(fd);
> +
> +	return NULL;
> +}
> +
> +static int test_fn(void *f0(void *), void *f1(void *))
> +{
> +	intptr_t i;
> +	int k;
> +	pthread_t id0[thread_cnt];
> +	pthread_t id1[thread_cnt];
> +	unsigned char buf[write_size];
> +
> +	int fd = SAFE_OPEN(fname, O_CREAT | O_TRUNC | O_RDWR, 0600);
> +
> +	spawn_threads(id0, f0);
> +	write_flag_cnt++;
> +	spawn_threads(id1, f1);
> +
> +	wait_threads(id0);
> +	wait_threads(id1);
> +
> +	tst_res(TINFO, "Verifying file's data");
> +	SAFE_LSEEK(fd, 0, SEEK_SET);
> +
> +	for (i = 0; i <= thread_cnt; ++i) {
> +
> +		SAFE_READ(1, fd, buf, write_size/2);
> +
> +		if (buf[0] != 'o' && buf[0] != 'p') {
> +			tst_res(TFAIL, "unexpected data read");
> +			return -1;
> +		}
> +
> +		for (k = 1; k < write_size/2; ++k) {
> +			if (buf[k] != buf[0]) {
> +				tst_res(TFAIL, "unexpected block read");
> +				return -1;
> +			}
> +		}

This check is really needed only in a case that we run two writers
concurently, but I guess that doing it at the end of the test does not
harm.

> +	}
> +	SAFE_CLOSE(fd);
> +
> +	return 0;
> +}
> +
> +static void tests(void)
> +{
> +	int ret = 0;
> +
> +	tst_res(TINFO, "OFD read locks vs OFD write locks");
> +	ret = test_fn(thread_fn_ofd_wlock, thread_fn_ofd_rlock);
> +	if (ret == 0)
> +		tst_res(TPASS, "access between threads synchronized");
> +
> +	ret = 0;
> +	write_flag_cnt = 0;
> +	tst_res(TINFO, "OFD write locks vs POSIX write locks");
> +	ret = test_fn(thread_fn_ofd_wlock, thread_fn_posix_wlock);
> +	if (ret == 0)
> +		tst_res(TPASS, "access between threads synchronized");
> +
> +	ret = 0;
> +	write_flag_cnt = 0;
> +	tst_res(TINFO, "OFD read locks vs POSIX write locks");
> +	ret = test_fn(thread_fn_ofd_rlock, thread_fn_posix_wlock);
> +	if (ret == 0)
> +		tst_res(TPASS, "access between threads synchronized");
> +
> +	ret = 0;
> +	write_flag_cnt = 0;
> +	tst_res(TINFO, "OFD write locks vs POSIX read locks");
> +	ret = test_fn(thread_fn_ofd_wlock, thread_fn_posix_rlock);
> +	if (ret == 0)
> +		tst_res(TPASS, "access between threads synchronized");
> +
> +	ret = 0;
> +	write_flag_cnt = 0;
> +	tst_res(TINFO, "OFD write locks vs OFD write locks");
> +	ret = test_fn(thread_fn_ofd_wlock, thread_fn_ofd_wlock);
> +	if (ret == 0)
> +		tst_res(TPASS, "access between threads synchronized");

Can we split this into a separate tests? Just create a structure with
two funciton pointers and a string for a description and we can use
.test with an index into an array of these.

Also there is no reason to print the TPASS message here, why don't we do
that in the test_fn() function?

And there is absolutely no need to zero the ret variable as well and the
write_flag_cnt should be reset in the test_fn() where it's actually set.

> +}
> +
> +static struct tst_test test = {
> +	.min_kver = "3.15.0",
                       ^
		       No need for the .0 at the end "3.15" is a valid
		       version string.
> +	.needs_tmpdir = 1,
> +	.test_all = tests,
> +	.setup = setup
> +};
> -- 
> 1.8.3.1
> 
> 
> -- 
> Mailing list info: https://lists.linux.it/listinfo/ltp

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v2] syscalls/fcntl36: add tests for OFD locks
  2017-08-14 16:02 ` Cyril Hrubis
@ 2017-08-21 11:17   ` Xiong Zhou
  2017-08-21 16:00     ` Cyril Hrubis
  0 siblings, 1 reply; 17+ messages in thread
From: Xiong Zhou @ 2017-08-21 11:17 UTC (permalink / raw)
  To: ltp

Signed-off-by: Xiong Zhou <xzhou@redhat.com>
---

v2:
  Basically rewritten.
  Pass structure parameter to threads.
  Block on read lock.
  Spawn racing threads one by one.
  Use tcnt iteration.

 runtest/syscalls                          |   2 +
 testcases/kernel/syscalls/fcntl/Makefile  |   3 +
 testcases/kernel/syscalls/fcntl/fcntl36.c | 380 ++++++++++++++++++++++++++++++
 3 files changed, 385 insertions(+)
 create mode 100644 testcases/kernel/syscalls/fcntl/fcntl36.c

diff --git a/runtest/syscalls b/runtest/syscalls
index 407e055..f2b2cbc 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -271,6 +271,8 @@ fcntl34 fcntl34
 fcntl34_64 fcntl34_64
 fcntl35 fcntl35
 fcntl35_64 fcntl35_64
+fcntl36 fcntl36
+fcntl36_64 fcntl36_64
 
 fdatasync01 fdatasync01
 fdatasync02 fdatasync02
diff --git a/testcases/kernel/syscalls/fcntl/Makefile b/testcases/kernel/syscalls/fcntl/Makefile
index d78dd72..ae37214 100644
--- a/testcases/kernel/syscalls/fcntl/Makefile
+++ b/testcases/kernel/syscalls/fcntl/Makefile
@@ -24,6 +24,9 @@ fcntl33_64: LDLIBS+=-lrt
 fcntl34: LDLIBS += -lpthread
 fcntl34_64: LDLIBS += -lpthread
 
+fcntl36: LDLIBS += -lpthread
+fcntl36_64: LDLIBS += -lpthread
+
 include $(top_srcdir)/include/mk/testcases.mk
 include $(abs_srcdir)/../utils/newer_64.mk
 
diff --git a/testcases/kernel/syscalls/fcntl/fcntl36.c b/testcases/kernel/syscalls/fcntl/fcntl36.c
new file mode 100644
index 0000000..18b5d02
--- /dev/null
+++ b/testcases/kernel/syscalls/fcntl/fcntl36.c
@@ -0,0 +1,380 @@
+/*
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Xiong Zhou <xzhou@redhat.com>
+ *
+ * This is testing OFD locks racing with POSIX locks:
+ *
+ *	OFD read  lock   vs   OFD   write lock
+ *	OFD read  lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX read  lock
+ *	OFD write lock   vs   OFD   write lock
+ *
+ * For example:
+ *
+ * 	Init an file with preset values.
+ *
+ * 	Threads acquire OFD READ  locks to read  a 4k section start from 0;
+ * 		checking data read back, there should not be any surprise
+ * 		values and data should be consistent in a 2k block.
+ *
+ * 	Threads acquire OFD WRITE locks to write a 4k section start from 2k,
+ * 		writing different values in different threads.
+ *
+ * 	Check file data after racing, there should not be any surprise values
+ * 		and data should be consistent in a 2k block.
+ *
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "lapi/fcntl.h"
+#include "tst_safe_pthread.h"
+#include "tst_test.h"
+
+static int thread_cnt;
+static const int max_thread_cnt = 32;
+static const char fname[] = "tst_ofd_posix_locks";
+const long write_size = 4096;
+
+struct param {
+	long offset;
+	long length;
+	long cnt;
+};
+
+static void setup(void)
+{
+	int fd = SAFE_OPEN(fname, O_CREAT | O_TRUNC | O_RDWR, 0600);
+	char *s = NULL;
+
+	thread_cnt = tst_ncpus_conf() * 3;
+	if (thread_cnt > max_thread_cnt)
+		thread_cnt = max_thread_cnt;
+
+	s = SAFE_MALLOC(thread_cnt * write_size);
+	memset(s, 1, thread_cnt * write_size);
+	SAFE_WRITE(1, fd, s, thread_cnt * write_size);
+
+	SAFE_CLOSE(fd);
+	if (s) {
+		free(s);
+		s = NULL;
+	}
+}
+
+static void spawn_threads(int cnt, pthread_t *id0, pthread_t *id1,
+		void *(*thread_f0)(void *), void *(*thread_f1)(void *),
+		struct param *p0, struct param *p1)
+{
+	int i;
+
+	for (i = 0; i < cnt; i++) {
+		SAFE_PTHREAD_CREATE(id0 + i, NULL, thread_f0, (void *)&p0[i]);
+		SAFE_PTHREAD_CREATE(id1 + i, NULL, thread_f1, (void *)&p1[i]);
+	}
+}
+
+static void wait_threads(int cnt, pthread_t *id0, pthread_t *id1)
+{
+	int i;
+
+	for (i = 0; i < cnt; i++) {
+		SAFE_PTHREAD_JOIN(id0[i], NULL);
+		SAFE_PTHREAD_JOIN(id1[i], NULL);
+	}
+}
+
+/* OFD write lock writing data*/
+static void *fn_ofd_w(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	memset(buf, pa->cnt, write_size);
+
+	lck.l_type = F_WRLCK;
+	SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+	SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+	SAFE_WRITE(1, fd, buf, pa->length);
+
+	lck.l_type = F_UNLCK;
+	SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+	sched_yield();
+	SAFE_CLOSE(fd);
+
+	return NULL;
+}
+
+/* POSIX write lock writing data*/
+static void *fn_posix_w(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	memset(buf, pa->cnt, write_size);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	lck.l_type = F_WRLCK;
+	SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+	SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+	SAFE_WRITE(1, fd, buf, pa->length);
+
+	lck.l_type = F_UNLCK;
+	SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+	sched_yield();
+	SAFE_CLOSE(fd);
+
+	return NULL;
+}
+
+/* OFD read lock reading data*/
+static void *fn_ofd_r(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	memset(buf, 0, write_size);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	lck.l_type = F_RDLCK;
+	if (fcntl(fd, F_OFD_SETLKW, &lck) == -1) {
+
+		tst_brk(TBROK | TERRNO, "acquiring OFD read lock failed");
+	} else {
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > thread_cnt + 1) {
+				tst_res(TFAIL, "unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				break;
+			}
+
+			if ((i < pa->length / 2 && buf[i] != buf[0]) ||
+				(i >= pa->length / 2 &&
+					buf[i] != buf[pa->length / 2])) {
+				tst_res(TFAIL, "unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
+	}
+
+	sched_yield();
+	SAFE_CLOSE(fd);
+
+	return NULL;
+}
+
+/* POSIX read lock reading data */
+static void *fn_posix_r(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	memset(buf, 0, write_size);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	lck.l_type = F_RDLCK;
+	if (fcntl(fd, F_SETLKW, &lck) == -1) {
+
+		tst_brk(TBROK | TERRNO, "acquiring OFD read lock failed");
+	} else {
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > thread_cnt + 1) {
+				tst_res(TFAIL, "unexpected data, "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				break;
+			}
+
+			if ((i < pa->length / 2 && buf[i] != buf[0]) ||
+				(i >= pa->length / 2 &&
+					buf[i] != buf[pa->length / 2])) {
+				tst_res(TFAIL, "unexpected data, "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLK, &lck);
+	}
+
+	sched_yield();
+	SAFE_CLOSE(fd);
+
+	return NULL;
+}
+
+/* Test different functions and verify data */
+static int test_fn(void *f0(void *), void *f1(void *), char *msg)
+{
+	int i, k, fd;
+	pthread_t id0[thread_cnt];
+	pthread_t id1[thread_cnt];
+	struct param p0[thread_cnt];
+	struct param p1[thread_cnt];
+	unsigned char buf[write_size];
+
+	setup();
+	tst_res(TINFO, msg);
+
+	for (i = 0; i < thread_cnt; i++) {
+
+		p0[i].offset = i * write_size;
+		p0[i].length = write_size;
+		p0[i].cnt = i + 2;
+
+		p1[i].offset = i * write_size;
+		p1[i].offset += write_size / 2;
+		p1[i].length = write_size;
+		p1[i].cnt = i + 2;
+	}
+
+	spawn_threads(thread_cnt, id0, id1, f0, f1, p0, p1);
+	wait_threads(thread_cnt, id0, id1);
+
+	tst_res(TINFO, "Verifying file's data");
+	fd = SAFE_OPEN(fname, O_RDWR);
+	memset(buf, 0, write_size);
+	SAFE_LSEEK(fd, 0, SEEK_SET);
+
+	for (i = 0; i <= thread_cnt; i++) {
+
+		SAFE_READ(1, fd, buf, write_size/2);
+
+		for (k = 0; k < write_size/2; k++) {
+
+			if (buf[k] < 2 || buf[k] > thread_cnt + 1) {
+				if (i == 0 && buf[k] == 1)
+					continue;
+				tst_res(TFAIL, "unexpected data, "
+					"offset %ld value %d",
+					i * write_size + k, buf[k]);
+				return -1;
+			}
+		}
+
+		for (k = 1; k < write_size/2; k++) {
+			if (buf[k] != buf[0]) {
+				tst_res(TFAIL, "unexpected block read");
+				return -1;
+			}
+		}
+	}
+
+	SAFE_CLOSE(fd);
+	tst_res(TPASS, "access between threads synchronized");
+
+	return 0;
+}
+
+static void tests(unsigned int i)
+{
+	switch (i) {
+	case 0:
+		test_fn(fn_ofd_r, fn_ofd_w,
+			"OFD read locks vs OFD write locks");
+		break;
+	case 1:
+		test_fn(fn_ofd_w, fn_posix_w,
+			"OFD write locks vs POSIX write locks");
+		break;
+	case 2:
+		test_fn(fn_ofd_r, fn_posix_w,
+			"OFD read locks vs POSIX write locks");
+		break;
+	case 3:
+		test_fn(fn_posix_r, fn_ofd_w,
+			"OFD write locks vs POSIX read locks");
+		break;
+	case 4:
+		test_fn(fn_ofd_w, fn_ofd_w,
+			"OFD write locks vs OFD write locks");
+		break;
+	}
+}
+
+static struct tst_test test = {
+	.min_kver = "3.15",
+	.needs_tmpdir = 1,
+	.test = tests,
+	.tcnt = 5,
+	.setup = setup
+};
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v2] syscalls/fcntl36: add tests for OFD locks
  2017-08-21 11:17   ` [LTP] [PATCH v2] " Xiong Zhou
@ 2017-08-21 16:00     ` Cyril Hrubis
  2017-08-22  8:35       ` [LTP] [PATCH v3] " Xiong Zhou
  0 siblings, 1 reply; 17+ messages in thread
From: Cyril Hrubis @ 2017-08-21 16:00 UTC (permalink / raw)
  To: ltp

Hi!
> +/*
> + * 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; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * 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, see <http://www.gnu.org/licenses/>.
> + *
> + * Author: Xiong Zhou <xzhou@redhat.com>
> + *
> + * This is testing OFD locks racing with POSIX locks:
> + *
> + *	OFD read  lock   vs   OFD   write lock
> + *	OFD read  lock   vs   POSIX write lock
> + *	OFD write lock   vs   POSIX write lock
> + *	OFD write lock   vs   POSIX read  lock
> + *	OFD write lock   vs   OFD   write lock
> + *
> + * For example:
> + *
> + * 	Init an file with preset values.
> + *
> + * 	Threads acquire OFD READ  locks to read  a 4k section start from 0;
> + * 		checking data read back, there should not be any surprise
> + * 		values and data should be consistent in a 2k block.
> + *
> + * 	Threads acquire OFD WRITE locks to write a 4k section start from 2k,
> + * 		writing different values in different threads.
> + *
> + * 	Check file data after racing, there should not be any surprise values
> + * 		and data should be consistent in a 2k block.
> + *
> + *
> + */
> +
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <fcntl.h>
> +#include <pthread.h>
> +#include <sched.h>
> +#include <errno.h>
> +
> +#include "lapi/fcntl.h"
> +#include "tst_safe_pthread.h"
> +#include "tst_test.h"
> +
> +static int thread_cnt;
> +static const int max_thread_cnt = 32;
> +static const char fname[] = "tst_ofd_posix_locks";
> +const long write_size = 4096;
> +
> +struct param {
> +	long offset;
> +	long length;
> +	long cnt;
> +};
> +
> +static void setup(void)
> +{
> +	int fd = SAFE_OPEN(fname, O_CREAT | O_TRUNC | O_RDWR, 0600);
> +	char *s = NULL;

There is not need to initialize the variable since it's set just a few
lines below.

> +	thread_cnt = tst_ncpus_conf() * 3;
> +	if (thread_cnt > max_thread_cnt)
> +		thread_cnt = max_thread_cnt;
> +
> +	s = SAFE_MALLOC(thread_cnt * write_size);
> +	memset(s, 1, thread_cnt * write_size);
> +	SAFE_WRITE(1, fd, s, thread_cnt * write_size);
> +
> +	SAFE_CLOSE(fd);
> +	if (s) {
> +		free(s);
> +		s = NULL;
> +	}

And there is no need both for the if (), since free(NULL) is no-op and
for the s = NULL since the variable gets out of scope anyway.

Moreover we do have tst_fill_file() function exactly for this purpose.

> +}
> +
> +static void spawn_threads(int cnt, pthread_t *id0, pthread_t *id1,
> +		void *(*thread_f0)(void *), void *(*thread_f1)(void *),
> +		struct param *p0, struct param *p1)
> +{
> +	int i;
> +
> +	for (i = 0; i < cnt; i++) {
> +		SAFE_PTHREAD_CREATE(id0 + i, NULL, thread_f0, (void *)&p0[i]);
> +		SAFE_PTHREAD_CREATE(id1 + i, NULL, thread_f1, (void *)&p1[i]);
> +	}
> +}
> +
> +static void wait_threads(int cnt, pthread_t *id0, pthread_t *id1)
> +{
> +	int i;
> +
> +	for (i = 0; i < cnt; i++) {
> +		SAFE_PTHREAD_JOIN(id0[i], NULL);
> +		SAFE_PTHREAD_JOIN(id1[i], NULL);
> +	}
> +}
> +
> +/* OFD write lock writing data*/
> +static void *fn_ofd_w(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	struct param *pa = (struct param *)arg;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = pa->offset,
> +		.l_len    = pa->length,
> +		.l_pid    = 0,
> +	};
> +
> +	memset(buf, pa->cnt, write_size);
> +
> +	lck.l_type = F_WRLCK;
> +	SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
> +
> +	SAFE_LSEEK(fd, pa->offset, SEEK_SET);
> +	SAFE_WRITE(1, fd, buf, pa->length);
> +
> +	lck.l_type = F_UNLCK;
> +	SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
> +
> +	sched_yield();
> +	SAFE_CLOSE(fd);
> +
> +	return NULL;
> +}
> +
> +/* POSIX write lock writing data*/
> +static void *fn_posix_w(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	struct param *pa = (struct param *)arg;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +
> +	memset(buf, pa->cnt, write_size);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = pa->offset,
> +		.l_len    = pa->length,
> +	};
> +
> +	lck.l_type = F_WRLCK;
> +	SAFE_FCNTL(fd, F_SETLKW, &lck);
> +
> +	SAFE_LSEEK(fd, pa->offset, SEEK_SET);
> +	SAFE_WRITE(1, fd, buf, pa->length);
> +
> +	lck.l_type = F_UNLCK;
> +	SAFE_FCNTL(fd, F_SETLKW, &lck);
> +
> +	sched_yield();
> +	SAFE_CLOSE(fd);
> +
> +	return NULL;
> +}
> +
> +/* OFD read lock reading data*/
> +static void *fn_ofd_r(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	struct param *pa = (struct param *)arg;
> +	int i;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +
> +	memset(buf, 0, write_size);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = pa->offset,
> +		.l_len    = pa->length,
> +		.l_pid    = 0,
> +	};
> +
> +	lck.l_type = F_RDLCK;
> +	if (fcntl(fd, F_OFD_SETLKW, &lck) == -1) {
> +
> +		tst_brk(TBROK | TERRNO, "acquiring OFD read lock failed");
> +	} else {

We can use SAFE_FCNTL() here since the file is pre-allocated, right?

> +
> +		/* rlock acquired */
> +		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
> +		SAFE_READ(1, fd, buf, pa->length);
> +
> +		/* Verifying data read */
> +		for (i = 0; i < pa->length; i++) {
> +
> +			if (buf[i] < 1 || buf[i] > thread_cnt + 1) {
> +				tst_res(TFAIL, "unexpected data "
> +					"offset %ld value %d",
> +					pa->offset + i, buf[i]);
> +				break;
> +			}
> +
> +			if ((i < pa->length / 2 && buf[i] != buf[0]) ||
> +				(i >= pa->length / 2 &&
> +					buf[i] != buf[pa->length / 2])) {
> +				tst_res(TFAIL, "unexpected data "
> +					"offset %ld value %d",
> +					pa->offset + i, buf[i]);
> +				break;
> +			}
> +		}
> +
> +		lck.l_type = F_UNLCK;
> +		SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
> +	}
> +
> +	sched_yield();
> +	SAFE_CLOSE(fd);
> +
> +	return NULL;
> +}
> +
> +/* POSIX read lock reading data */
> +static void *fn_posix_r(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	struct param *pa = (struct param *)arg;
> +	int i;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +
> +	memset(buf, 0, write_size);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = pa->offset,
> +		.l_len    = pa->length,
> +	};
> +
> +	lck.l_type = F_RDLCK;
> +	if (fcntl(fd, F_SETLKW, &lck) == -1) {
> +
> +		tst_brk(TBROK | TERRNO, "acquiring OFD read lock failed");
> +	} else {

And here as well.

> +		/* rlock acquired */
> +		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
> +		SAFE_READ(1, fd, buf, pa->length);
> +
> +		/* Verifying data read */
> +		for (i = 0; i < pa->length; i++) {
> +
> +			if (buf[i] < 1 || buf[i] > thread_cnt + 1) {
> +				tst_res(TFAIL, "unexpected data, "
> +					"offset %ld value %d",
> +					pa->offset + i, buf[i]);
> +				break;
> +			}
> +
> +			if ((i < pa->length / 2 && buf[i] != buf[0]) ||
> +				(i >= pa->length / 2 &&
> +					buf[i] != buf[pa->length / 2])) {
> +				tst_res(TFAIL, "unexpected data, "
> +					"offset %ld value %d",
> +					pa->offset + i, buf[i]);
> +				break;
> +			}
> +		}
> +
> +		lck.l_type = F_UNLCK;
> +		SAFE_FCNTL(fd, F_SETLK, &lck);
> +	}
> +
> +	sched_yield();
> +	SAFE_CLOSE(fd);
> +
> +	return NULL;
> +}

These functions do just one iteration now. We should run them in a loop
for some time in order stress the locks a bit. The easiest way would be
to declare global volatile flag and loop while it's set in these
functions, sleep in the main thread for a bit between spawn_threads()
and join_threads() then reset the flag and join the threads. We can even
add a test parameter for how long the test should run.

And if we do that we should as well change the value writers write on
each iteration otherwise we wouldn't test anything after first iteration
in the writer vs reader case. I would pass the cnt as it's done here but
will increment it each iteration and set it to 2 once it reaches 256 in
order to fit into a byte.

> +/* Test different functions and verify data */
> +static int test_fn(void *f0(void *), void *f1(void *), char *msg)

Again this should be just void since we do not use the returned value at
all.

> +{
> +	int i, k, fd;
> +	pthread_t id0[thread_cnt];
> +	pthread_t id1[thread_cnt];
> +	struct param p0[thread_cnt];
> +	struct param p1[thread_cnt];
> +	unsigned char buf[write_size];
> +
> +	setup();

This is not right, if we want to rewrite the test file before each test
the code to write the file should be here and the setup should contain
only the check for thread_cnt.

> +	tst_res(TINFO, msg);
> +
> +	for (i = 0; i < thread_cnt; i++) {
> +
> +		p0[i].offset = i * write_size;
> +		p0[i].length = write_size;
> +		p0[i].cnt = i + 2;
> +
> +		p1[i].offset = i * write_size;
> +		p1[i].offset += write_size / 2;
> +		p1[i].length = write_size;
> +		p1[i].cnt = i + 2;
> +	}
> +
> +	spawn_threads(thread_cnt, id0, id1, f0, f1, p0, p1);
> +	wait_threads(thread_cnt, id0, id1);
> +
> +	tst_res(TINFO, "Verifying file's data");
> +	fd = SAFE_OPEN(fname, O_RDWR);
> +	memset(buf, 0, write_size);

Here again, we actually fill the buffer with data and we make sure that
it has been filled (since we pass 1 to the SAFE_READ() as first
paramter) hence there is absolutely no need to initialize the buffer
here.

> +	SAFE_LSEEK(fd, 0, SEEK_SET);
> +
> +	for (i = 0; i <= thread_cnt; i++) {
> +
> +		SAFE_READ(1, fd, buf, write_size/2);
> +
> +		for (k = 0; k < write_size/2; k++) {
> +
> +			if (buf[k] < 2 || buf[k] > thread_cnt + 1) {
> +				if (i == 0 && buf[k] == 1)
> +					continue;
> +				tst_res(TFAIL, "unexpected data, "
> +					"offset %ld value %d",
> +					i * write_size + k, buf[k]);
> +				return -1;
> +			}
> +		}
> +
> +		for (k = 1; k < write_size/2; k++) {
> +			if (buf[k] != buf[0]) {
> +				tst_res(TFAIL, "unexpected block read");
> +				return -1;
> +			}
> +		}
> +	}
> +
> +	SAFE_CLOSE(fd);
> +	tst_res(TPASS, "access between threads synchronized");
> +
> +	return 0;
> +}
> +
> +static void tests(unsigned int i)
> +{
> +	switch (i) {
> +	case 0:
> +		test_fn(fn_ofd_r, fn_ofd_w,
> +			"OFD read locks vs OFD write locks");
> +		break;
> +	case 1:
> +		test_fn(fn_ofd_w, fn_posix_w,
> +			"OFD write locks vs POSIX write locks");
> +		break;
> +	case 2:
> +		test_fn(fn_ofd_r, fn_posix_w,
> +			"OFD read locks vs POSIX write locks");
> +		break;
> +	case 3:
> +		test_fn(fn_posix_r, fn_ofd_w,
> +			"OFD write locks vs POSIX read locks");
> +		break;
> +	case 4:
> +		test_fn(fn_ofd_w, fn_ofd_w,
> +			"OFD write locks vs OFD write locks");
> +		break;
> +	}
> +}

We may as well add these into a structure and use index into an array
instead of the switch, something as:

static struct tcase {
	void* (*fn1)(void *);
	void* (*fn2)(void *);
	const char *desc;
} tcases[] = {
	{fn_ofd_r, fn_ofd_w, "OFD read locks vs OFD write locks"},
	...
};

static void tests(unsigned int i)
{
	test(tcase[i].fn0, tcase[i].fn1, tcase[i].desc);
}

> +static struct tst_test test = {
> +	.min_kver = "3.15",
> +	.needs_tmpdir = 1,
> +	.test = tests,
> +	.tcnt = 5,
> +	.setup = setup
> +};

Otherwise it looks good.

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v3] syscalls/fcntl36: add tests for OFD locks
  2017-08-21 16:00     ` Cyril Hrubis
@ 2017-08-22  8:35       ` Xiong Zhou
  2017-08-22 14:23         ` Cyril Hrubis
  0 siblings, 1 reply; 17+ messages in thread
From: Xiong Zhou @ 2017-08-22  8:35 UTC (permalink / raw)
  To: ltp

Testing OFD locks racing with POSIX locks:

  OFD read  lock   vs   OFD   write lock
  OFD read  lock   vs   POSIX write lock
  OFD write lock   vs   POSIX write lock
  OFD write lock   vs   POSIX read  lock
  OFD write lock   vs   OFD   write lock

Signed-off-by: Xiong Zhou <xzhou@redhat.com>
---

v3:
  Use tst_fill_file.
  Loop in test function.
  Other fix.

v2:
  Basically rewritten.
  Pass structure parameter to threads.
  Block on read lock.
  Spawn racing threads one by one.
  Use tcnt iteration.

 runtest/syscalls                          |   2 +
 testcases/kernel/syscalls/fcntl/Makefile  |   3 +
 testcases/kernel/syscalls/fcntl/fcntl36.c | 386 ++++++++++++++++++++++++++++++
 3 files changed, 391 insertions(+)
 create mode 100644 testcases/kernel/syscalls/fcntl/fcntl36.c

diff --git a/runtest/syscalls b/runtest/syscalls
index 407e055..f2b2cbc 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -271,6 +271,8 @@ fcntl34 fcntl34
 fcntl34_64 fcntl34_64
 fcntl35 fcntl35
 fcntl35_64 fcntl35_64
+fcntl36 fcntl36
+fcntl36_64 fcntl36_64
 
 fdatasync01 fdatasync01
 fdatasync02 fdatasync02
diff --git a/testcases/kernel/syscalls/fcntl/Makefile b/testcases/kernel/syscalls/fcntl/Makefile
index d78dd72..ae37214 100644
--- a/testcases/kernel/syscalls/fcntl/Makefile
+++ b/testcases/kernel/syscalls/fcntl/Makefile
@@ -24,6 +24,9 @@ fcntl33_64: LDLIBS+=-lrt
 fcntl34: LDLIBS += -lpthread
 fcntl34_64: LDLIBS += -lpthread
 
+fcntl36: LDLIBS += -lpthread
+fcntl36_64: LDLIBS += -lpthread
+
 include $(top_srcdir)/include/mk/testcases.mk
 include $(abs_srcdir)/../utils/newer_64.mk
 
diff --git a/testcases/kernel/syscalls/fcntl/fcntl36.c b/testcases/kernel/syscalls/fcntl/fcntl36.c
new file mode 100644
index 0000000..7aea434
--- /dev/null
+++ b/testcases/kernel/syscalls/fcntl/fcntl36.c
@@ -0,0 +1,386 @@
+/*
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Xiong Zhou <xzhou@redhat.com>
+ *
+ * This is testing OFD locks racing with POSIX locks:
+ *
+ *	OFD read  lock   vs   OFD   write lock
+ *	OFD read  lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX read  lock
+ *	OFD write lock   vs   OFD   write lock
+ *
+ * For example:
+ *
+ * 	Init an file with preset values.
+ *
+ * 	Threads acquire OFD READ  locks to read  a 4k section start from 0;
+ * 		checking data read back, there should not be any surprise
+ * 		values and data should be consistent in a 2k block.
+ *
+ * 	Threads acquire OFD WRITE locks to write a 4k section start from 2k,
+ * 		writing different values in different threads.
+ *
+ * 	Check file data after racing, there should not be any surprise values
+ * 		and data should be consistent in a 2k block.
+ *
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "lapi/fcntl.h"
+#include "tst_safe_pthread.h"
+#include "tst_test.h"
+
+static int thread_cnt;
+static volatile int loop_flag;
+static const int max_thread_cnt = 32;
+static const char fname[] = "tst_ofd_posix_locks";
+static const long write_size = 4096;
+
+struct param {
+	long offset;
+	long length;
+	long cnt;
+};
+
+static void setup(void)
+{
+	thread_cnt = tst_ncpus_conf() * 3;
+	if (thread_cnt > max_thread_cnt)
+		thread_cnt = max_thread_cnt;
+}
+
+#define debug
+
+/* OFD write lock writing data*/
+static void *fn_ofd_w(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (1) {
+
+		if (loop_flag == 1)
+			break;
+
+		memset(buf, wt, write_size);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX write lock writing data*/
+static void *fn_posix_w(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (1) {
+
+		if (loop_flag == 1)
+			break;
+
+		memset(buf, wt, write_size);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* OFD read lock reading data*/
+static void *fn_ofd_r(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (1) {
+
+		if (loop_flag == 1)
+			break;
+
+		memset(buf, 0, write_size);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				break;
+			}
+
+			long tmp = pa->length / 2;
+
+			if ((i < tmp && buf[i] != buf[0]) ||
+			    (i >= tmp && buf[i] != buf[tmp])) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX read lock reading data */
+static void *fn_posix_r(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (1) {
+
+		if (loop_flag == 1)
+			break;
+
+		memset(buf, 0, write_size);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data, "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				break;
+			}
+
+			long tmp = pa->length / 2;
+
+			if ((i < tmp && buf[i] != buf[0]) ||
+			    (i >= tmp && buf[i] != buf[tmp])) {
+
+				tst_res(TFAIL, "Unexpected data, "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* Test different functions and verify data */
+static void test_fn(void *f0(void *), void *f1(void *), const char *msg)
+{
+	int i, k, fd;
+	pthread_t id0[thread_cnt];
+	pthread_t id1[thread_cnt];
+	struct param p0[thread_cnt];
+	struct param p1[thread_cnt];
+	unsigned char buf[write_size];
+
+	if (tst_fill_file(fname, 1, write_size, thread_cnt)) {
+		tst_brk(TBROK, "Failed to create tst file");
+	}
+
+	tst_res(TINFO, msg);
+
+	for (i = 0; i < thread_cnt; i++) {
+
+		p0[i].offset = i * write_size;
+		p0[i].length = write_size;
+		p0[i].cnt = i + 2;
+
+		p1[i].offset = i * write_size;
+		p1[i].offset += write_size / 2;
+		p1[i].length = write_size;
+		p1[i].cnt = i + 2;
+	}
+
+	loop_flag = 0;
+	for (i = 0; i < thread_cnt; i++) {
+		SAFE_PTHREAD_CREATE(id0 + i, NULL, f0, (void *)&p0[i]);
+		SAFE_PTHREAD_CREATE(id1 + i, NULL, f1, (void *)&p1[i]);
+	}
+
+	sleep(3);
+	loop_flag = 1;
+	for (i = 0; i < thread_cnt; i++) {
+		SAFE_PTHREAD_JOIN(id0[i], NULL);
+		SAFE_PTHREAD_JOIN(id1[i], NULL);
+	}
+
+	tst_res(TINFO, "Verifying file's data");
+	fd = SAFE_OPEN(fname, O_RDWR);
+	SAFE_LSEEK(fd, 0, SEEK_SET);
+
+	for (i = 0; i < thread_cnt * 2; i++) {
+
+		SAFE_READ(1, fd, buf, write_size/2);
+
+		for (k = 0; k < write_size/2; k++) {
+
+			if (buf[k] < 2 || buf[k] > 254) {
+
+				if (i == 0 && buf[k] == 1)
+					continue;
+				tst_res(TFAIL, "Unexpected data, "
+					"offset %ld value %d",
+					i * write_size / 2 + k, buf[k]);
+				return;
+			}
+		}
+
+		for (k = 1; k < write_size/2; k++) {
+
+			if (buf[k] != buf[0]) {
+				tst_res(TFAIL, "Unexpected block read");
+				return;
+			}
+		}
+	}
+
+	SAFE_CLOSE(fd);
+	tst_res(TPASS, "Access between threads synchronized");
+}
+
+static struct tcase {
+
+	void *(*fn0)(void *);
+	void *(*fn1)(void *);
+	const char *desc;
+
+} tcases[] = {
+
+	{fn_ofd_r,   fn_ofd_w,   "OFD read locks vs OFD write locks"},
+	{fn_ofd_w,   fn_posix_w, "OFD write locks vs POSIX write locks"},
+	{fn_ofd_r,   fn_posix_w, "OFD read locks vs POSIX write locks"},
+	{fn_posix_r, fn_ofd_w,   "OFD write locks vs POSIX read locks"},
+	{fn_ofd_w,   fn_ofd_w,   "OFD write locks vs OFD write locks"},
+};
+
+static void tests(unsigned int i)
+{
+	test_fn(tcases[i].fn0, tcases[i].fn1, tcases[i].desc);
+}
+
+static struct tst_test test = {
+	.min_kver = "3.15",
+	.needs_tmpdir = 1,
+	.test = tests,
+	.tcnt = 5,
+	.setup = setup
+};
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v3] syscalls/fcntl36: add tests for OFD locks
  2017-08-22  8:35       ` [LTP] [PATCH v3] " Xiong Zhou
@ 2017-08-22 14:23         ` Cyril Hrubis
  2017-08-23  4:15           ` Xiong Zhou
  0 siblings, 1 reply; 17+ messages in thread
From: Cyril Hrubis @ 2017-08-22 14:23 UTC (permalink / raw)
  To: ltp

Hi!
>  runtest/syscalls                          |   2 +
>  testcases/kernel/syscalls/fcntl/Makefile  |   3 +
>  testcases/kernel/syscalls/fcntl/fcntl36.c | 386 ++++++++++++++++++++++++++++++

Don't forget about .gitignore entry as well.

...

> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <fcntl.h>
> +#include <pthread.h>
> +#include <sched.h>
> +#include <errno.h>
> +
> +#include "lapi/fcntl.h"
> +#include "tst_safe_pthread.h"
> +#include "tst_test.h"
> +
> +static int thread_cnt;
> +static volatile int loop_flag;
> +static const int max_thread_cnt = 32;
> +static const char fname[] = "tst_ofd_posix_locks";
> +static const long write_size = 4096;
> +
> +struct param {
> +	long offset;
> +	long length;
> +	long cnt;
> +};
> +
> +static void setup(void)
> +{
> +	thread_cnt = tst_ncpus_conf() * 3;
> +	if (thread_cnt > max_thread_cnt)
> +		thread_cnt = max_thread_cnt;
> +}
> +
> +#define debug
             ^
Forget to remove this

> +/* OFD write lock writing data*/
> +static void *fn_ofd_w(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	struct param *pa = (struct param *)arg;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +	long wt = pa->cnt;
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = pa->offset,
> +		.l_len    = pa->length,
> +		.l_pid    = 0,
> +	};
> +
> +	while (1) {
> +
> +		if (loop_flag == 1)
> +			break;

Common, why not just while (loop_flag) ?

And we set the loop_flag to 1 before we spawn the threads and then reset
it to 0 after we do the sleep.

> +		memset(buf, wt, write_size);
> +
> +		lck.l_type = F_WRLCK;
> +		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
> +
> +		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
> +		SAFE_WRITE(1, fd, buf, pa->length);
> +
> +		lck.l_type = F_UNLCK;
> +		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
> +
> +		wt++;
> +		if (wt >= 255)
> +			wt = pa->cnt;
> +
> +		sched_yield();
> +	}
> +
> +	SAFE_CLOSE(fd);
> +	return NULL;
> +}
> +
> +/* POSIX write lock writing data*/
> +static void *fn_posix_w(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	struct param *pa = (struct param *)arg;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +	long wt = pa->cnt;
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = pa->offset,
> +		.l_len    = pa->length,
> +	};
> +
> +	while (1) {
> +
> +		if (loop_flag == 1)
> +			break;

Here as well.

> +		memset(buf, wt, write_size);
> +
> +		lck.l_type = F_WRLCK;
> +		SAFE_FCNTL(fd, F_SETLKW, &lck);
> +
> +		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
> +		SAFE_WRITE(1, fd, buf, pa->length);
> +
> +		lck.l_type = F_UNLCK;
> +		SAFE_FCNTL(fd, F_SETLKW, &lck);
> +
> +		wt++;
> +		if (wt >= 255)
> +			wt = pa->cnt;
> +
> +		sched_yield();
> +	}
> +
> +	SAFE_CLOSE(fd);
> +	return NULL;
> +}
> +
> +/* OFD read lock reading data*/
> +static void *fn_ofd_r(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	struct param *pa = (struct param *)arg;
> +	int i;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = pa->offset,
> +		.l_len    = pa->length,
> +		.l_pid    = 0,
> +	};
> +
> +	while (1) {
> +
> +		if (loop_flag == 1)
> +			break;

And here as well.

> +		memset(buf, 0, write_size);
> +
> +		lck.l_type = F_RDLCK;
> +		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
> +
> +		/* rlock acquired */
> +		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
> +		SAFE_READ(1, fd, buf, pa->length);
> +
> +		/* Verifying data read */
> +		for (i = 0; i < pa->length; i++) {
> +
> +			if (buf[i] < 1 || buf[i] > 254) {
> +
> +				tst_res(TFAIL, "Unexpected data "
> +					"offset %ld value %d",
> +					pa->offset + i, buf[i]);
> +				break;
> +			}
> +
> +			long tmp = pa->length / 2;

We can simplify this with:
			int j = i / (pa->length/2);

			if (buf[i] != buf[j]) {
				...
			}

> +			if ((i < tmp && buf[i] != buf[0]) ||
> +			    (i >= tmp && buf[i] != buf[tmp])) {
> +
> +				tst_res(TFAIL, "Unexpected data "
> +					"offset %ld value %d",
> +					pa->offset + i, buf[i]);
> +				break;
> +			}
> +		}
> +
> +		lck.l_type = F_UNLCK;
> +		SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
> +
> +		sched_yield();
> +	}
> +
> +	SAFE_CLOSE(fd);
> +	return NULL;
> +}
> +
> +/* POSIX read lock reading data */
> +static void *fn_posix_r(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	struct param *pa = (struct param *)arg;
> +	int i;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = pa->offset,
> +		.l_len    = pa->length,
> +	};
> +
> +	while (1) {
> +
> +		if (loop_flag == 1)
> +			break;

Here as well.

> +		memset(buf, 0, write_size);
> +
> +		lck.l_type = F_RDLCK;
> +		SAFE_FCNTL(fd, F_SETLKW, &lck);
> +
> +		/* rlock acquired */
> +		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
> +		SAFE_READ(1, fd, buf, pa->length);
> +
> +		/* Verifying data read */
> +		for (i = 0; i < pa->length; i++) {
> +
> +			if (buf[i] < 1 || buf[i] > 254) {
> +
> +				tst_res(TFAIL, "Unexpected data, "
> +					"offset %ld value %d",
> +					pa->offset + i, buf[i]);
> +				break;
> +			}
> +
> +			long tmp = pa->length / 2;

And here as well.

> +			if ((i < tmp && buf[i] != buf[0]) ||
> +			    (i >= tmp && buf[i] != buf[tmp])) {
> +
> +				tst_res(TFAIL, "Unexpected data, "
> +					"offset %ld value %d",
> +					pa->offset + i, buf[i]);
> +				break;
> +			}
> +		}
> +
> +		lck.l_type = F_UNLCK;
> +		SAFE_FCNTL(fd, F_SETLK, &lck);
> +
> +		sched_yield();
> +	}
> +
> +	SAFE_CLOSE(fd);
> +	return NULL;
> +}
> +
> +/* Test different functions and verify data */
> +static void test_fn(void *f0(void *), void *f1(void *), const char *msg)
> +{
> +	int i, k, fd;
> +	pthread_t id0[thread_cnt];
> +	pthread_t id1[thread_cnt];
> +	struct param p0[thread_cnt];
> +	struct param p1[thread_cnt];
> +	unsigned char buf[write_size];
> +
> +	if (tst_fill_file(fname, 1, write_size, thread_cnt)) {
> +		tst_brk(TBROK, "Failed to create tst file");
> +	}

No need for the curly braces around single line statement here.

> +	tst_res(TINFO, msg);
> +
> +	for (i = 0; i < thread_cnt; i++) {
> +
> +		p0[i].offset = i * write_size;
> +		p0[i].length = write_size;
> +		p0[i].cnt = i + 2;
> +
> +		p1[i].offset = i * write_size;
> +		p1[i].offset += write_size / 2;
> +		p1[i].length = write_size;
> +		p1[i].cnt = i + 2;
> +	}
> +
> +	loop_flag = 0;
> +	for (i = 0; i < thread_cnt; i++) {
> +		SAFE_PTHREAD_CREATE(id0 + i, NULL, f0, (void *)&p0[i]);
> +		SAFE_PTHREAD_CREATE(id1 + i, NULL, f1, (void *)&p1[i]);
> +	}
> +
> +	sleep(3);

I would go fo 1s for default run, but that is very minor.

> +	loop_flag = 1;
> +	for (i = 0; i < thread_cnt; i++) {
> +		SAFE_PTHREAD_JOIN(id0[i], NULL);
> +		SAFE_PTHREAD_JOIN(id1[i], NULL);
> +	}
> +
> +	tst_res(TINFO, "Verifying file's data");
> +	fd = SAFE_OPEN(fname, O_RDWR);
> +	SAFE_LSEEK(fd, 0, SEEK_SET);

We do not write to the file, so we may as well open it RDONLY and since
we are opening it we don't have to seek it to the start.

> +	for (i = 0; i < thread_cnt * 2; i++) {
> +
> +		SAFE_READ(1, fd, buf, write_size/2);
> +
> +		for (k = 0; k < write_size/2; k++) {
> +
> +			if (buf[k] < 2 || buf[k] > 254) {
> +
> +				if (i == 0 && buf[k] == 1)
> +					continue;
> +				tst_res(TFAIL, "Unexpected data, "
> +					"offset %ld value %d",
> +					i * write_size / 2 + k, buf[k]);
> +				return;
> +			}
> +		}
> +
> +		for (k = 1; k < write_size/2; k++) {
> +
> +			if (buf[k] != buf[0]) {
> +				tst_res(TFAIL, "Unexpected block read");
> +				return;
> +			}
> +		}
> +	}

We should close the fd before the return here.

> +	SAFE_CLOSE(fd);
> +	tst_res(TPASS, "Access between threads synchronized");

And here we would print TPASS even if reader thread failed the test. So
we should either change tst_res() to tst_brk() in the reader thread
functions so that we exit the test if we read back wrong data. Or setup
a global failure counter and print TPASS only if the counter was not set
from one of the reader threads. Or propagate the failure from the tread
return value (the one you get from SAFE_JOIN()).

> +}
> +
> +static struct tcase {
> +
> +	void *(*fn0)(void *);
> +	void *(*fn1)(void *);
> +	const char *desc;
> +
> +} tcases[] = {
> +
> +	{fn_ofd_r,   fn_ofd_w,   "OFD read locks vs OFD write locks"},
> +	{fn_ofd_w,   fn_posix_w, "OFD write locks vs POSIX write locks"},
> +	{fn_ofd_r,   fn_posix_w, "OFD read locks vs POSIX write locks"},
> +	{fn_posix_r, fn_ofd_w,   "OFD write locks vs POSIX read locks"},
> +	{fn_ofd_w,   fn_ofd_w,   "OFD write locks vs OFD write locks"},
> +};
> +
> +static void tests(unsigned int i)
> +{
> +	test_fn(tcases[i].fn0, tcases[i].fn1, tcases[i].desc);
> +}
> +
> +static struct tst_test test = {
> +	.min_kver = "3.15",
> +	.needs_tmpdir = 1,
> +	.test = tests,
> +	.tcnt = 5,
                ^
		ARRAY_SIZE(tcases) is safer here, that way it will be
		correct even if we add/remove tests in the array.
> +	.setup = setup
> +};
> -- 
> 1.8.3.1
> 

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v3] syscalls/fcntl36: add tests for OFD locks
  2017-08-22 14:23         ` Cyril Hrubis
@ 2017-08-23  4:15           ` Xiong Zhou
  2017-08-23  8:40             ` Cyril Hrubis
  2017-08-23  9:53             ` [LTP] [PATCH v3] " Cyril Hrubis
  0 siblings, 2 replies; 17+ messages in thread
From: Xiong Zhou @ 2017-08-23  4:15 UTC (permalink / raw)
  To: ltp

On Tue, Aug 22, 2017 at 04:23:57PM +0200, Cyril Hrubis wrote:
> Hi!
> >  runtest/syscalls                          |   2 +
> >  testcases/kernel/syscalls/fcntl/Makefile  |   3 +
> >  testcases/kernel/syscalls/fcntl/fcntl36.c | 386 ++++++++++++++++++++++++++++++
> 
> Don't forget about .gitignore entry as well.

Ha, now i found the right one, :)

> 
> ...
> 
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
...
> > +		SAFE_READ(1, fd, buf, pa->length);
> > +
> > +		/* Verifying data read */
> > +		for (i = 0; i < pa->length; i++) {
> > +
> > +			if (buf[i] < 1 || buf[i] > 254) {
> > +
> > +				tst_res(TFAIL, "Unexpected data "
> > +					"offset %ld value %d",
> > +					pa->offset + i, buf[i]);
> > +				break;
> > +			}
> > +
> > +			long tmp = pa->length / 2;
> 
> We can simplify this with:
> 			int j = i / (pa->length/2);
> 
> 			if (buf[i] != buf[j]) {
> 				...
> 			}

After checking, this is not what I meant to check here about
this data verifying.

We could read 4k data that first half 2k of it have the same
value and the another half 2k have a different value.

> 
> > +			if ((i < tmp && buf[i] != buf[0]) ||
> > +			    (i >= tmp && buf[i] != buf[tmp])) {
> > +
> > +				tst_res(TFAIL, "Unexpected data "
> > +					"offset %ld value %d",
> > +					pa->offset + i, buf[i]);
> > +				break;
> > +			}
> > +		}
> > +
...
> > +	if (tst_fill_file(fname, 1, write_size, thread_cnt)) {
> > +		tst_brk(TBROK, "Failed to create tst file");
> > +	}
> 
> No need for the curly braces around single line statement here.

I thought it's safer for a macro. Now it's one line function in
the macro, not hurting to remove the braces.

Thanks for reviewing!
Xiong

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v3] syscalls/fcntl36: add tests for OFD locks
  2017-08-23  4:15           ` Xiong Zhou
@ 2017-08-23  8:40             ` Cyril Hrubis
  2017-08-23  9:42               ` [LTP] [PATCH v4] " Xiong Zhou
  2017-08-23  9:53             ` [LTP] [PATCH v3] " Cyril Hrubis
  1 sibling, 1 reply; 17+ messages in thread
From: Cyril Hrubis @ 2017-08-23  8:40 UTC (permalink / raw)
  To: ltp

Hi!
> > > +	if (tst_fill_file(fname, 1, write_size, thread_cnt)) {
> > > +		tst_brk(TBROK, "Failed to create tst file");
> > > +	}
> > 
> > No need for the curly braces around single line statement here.
> 
> I thought it's safer for a macro. Now it's one line function in
> the macro, not hurting to remove the braces.

Even macros that have several lines are safe in LTP since we use the
do { ... } while (0) idiom.

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v4] syscalls/fcntl36: add tests for OFD locks
  2017-08-23  8:40             ` Cyril Hrubis
@ 2017-08-23  9:42               ` Xiong Zhou
  0 siblings, 0 replies; 17+ messages in thread
From: Xiong Zhou @ 2017-08-23  9:42 UTC (permalink / raw)
  To: ltp

Testing OFD locks racing with POSIX locks:

  OFD read  lock   vs   OFD   write lock
  OFD read  lock   vs   POSIX write lock
  OFD write lock   vs   POSIX write lock
  OFD write lock   vs   POSIX read  lock
  OFD write lock   vs   OFD   write lock

Signed-off-by: Xiong Zhou <xzhou@redhat.com>
---

v4:
  Add .gitignore entry.
  Count failures in reader, then check it in main loop.
  Other fix.

v3:
  Use tst_fill_file.
  Loop in test function.
  Other fix.

v2:
  Basically rewritten.
  Pass structure parameter to threads.
  Block on read lock.
  Spawn racing threads one by one.
  Use tcnt iteration.

 runtest/syscalls                          |   2 +
 testcases/kernel/syscalls/.gitignore      |   2 +
 testcases/kernel/syscalls/fcntl/Makefile  |   3 +
 testcases/kernel/syscalls/fcntl/fcntl36.c | 378 ++++++++++++++++++++++++++++++
 4 files changed, 385 insertions(+)
 create mode 100644 testcases/kernel/syscalls/fcntl/fcntl36.c

diff --git a/runtest/syscalls b/runtest/syscalls
index 407e055..f2b2cbc 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -271,6 +271,8 @@ fcntl34 fcntl34
 fcntl34_64 fcntl34_64
 fcntl35 fcntl35
 fcntl35_64 fcntl35_64
+fcntl36 fcntl36
+fcntl36_64 fcntl36_64
 
 fdatasync01 fdatasync01
 fdatasync02 fdatasync02
diff --git a/testcases/kernel/syscalls/.gitignore b/testcases/kernel/syscalls/.gitignore
index 2a7026d..32193e1 100644
--- a/testcases/kernel/syscalls/.gitignore
+++ b/testcases/kernel/syscalls/.gitignore
@@ -234,6 +234,8 @@
 /fcntl/fcntl34_64
 /fcntl/fcntl35
 /fcntl/fcntl35_64
+/fcntl/fcntl36
+/fcntl/fcntl36_64
 /fdatasync/fdatasync01
 /fdatasync/fdatasync02
 /flistxattr/flistxattr01
diff --git a/testcases/kernel/syscalls/fcntl/Makefile b/testcases/kernel/syscalls/fcntl/Makefile
index d78dd72..ae37214 100644
--- a/testcases/kernel/syscalls/fcntl/Makefile
+++ b/testcases/kernel/syscalls/fcntl/Makefile
@@ -24,6 +24,9 @@ fcntl33_64: LDLIBS+=-lrt
 fcntl34: LDLIBS += -lpthread
 fcntl34_64: LDLIBS += -lpthread
 
+fcntl36: LDLIBS += -lpthread
+fcntl36_64: LDLIBS += -lpthread
+
 include $(top_srcdir)/include/mk/testcases.mk
 include $(abs_srcdir)/../utils/newer_64.mk
 
diff --git a/testcases/kernel/syscalls/fcntl/fcntl36.c b/testcases/kernel/syscalls/fcntl/fcntl36.c
new file mode 100644
index 0000000..1afa278
--- /dev/null
+++ b/testcases/kernel/syscalls/fcntl/fcntl36.c
@@ -0,0 +1,378 @@
+/*
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Xiong Zhou <xzhou@redhat.com>
+ *
+ * This is testing OFD locks racing with POSIX locks:
+ *
+ *	OFD read  lock   vs   OFD   write lock
+ *	OFD read  lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX read  lock
+ *	OFD write lock   vs   OFD   write lock
+ *
+ * For example:
+ *
+ * 	Init an file with preset values.
+ *
+ * 	Threads acquire OFD READ  locks to read  a 4k section start from 0;
+ * 		checking data read back, there should not be any surprise
+ * 		values and data should be consistent in a 2k block.
+ *
+ * 	Threads acquire OFD WRITE locks to write a 4k section start from 2k,
+ * 		writing different values in different threads.
+ *
+ * 	Check file data after racing, there should not be any surprise values
+ * 		and data should be consistent in a 2k block.
+ *
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "lapi/fcntl.h"
+#include "tst_safe_pthread.h"
+#include "tst_test.h"
+
+static int thread_cnt;
+static int fail_cnt = 0;
+static volatile int loop_flag = 1;
+static const int max_thread_cnt = 32;
+static const char fname[] = "tst_ofd_posix_locks";
+static const long write_size = 4096;
+
+struct param {
+	long offset;
+	long length;
+	long cnt;
+};
+
+static void setup(void)
+{
+	thread_cnt = tst_ncpus_conf() * 3;
+	if (thread_cnt > max_thread_cnt)
+		thread_cnt = max_thread_cnt;
+}
+
+/* OFD write lock writing data*/
+static void *fn_ofd_w(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, wt, write_size);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX write lock writing data*/
+static void *fn_posix_w(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, wt, write_size);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* OFD read lock reading data*/
+static void *fn_ofd_r(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, 0, write_size);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_cnt++;
+				break;
+			}
+
+			long tmp = pa->length / 2;
+
+			if ((i < tmp && buf[i] != buf[0]) ||
+			    (i >= tmp && buf[i] != buf[tmp])) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_cnt++;
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX read lock reading data */
+static void *fn_posix_r(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, 0, write_size);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data, "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_cnt++;
+				break;
+			}
+
+			long tmp = pa->length / 2;
+
+			if ((i < tmp && buf[i] != buf[0]) ||
+			    (i >= tmp && buf[i] != buf[tmp])) {
+
+				tst_res(TFAIL, "Unexpected data, "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_cnt++;
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* Test different functions and verify data */
+static void test_fn(void *f0(void *), void *f1(void *), const char *msg)
+{
+	int i, k, fd;
+	pthread_t id0[thread_cnt];
+	pthread_t id1[thread_cnt];
+	struct param p0[thread_cnt];
+	struct param p1[thread_cnt];
+	unsigned char buf[write_size];
+
+	if (tst_fill_file(fname, 1, write_size, thread_cnt))
+		tst_brk(TBROK, "Failed to create tst file");
+
+	tst_res(TINFO, msg);
+
+	for (i = 0; i < thread_cnt; i++) {
+
+		p0[i].offset = i * write_size;
+		p0[i].length = write_size;
+		p0[i].cnt = i + 2;
+
+		p1[i].offset = i * write_size;
+		p1[i].offset += write_size / 2;
+		p1[i].length = write_size;
+		p1[i].cnt = i + 2;
+	}
+
+	loop_flag = 1;
+	for (i = 0; i < thread_cnt; i++) {
+		SAFE_PTHREAD_CREATE(id0 + i, NULL, f0, (void *)&p0[i]);
+		SAFE_PTHREAD_CREATE(id1 + i, NULL, f1, (void *)&p1[i]);
+	}
+
+	sleep(1);
+	loop_flag = 0;
+	for (i = 0; i < thread_cnt; i++) {
+		SAFE_PTHREAD_JOIN(id0[i], NULL);
+		SAFE_PTHREAD_JOIN(id1[i], NULL);
+	}
+
+	tst_res(TINFO, "Verifying file's data");
+	fd = SAFE_OPEN(fname, O_RDONLY);
+
+	for (i = 0; i < thread_cnt * 2; i++) {
+
+		SAFE_READ(1, fd, buf, write_size/2);
+
+		for (k = 0; k < write_size/2; k++) {
+
+			if (buf[k] < 2 || buf[k] > 254) {
+
+				if (i == 0 && buf[k] == 1)
+					continue;
+				tst_res(TFAIL, "Unexpected data, "
+					"offset %ld value %d",
+					i * write_size / 2 + k, buf[k]);
+				SAFE_CLOSE(fd);
+				return;
+			}
+		}
+
+		for (k = 1; k < write_size/2; k++) {
+
+			if (buf[k] != buf[0]) {
+				tst_res(TFAIL, "Unexpected block read");
+				SAFE_CLOSE(fd);
+				return;
+			}
+		}
+	}
+
+	SAFE_CLOSE(fd);
+	if (fail_cnt == 0)
+		tst_res(TPASS, "Access between threads synchronized");
+}
+
+static struct tcase {
+
+	void *(*fn0)(void *);
+	void *(*fn1)(void *);
+	const char *desc;
+
+} tcases[] = {
+
+	{fn_ofd_r,   fn_ofd_w,   "OFD read locks vs OFD write locks"},
+	{fn_ofd_w,   fn_posix_w, "OFD write locks vs POSIX write locks"},
+	{fn_ofd_r,   fn_posix_w, "OFD read locks vs POSIX write locks"},
+	{fn_posix_r, fn_ofd_w,   "OFD write locks vs POSIX read locks"},
+	{fn_ofd_w,   fn_ofd_w,   "OFD write locks vs OFD write locks"},
+};
+
+static void tests(unsigned int i)
+{
+	test_fn(tcases[i].fn0, tcases[i].fn1, tcases[i].desc);
+}
+
+static struct tst_test test = {
+	.min_kver = "3.15",
+	.needs_tmpdir = 1,
+	.test = tests,
+	.tcnt = ARRAY_SIZE(tcases),
+	.setup = setup
+};
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v3] syscalls/fcntl36: add tests for OFD locks
  2017-08-23  4:15           ` Xiong Zhou
  2017-08-23  8:40             ` Cyril Hrubis
@ 2017-08-23  9:53             ` Cyril Hrubis
  2017-08-23 13:15               ` [LTP] [PATCH v5] " Xiong Zhou
  1 sibling, 1 reply; 17+ messages in thread
From: Cyril Hrubis @ 2017-08-23  9:53 UTC (permalink / raw)
  To: ltp

Hi!
> > > +#include <sys/types.h>
> > > +#include <sys/stat.h>
> ...
> > > +		SAFE_READ(1, fd, buf, pa->length);
> > > +
> > > +		/* Verifying data read */
> > > +		for (i = 0; i < pa->length; i++) {
> > > +
> > > +			if (buf[i] < 1 || buf[i] > 254) {
> > > +
> > > +				tst_res(TFAIL, "Unexpected data "
> > > +					"offset %ld value %d",
> > > +					pa->offset + i, buf[i]);
> > > +				break;
> > > +			}
> > > +
> > > +			long tmp = pa->length / 2;
> > 
> > We can simplify this with:
> > 			int j = i / (pa->length/2);
> > 
> > 			if (buf[i] != buf[j]) {
> > 				...
> > 			}
> 
> After checking, this is not what I meant to check here about
> this data verifying.
> 
> We could read 4k data that first half 2k of it have the same
> value and the another half 2k have a different value.

Ah right, made a silly mistake the correct formula is:

	int j = (i / (pa->length/2)) * pa->length/2;

The previous one was yielding incorrect result for i >= pa->length/2.

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v5] syscalls/fcntl36: add tests for OFD locks
  2017-08-23  9:53             ` [LTP] [PATCH v3] " Cyril Hrubis
@ 2017-08-23 13:15               ` Xiong Zhou
  2017-08-23 13:35                 ` Cyril Hrubis
  0 siblings, 1 reply; 17+ messages in thread
From: Xiong Zhou @ 2017-08-23 13:15 UTC (permalink / raw)
  To: ltp

Testing OFD locks racing with POSIX locks:

  OFD read  lock   vs   OFD   write lock
  OFD read  lock   vs   POSIX write lock
  OFD write lock   vs   POSIX write lock
  OFD write lock   vs   POSIX read  lock
  OFD write lock   vs   OFD   write lock

Signed-off-by: Xiong Zhou <xzhou@redhat.com>
---

v5:
  Optimize data checking.

v4:
  Add .gitignore entry.
  Count failures in reader, then check it in main loop.
  Other fix.

v3:
  Use tst_fill_file.
  Loop in test function.
  Other fix.

v2:
  Basically rewritten.
  Pass structure parameter to threads.
  Block on read lock.
  Spawn racing threads one by one.
  Use tcnt iteration.
 runtest/syscalls                          |   2 +
 testcases/kernel/syscalls/.gitignore      |   2 +
 testcases/kernel/syscalls/fcntl/Makefile  |   3 +
 testcases/kernel/syscalls/fcntl/fcntl36.c | 376 ++++++++++++++++++++++++++++++
 4 files changed, 383 insertions(+)
 create mode 100644 testcases/kernel/syscalls/fcntl/fcntl36.c

diff --git a/runtest/syscalls b/runtest/syscalls
index 407e055..f2b2cbc 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -271,6 +271,8 @@ fcntl34 fcntl34
 fcntl34_64 fcntl34_64
 fcntl35 fcntl35
 fcntl35_64 fcntl35_64
+fcntl36 fcntl36
+fcntl36_64 fcntl36_64
 
 fdatasync01 fdatasync01
 fdatasync02 fdatasync02
diff --git a/testcases/kernel/syscalls/.gitignore b/testcases/kernel/syscalls/.gitignore
index 2a7026d..32193e1 100644
--- a/testcases/kernel/syscalls/.gitignore
+++ b/testcases/kernel/syscalls/.gitignore
@@ -234,6 +234,8 @@
 /fcntl/fcntl34_64
 /fcntl/fcntl35
 /fcntl/fcntl35_64
+/fcntl/fcntl36
+/fcntl/fcntl36_64
 /fdatasync/fdatasync01
 /fdatasync/fdatasync02
 /flistxattr/flistxattr01
diff --git a/testcases/kernel/syscalls/fcntl/Makefile b/testcases/kernel/syscalls/fcntl/Makefile
index d78dd72..ae37214 100644
--- a/testcases/kernel/syscalls/fcntl/Makefile
+++ b/testcases/kernel/syscalls/fcntl/Makefile
@@ -24,6 +24,9 @@ fcntl33_64: LDLIBS+=-lrt
 fcntl34: LDLIBS += -lpthread
 fcntl34_64: LDLIBS += -lpthread
 
+fcntl36: LDLIBS += -lpthread
+fcntl36_64: LDLIBS += -lpthread
+
 include $(top_srcdir)/include/mk/testcases.mk
 include $(abs_srcdir)/../utils/newer_64.mk
 
diff --git a/testcases/kernel/syscalls/fcntl/fcntl36.c b/testcases/kernel/syscalls/fcntl/fcntl36.c
new file mode 100644
index 0000000..7bd001a
--- /dev/null
+++ b/testcases/kernel/syscalls/fcntl/fcntl36.c
@@ -0,0 +1,376 @@
+/*
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Xiong Zhou <xzhou@redhat.com>
+ *
+ * This is testing OFD locks racing with POSIX locks:
+ *
+ *	OFD read  lock   vs   OFD   write lock
+ *	OFD read  lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX read  lock
+ *	OFD write lock   vs   OFD   write lock
+ *
+ * For example:
+ *
+ * 	Init an file with preset values.
+ *
+ * 	Threads acquire OFD READ  locks to read  a 4k section start from 0;
+ * 		checking data read back, there should not be any surprise
+ * 		values and data should be consistent in a 2k block.
+ *
+ * 	Threads acquire OFD WRITE locks to write a 4k section start from 2k,
+ * 		writing different values in different threads.
+ *
+ * 	Check file data after racing, there should not be any surprise values
+ * 		and data should be consistent in a 2k block.
+ *
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "lapi/fcntl.h"
+#include "tst_safe_pthread.h"
+#include "tst_test.h"
+
+static int thread_cnt;
+static int fail_cnt = 0;
+static volatile int loop_flag = 1;
+static const int max_thread_cnt = 32;
+static const char fname[] = "tst_ofd_posix_locks";
+static const long write_size = 4096;
+
+struct param {
+	long offset;
+	long length;
+	long cnt;
+};
+
+static void setup(void)
+{
+	thread_cnt = tst_ncpus_conf() * 3;
+	if (thread_cnt > max_thread_cnt)
+		thread_cnt = max_thread_cnt;
+}
+
+/* OFD write lock writing data*/
+static void *fn_ofd_w(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, wt, write_size);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX write lock writing data*/
+static void *fn_posix_w(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, wt, write_size);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* OFD read lock reading data*/
+static void *fn_ofd_r(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, 0, write_size);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_cnt++;
+				break;
+			}
+
+			int j = (i / (pa->length/2)) * pa->length/2;
+
+			if (buf[i] != buf[j]) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_cnt++;
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX read lock reading data */
+static void *fn_posix_r(void *arg)
+{
+	unsigned char buf[write_size];
+	struct param *pa = (struct param *)arg;
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, 0, write_size);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data, "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_cnt++;
+				break;
+			}
+
+			int j = (i / (pa->length/2)) * pa->length/2;
+
+			if (buf[i] != buf[j]) {
+
+				tst_res(TFAIL, "Unexpected data, "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_cnt++;
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* Test different functions and verify data */
+static void test_fn(void *f0(void *), void *f1(void *), const char *msg)
+{
+	int i, k, fd;
+	pthread_t id0[thread_cnt];
+	pthread_t id1[thread_cnt];
+	struct param p0[thread_cnt];
+	struct param p1[thread_cnt];
+	unsigned char buf[write_size];
+
+	if (tst_fill_file(fname, 1, write_size, thread_cnt))
+		tst_brk(TBROK, "Failed to create tst file");
+
+	tst_res(TINFO, msg);
+
+	for (i = 0; i < thread_cnt; i++) {
+
+		p0[i].offset = i * write_size;
+		p0[i].length = write_size;
+		p0[i].cnt = i + 2;
+
+		p1[i].offset = i * write_size;
+		p1[i].offset += write_size / 2;
+		p1[i].length = write_size;
+		p1[i].cnt = i + 2;
+	}
+
+	loop_flag = 1;
+	for (i = 0; i < thread_cnt; i++) {
+		SAFE_PTHREAD_CREATE(id0 + i, NULL, f0, (void *)&p0[i]);
+		SAFE_PTHREAD_CREATE(id1 + i, NULL, f1, (void *)&p1[i]);
+	}
+
+	sleep(1);
+	loop_flag = 0;
+	for (i = 0; i < thread_cnt; i++) {
+		SAFE_PTHREAD_JOIN(id0[i], NULL);
+		SAFE_PTHREAD_JOIN(id1[i], NULL);
+	}
+
+	tst_res(TINFO, "Verifying file's data");
+	fd = SAFE_OPEN(fname, O_RDONLY);
+
+	for (i = 0; i < thread_cnt * 2; i++) {
+
+		SAFE_READ(1, fd, buf, write_size/2);
+
+		for (k = 0; k < write_size/2; k++) {
+
+			if (buf[k] < 2 || buf[k] > 254) {
+
+				if (i == 0 && buf[k] == 1)
+					continue;
+				tst_res(TFAIL, "Unexpected data, "
+					"offset %ld value %d",
+					i * write_size / 2 + k, buf[k]);
+				SAFE_CLOSE(fd);
+				return;
+			}
+		}
+
+		for (k = 1; k < write_size/2; k++) {
+
+			if (buf[k] != buf[0]) {
+				tst_res(TFAIL, "Unexpected block read");
+				SAFE_CLOSE(fd);
+				return;
+			}
+		}
+	}
+
+	SAFE_CLOSE(fd);
+	if (fail_cnt == 0)
+		tst_res(TPASS, "Access between threads synchronized");
+}
+
+static struct tcase {
+
+	void *(*fn0)(void *);
+	void *(*fn1)(void *);
+	const char *desc;
+
+} tcases[] = {
+
+	{fn_ofd_r,   fn_ofd_w,   "OFD read locks vs OFD write locks"},
+	{fn_ofd_w,   fn_posix_w, "OFD write locks vs POSIX write locks"},
+	{fn_ofd_r,   fn_posix_w, "OFD read locks vs POSIX write locks"},
+	{fn_posix_r, fn_ofd_w,   "OFD write locks vs POSIX read locks"},
+	{fn_ofd_w,   fn_ofd_w,   "OFD write locks vs OFD write locks"},
+};
+
+static void tests(unsigned int i)
+{
+	test_fn(tcases[i].fn0, tcases[i].fn1, tcases[i].desc);
+}
+
+static struct tst_test test = {
+	.min_kver = "3.15",
+	.needs_tmpdir = 1,
+	.test = tests,
+	.tcnt = ARRAY_SIZE(tcases),
+	.setup = setup
+};
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v5] syscalls/fcntl36: add tests for OFD locks
  2017-08-23 13:15               ` [LTP] [PATCH v5] " Xiong Zhou
@ 2017-08-23 13:35                 ` Cyril Hrubis
  2017-08-24  7:48                   ` [LTP] [PATCH v6] " Xiong Zhou
  0 siblings, 1 reply; 17+ messages in thread
From: Cyril Hrubis @ 2017-08-23 13:35 UTC (permalink / raw)
  To: ltp

Hi!
> +/* OFD write lock writing data*/
> +static void *fn_ofd_w(void *arg)
> +{
> +	unsigned char buf[write_size];
> +	struct param *pa = (struct param *)arg;
> +	int fd = SAFE_OPEN(fname, O_RDWR);
> +	long wt = pa->cnt;
> +
> +	struct flock64 lck = {
> +		.l_whence = SEEK_SET,
> +		.l_start  = pa->offset,
> +		.l_len    = pa->length,
> +		.l_pid    = 0,
> +	};
> +
> +	while (loop_flag) {
> +
> +		memset(buf, wt, write_size);
> +
> +		lck.l_type = F_WRLCK;
> +		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
> +
> +		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
> +		SAFE_WRITE(1, fd, buf, pa->length);

Just a minor note, we use both write_size and pa->length randomly here
and in all of the test functions. It would be a much cleaner to use only
pa->lenght instead.

> +/* Test different functions and verify data */
> +static void test_fn(void *f0(void *), void *f1(void *), const char *msg)
> +{
> +	int i, k, fd;
> +	pthread_t id0[thread_cnt];
> +	pthread_t id1[thread_cnt];
> +	struct param p0[thread_cnt];
> +	struct param p1[thread_cnt];
> +	unsigned char buf[write_size];
> +
> +	if (tst_fill_file(fname, 1, write_size, thread_cnt))
> +		tst_brk(TBROK, "Failed to create tst file");
> +
> +	tst_res(TINFO, msg);
> +
> +	for (i = 0; i < thread_cnt; i++) {
> +
> +		p0[i].offset = i * write_size;
> +		p0[i].length = write_size;
> +		p0[i].cnt = i + 2;
> +
> +		p1[i].offset = i * write_size;
> +		p1[i].offset += write_size / 2;
> +		p1[i].length = write_size;
> +		p1[i].cnt = i + 2;
> +	}

We should reset the fail_cnt here. And it would be safer to rename it to
fail_flag and set it to 1 on a failure because if we are terribly
unlucky the incements can overflow and we may as well end up with a
zero.

Otherwise it looks good.

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v6] syscalls/fcntl36: add tests for OFD locks
  2017-08-23 13:35                 ` Cyril Hrubis
@ 2017-08-24  7:48                   ` Xiong Zhou
  2017-08-29 14:27                     ` Cyril Hrubis
  0 siblings, 1 reply; 17+ messages in thread
From: Xiong Zhou @ 2017-08-24  7:48 UTC (permalink / raw)
  To: ltp

Testing OFD locks racing with POSIX locks:

  OFD read  lock   vs   OFD   write lock
  OFD read  lock   vs   POSIX write lock
  OFD write lock   vs   POSIX write lock
  OFD write lock   vs   POSIX read  lock
  OFD write lock   vs   OFD   write lock
  OFD   r/w locks  vs   POSIX write locks
  POSIX r/w locks  vs   OFD   write locks
  OFD   r/w locks  vs   POSIX read  locks
  POSIX r/w locks  vs   OFD   read  locks
  OFD   r/w locks  vs   POSIX r/w   locks

Signed-off-by: Xiong Zhou <xzhou@redhat.com>
---

v6:
  Use param->length in thread_fn as r/w size instead of
write_size directly, thanks to C99.

  Reset fail_flag after each sub case.

  Race up to 4 bunches of threads acquiring these 4 kinds of locks,
not just only 2 of them. (kind crazy)

v5:
  Optimize data checking.

v4:
  Add .gitignore entry.
  Count failures in reader, then check it in main loop.
  Other fix.

v3:
  Use tst_fill_file.
  Loop in test function.
  Other fix.

v2:
  Basically rewritten.
  Pass structure parameter to threads.
  Block on read lock.
  Spawn racing threads one by one.
  Use tcnt iteration.

 runtest/syscalls                          |   2 +
 testcases/kernel/syscalls/.gitignore      |   2 +
 testcases/kernel/syscalls/fcntl/Makefile  |   3 +
 testcases/kernel/syscalls/fcntl/fcntl36.c | 429 ++++++++++++++++++++++++++++++
 4 files changed, 436 insertions(+)
 create mode 100644 testcases/kernel/syscalls/fcntl/fcntl36.c

diff --git a/runtest/syscalls b/runtest/syscalls
index 407e055..f2b2cbc 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -271,6 +271,8 @@ fcntl34 fcntl34
 fcntl34_64 fcntl34_64
 fcntl35 fcntl35
 fcntl35_64 fcntl35_64
+fcntl36 fcntl36
+fcntl36_64 fcntl36_64
 
 fdatasync01 fdatasync01
 fdatasync02 fdatasync02
diff --git a/testcases/kernel/syscalls/.gitignore b/testcases/kernel/syscalls/.gitignore
index 2a7026d..32193e1 100644
--- a/testcases/kernel/syscalls/.gitignore
+++ b/testcases/kernel/syscalls/.gitignore
@@ -234,6 +234,8 @@
 /fcntl/fcntl34_64
 /fcntl/fcntl35
 /fcntl/fcntl35_64
+/fcntl/fcntl36
+/fcntl/fcntl36_64
 /fdatasync/fdatasync01
 /fdatasync/fdatasync02
 /flistxattr/flistxattr01
diff --git a/testcases/kernel/syscalls/fcntl/Makefile b/testcases/kernel/syscalls/fcntl/Makefile
index d78dd72..ae37214 100644
--- a/testcases/kernel/syscalls/fcntl/Makefile
+++ b/testcases/kernel/syscalls/fcntl/Makefile
@@ -24,6 +24,9 @@ fcntl33_64: LDLIBS+=-lrt
 fcntl34: LDLIBS += -lpthread
 fcntl34_64: LDLIBS += -lpthread
 
+fcntl36: LDLIBS += -lpthread
+fcntl36_64: LDLIBS += -lpthread
+
 include $(top_srcdir)/include/mk/testcases.mk
 include $(abs_srcdir)/../utils/newer_64.mk
 
diff --git a/testcases/kernel/syscalls/fcntl/fcntl36.c b/testcases/kernel/syscalls/fcntl/fcntl36.c
new file mode 100644
index 0000000..e1205e2
--- /dev/null
+++ b/testcases/kernel/syscalls/fcntl/fcntl36.c
@@ -0,0 +1,429 @@
+/*
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Xiong Zhou <xzhou@redhat.com>
+ *
+ * This is testing OFD locks racing with POSIX locks:
+ *
+ *	OFD read  lock   vs   OFD   write lock
+ *	OFD read  lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX read  lock
+ *	OFD write lock   vs   OFD   write lock
+ *
+ *	OFD   r/w locks vs POSIX write locks
+ *	POSIX r/w locks vs OFD   write locks
+ *	OFD   r/w locks vs POSIX read locks
+ *	POSIX r/w locks vs OFD   read locks
+ *
+ *	OFD   r/w locks vs POSIX r/w locks
+ *
+ * For example:
+ *
+ * 	Init an file with preset values.
+ *
+ * 	Threads acquire OFD READ  locks to read  a 4k section start from 0;
+ * 		checking data read back, there should not be any surprise
+ * 		values and data should be consistent in a 2k block.
+ *
+ * 	Threads acquire OFD WRITE locks to write a 4k section start from 1k,
+ * 		writing different values in different threads.
+ *
+ * 	Check file data after racing, there should not be any surprise values
+ * 		and data should be consistent in a 1k block.
+ *
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "lapi/fcntl.h"
+#include "tst_safe_pthread.h"
+#include "tst_test.h"
+
+static int thread_cnt;
+static int fail_flag = 0;
+static volatile int loop_flag = 1;
+static const int max_thread_cnt = 32;
+static const char fname[] = "tst_ofd_posix_locks";
+static const long write_size = 4096;
+
+struct param {
+	long offset;
+	long length;
+	long cnt;
+};
+
+static void setup(void)
+{
+	thread_cnt = tst_ncpus_conf() * 3;
+	if (thread_cnt > max_thread_cnt)
+		thread_cnt = max_thread_cnt;
+}
+
+/* OFD write lock writing data*/
+static void *fn_ofd_w(void *arg)
+{
+	struct param *pa = (struct param *)arg;
+	unsigned char buf[pa->length];
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, wt, pa->length);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX write lock writing data*/
+static void *fn_posix_w(void *arg)
+{
+	struct param *pa = (struct param *)arg;
+	unsigned char buf[pa->length];
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, wt, pa->length);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* OFD read lock reading data*/
+static void *fn_ofd_r(void *arg)
+{
+	struct param *pa = (struct param *)arg;
+	unsigned char buf[pa->length];
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, 0, pa->length);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_flag = 1;
+				break;
+			}
+
+			int j = (i / (pa->length/4)) * pa->length/4;
+
+			if (buf[i] != buf[j]) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_flag = 1;
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX read lock reading data */
+static void *fn_posix_r(void *arg)
+{
+	struct param *pa = (struct param *)arg;
+	unsigned char buf[pa->length];
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, 0, pa->length);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_flag = 1;
+				break;
+			}
+
+			int j = (i / (pa->length/4)) * pa->length/4;
+
+			if (buf[i] != buf[j]) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_flag = 1;
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* Test different functions and verify data */
+static void test_fn(void *f0(void *), void *f1(void *),
+		    void *f2(void *), void *f3(void *), const char *msg)
+{
+	int i, k, fd;
+	pthread_t id0[thread_cnt];
+	pthread_t id1[thread_cnt];
+	pthread_t id2[thread_cnt];
+	pthread_t id3[thread_cnt];
+	struct param p0[thread_cnt];
+	struct param p1[thread_cnt];
+	struct param p2[thread_cnt];
+	struct param p3[thread_cnt];
+	unsigned char buf[write_size];
+
+	if (tst_fill_file(fname, 1, write_size, thread_cnt + 1))
+		tst_brk(TBROK, "Failed to create tst file");
+
+	tst_res(TINFO, msg);
+
+	for (i = 0; i < thread_cnt; i++) {
+
+		p0[i].offset = i * write_size;
+		p0[i].length = write_size;
+		p0[i].cnt = i + 2;
+
+		p1[i].offset = i * write_size + write_size / 4;
+		p1[i].length = write_size;
+		p1[i].cnt = i + 2;
+
+		p2[i].offset = i * write_size + write_size / 2;
+		p2[i].length = write_size;
+		p2[i].cnt = i + 2;
+
+		p3[i].offset = i * write_size + write_size * 3 / 4;
+		p3[i].length = write_size;
+		p3[i].cnt = i + 2;
+	}
+
+	fail_flag = 0;
+	loop_flag = 1;
+
+	for (i = 0; i < thread_cnt; i++) {
+		if (f0 != NULL)
+			SAFE_PTHREAD_CREATE(id0 + i, NULL, f0, (void *)&p0[i]);
+		if (f1 != NULL)
+			SAFE_PTHREAD_CREATE(id1 + i, NULL, f1, (void *)&p1[i]);
+		if (f2 != NULL)
+			SAFE_PTHREAD_CREATE(id2 + i, NULL, f2, (void *)&p2[i]);
+		if (f3 != NULL)
+			SAFE_PTHREAD_CREATE(id3 + i, NULL, f3, (void *)&p3[i]);
+	}
+
+	sleep(1);
+	loop_flag = 0;
+
+	for (i = 0; i < thread_cnt; i++) {
+		if (f0 != NULL)
+			SAFE_PTHREAD_JOIN(id0[i], NULL);
+		if (f1 != NULL)
+			SAFE_PTHREAD_JOIN(id1[i], NULL);
+		if (f2 != NULL)
+			SAFE_PTHREAD_JOIN(id2[i], NULL);
+		if (f3 != NULL)
+			SAFE_PTHREAD_JOIN(id3[i], NULL);
+	}
+
+	fd = SAFE_OPEN(fname, O_RDONLY);
+
+	for (i = 0; i < thread_cnt * 4; i++) {
+
+		SAFE_READ(1, fd, buf, write_size/4);
+
+		for (k = 0; k < write_size/4; k++) {
+
+			if (buf[k] < 2 || buf[k] > 254) {
+
+				if (i < 3 && buf[k] == 1)
+					continue;
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					i * write_size / 4 + k, buf[k]);
+				SAFE_CLOSE(fd);
+				return;
+			}
+		}
+
+		for (k = 1; k < write_size/4; k++) {
+
+			if (buf[k] != buf[0]) {
+				tst_res(TFAIL, "Unexpected block read");
+				SAFE_CLOSE(fd);
+				return;
+			}
+		}
+	}
+
+	SAFE_CLOSE(fd);
+	if (fail_flag == 0)
+		tst_res(TPASS, "Access between threads synchronized");
+}
+
+static struct tcase {
+
+	void *(*fn0)(void *);
+	void *(*fn1)(void *);
+	void *(*fn2)(void *);
+	void *(*fn3)(void *);
+	const char *desc;
+
+} tcases[] = {
+
+	{fn_ofd_r,   fn_ofd_w,
+		NULL, NULL, "OFD read  locks vs OFD   write locks"},
+	{fn_ofd_w,   fn_posix_w,
+		NULL, NULL, "OFD write locks vs POSIX write locks"},
+	{fn_ofd_r,   fn_posix_w,
+		NULL, NULL, "OFD read  locks vs POSIX write locks"},
+	{fn_posix_r, fn_ofd_w,
+		NULL, NULL, "OFD write locks vs POSIX read  locks"},
+	{fn_ofd_w,   fn_ofd_w,
+		NULL, NULL, "OFD write locks vs OFD   write locks"},
+
+	{fn_ofd_r,   fn_ofd_w,   fn_posix_w,
+		      NULL, "OFD   r/w locks vs POSIX write locks"},
+	{fn_posix_r, fn_ofd_w,   fn_posix_w,
+		      NULL, "POSIX r/w locks vs OFD   write locks"},
+	{fn_ofd_r,   fn_posix_r, fn_ofd_w,
+		      NULL, "OFD   r/w locks vs POSIX read locks"},
+	{fn_ofd_r,   fn_posix_r, fn_posix_w,
+		      NULL, "POSIX r/w locks vs OFD   read locks"},
+
+	{fn_ofd_r,   fn_posix_r, fn_posix_w, fn_ofd_w,
+			    "OFD   r/w locks vs POSIX r/w locks"},
+};
+
+static void tests(unsigned int i)
+{
+	test_fn(tcases[i].fn0, tcases[i].fn1,
+		tcases[i].fn2, tcases[i].fn3, tcases[i].desc);
+}
+
+static struct tst_test test = {
+	.min_kver = "3.15",
+	.needs_tmpdir = 1,
+	.test = tests,
+	.tcnt = ARRAY_SIZE(tcases),
+	.setup = setup
+};
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v6] syscalls/fcntl36: add tests for OFD locks
  2017-08-24  7:48                   ` [LTP] [PATCH v6] " Xiong Zhou
@ 2017-08-29 14:27                     ` Cyril Hrubis
  2017-09-01  3:00                       ` [LTP] [PATCH v7] " Xiong Zhou
  0 siblings, 1 reply; 17+ messages in thread
From: Cyril Hrubis @ 2017-08-29 14:27 UTC (permalink / raw)
  To: ltp

Hi!
> +} tcases[] = {
> +
> +	{fn_ofd_r,   fn_ofd_w,
> +		NULL, NULL, "OFD read  locks vs OFD   write locks"},
> +	{fn_ofd_w,   fn_posix_w,
> +		NULL, NULL, "OFD write locks vs POSIX write locks"},
> +	{fn_ofd_r,   fn_posix_w,
> +		NULL, NULL, "OFD read  locks vs POSIX write locks"},
> +	{fn_posix_r, fn_ofd_w,
> +		NULL, NULL, "OFD write locks vs POSIX read  locks"},
> +	{fn_ofd_w,   fn_ofd_w,
> +		NULL, NULL, "OFD write locks vs OFD   write locks"},
> +
> +	{fn_ofd_r,   fn_ofd_w,   fn_posix_w,
> +		      NULL, "OFD   r/w locks vs POSIX write locks"},
> +	{fn_posix_r, fn_ofd_w,   fn_posix_w,
> +		      NULL, "POSIX r/w locks vs OFD   write locks"},
> +	{fn_ofd_r,   fn_posix_r, fn_ofd_w,
> +		      NULL, "OFD   r/w locks vs POSIX read locks"},
> +	{fn_ofd_r,   fn_posix_r, fn_posix_w,
> +		      NULL, "POSIX r/w locks vs OFD   read locks"},
> +
> +	{fn_ofd_r,   fn_posix_r, fn_posix_w, fn_ofd_w,
> +			    "OFD   r/w locks vs POSIX r/w locks"},

The cases that concurently run posix read vs. posix write locks
unfortunately does not work, since posix locks are process based we
cannot have more than one lock on a file region per process (threads are
counted as single process as well) and conflicting locks are converted
i.e. the last two will fail with wrongly read data in the fn_posix_r
funciton in a case that posix read and posix write locks were held
concurently (which happens for me and the test fails on my testing
machine).

We can run as many threads with ofd locks as we want, since these are
based on open file descriptors and we can add exactly one posix lock
thread to the mix as well but no more than that.

Moreover I think that we may need a pthread_barrier before we close the
file descriptors in the test functions since the posix locks are dropped
when any file descriptor asociated with the file is closed, since if we
run more than two threads and ofd thread closes the fd, the posix thread
looses it's lock and the remaining ofd thread(s) run concurently, it's
unlikely that something like this would happen, but it's possible.

> +};
> +
> +static void tests(unsigned int i)
> +{
> +	test_fn(tcases[i].fn0, tcases[i].fn1,
> +		tcases[i].fn2, tcases[i].fn3, tcases[i].desc);
> +}
> +
> +static struct tst_test test = {
> +	.min_kver = "3.15",
> +	.needs_tmpdir = 1,
> +	.test = tests,
> +	.tcnt = ARRAY_SIZE(tcases),
> +	.setup = setup
> +};
> -- 
> 1.8.3.1
> 

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v7] syscalls/fcntl36: add tests for OFD locks
  2017-08-29 14:27                     ` Cyril Hrubis
@ 2017-09-01  3:00                       ` Xiong Zhou
  2017-09-05 12:06                         ` Cyril Hrubis
  0 siblings, 1 reply; 17+ messages in thread
From: Xiong Zhou @ 2017-09-01  3:00 UTC (permalink / raw)
  To: ltp

Testing OFD locks racing with POSIX locks:

  OFD read  lock   vs   OFD   write lock
  OFD read  lock   vs   POSIX write lock
  OFD write lock   vs   POSIX write lock
  OFD write lock   vs   POSIX read  lock
  OFD write lock   vs   OFD   write lock
  OFD   r/w locks  vs   POSIX write locks
  OFD   r/w locks  vs   POSIX read  locks

Signed-off-by: Xiong Zhou <xzhou@redhat.com>
---

v7:
  Add pthread_barrier.
  Run only one kind of POSIX lock threads.
  Add fn_dummy, save efforts to look at thread funcs before firing them.

v6:
  Use param->length in thread_fn as r/w size instead of
write_size directly. Thanks to C99.

  Reset fail_flag after each sub case.

  Race up to 4 bunches of threads acquiring these 4 kinds of locks,
not just only 2 of them. (kind crazy)

v5:
  Optimize data checking.

v4:
  Add .gitignore entry.
  Count failures in reader, then check it in main loop.
  Other fix.

v3:
  Use tst_fill_file.
  Loop in test function.
  Other fix.

v2:
  Basically rewritten.
  Pass structure parameter to threads.
  Block on read lock.
  Spawn racing threads one by one.
  Use tcnt iteration.
 runtest/syscalls                          |   2 +
 testcases/kernel/syscalls/.gitignore      |   2 +
 testcases/kernel/syscalls/fcntl/Makefile  |   3 +
 testcases/kernel/syscalls/fcntl/fcntl36.c | 419 ++++++++++++++++++++++++++++++
 4 files changed, 426 insertions(+)
 create mode 100644 testcases/kernel/syscalls/fcntl/fcntl36.c

diff --git a/runtest/syscalls b/runtest/syscalls
index 407e055..f2b2cbc 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -271,6 +271,8 @@ fcntl34 fcntl34
 fcntl34_64 fcntl34_64
 fcntl35 fcntl35
 fcntl35_64 fcntl35_64
+fcntl36 fcntl36
+fcntl36_64 fcntl36_64
 
 fdatasync01 fdatasync01
 fdatasync02 fdatasync02
diff --git a/testcases/kernel/syscalls/.gitignore b/testcases/kernel/syscalls/.gitignore
index 2a7026d..32193e1 100644
--- a/testcases/kernel/syscalls/.gitignore
+++ b/testcases/kernel/syscalls/.gitignore
@@ -234,6 +234,8 @@
 /fcntl/fcntl34_64
 /fcntl/fcntl35
 /fcntl/fcntl35_64
+/fcntl/fcntl36
+/fcntl/fcntl36_64
 /fdatasync/fdatasync01
 /fdatasync/fdatasync02
 /flistxattr/flistxattr01
diff --git a/testcases/kernel/syscalls/fcntl/Makefile b/testcases/kernel/syscalls/fcntl/Makefile
index d78dd72..ae37214 100644
--- a/testcases/kernel/syscalls/fcntl/Makefile
+++ b/testcases/kernel/syscalls/fcntl/Makefile
@@ -24,6 +24,9 @@ fcntl33_64: LDLIBS+=-lrt
 fcntl34: LDLIBS += -lpthread
 fcntl34_64: LDLIBS += -lpthread
 
+fcntl36: LDLIBS += -lpthread
+fcntl36_64: LDLIBS += -lpthread
+
 include $(top_srcdir)/include/mk/testcases.mk
 include $(abs_srcdir)/../utils/newer_64.mk
 
diff --git a/testcases/kernel/syscalls/fcntl/fcntl36.c b/testcases/kernel/syscalls/fcntl/fcntl36.c
new file mode 100644
index 0000000..fad3d8a
--- /dev/null
+++ b/testcases/kernel/syscalls/fcntl/fcntl36.c
@@ -0,0 +1,419 @@
+/*
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Xiong Zhou <xzhou@redhat.com>
+ *
+ * This is testing OFD locks racing with POSIX locks:
+ *
+ *	OFD read  lock   vs   OFD   write lock
+ *	OFD read  lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX write lock
+ *	OFD write lock   vs   POSIX read  lock
+ *	OFD write lock   vs   OFD   write lock
+ *
+ *	OFD   r/w locks vs POSIX write locks
+ *	OFD   r/w locks vs POSIX read locks
+ *
+ * For example:
+ *
+ * 	Init an file with preset values.
+ *
+ * 	Threads acquire OFD READ  locks to read  a 4k section start from 0;
+ * 		checking data read back, there should not be any surprise
+ * 		values and data should be consistent in a 2k block.
+ *
+ * 	Threads acquire OFD WRITE locks to write a 4k section start from 1k,
+ * 		writing different values in different threads.
+ *
+ * 	Check file data after racing, there should not be any surprise values
+ * 		and data should be consistent in a 1k block.
+ *
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "lapi/fcntl.h"
+#include "tst_safe_pthread.h"
+#include "tst_test.h"
+
+static int thread_cnt;
+static int fail_flag = 0;
+static volatile int loop_flag = 1;
+static const int max_thread_cnt = 32;
+static const char fname[] = "tst_ofd_posix_locks";
+static const long write_size = 4096;
+static pthread_barrier_t barrier;
+
+struct param {
+	long offset;
+	long length;
+	long cnt;
+};
+
+static void setup(void)
+{
+	thread_cnt = tst_ncpus_conf() * 3;
+	if (thread_cnt > max_thread_cnt)
+		thread_cnt = max_thread_cnt;
+}
+
+/* OFD write lock writing data*/
+static void *fn_ofd_w(void *arg)
+{
+	struct param *pa = (struct param *)arg;
+	unsigned char buf[pa->length];
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, wt, pa->length);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	pthread_barrier_wait(&barrier);
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX write lock writing data*/
+static void *fn_posix_w(void *arg)
+{
+	struct param *pa = (struct param *)arg;
+	unsigned char buf[pa->length];
+	int fd = SAFE_OPEN(fname, O_RDWR);
+	long wt = pa->cnt;
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, wt, pa->length);
+
+		lck.l_type = F_WRLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_WRITE(1, fd, buf, pa->length);
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		wt++;
+		if (wt >= 255)
+			wt = pa->cnt;
+
+		sched_yield();
+	}
+
+	pthread_barrier_wait(&barrier);
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* OFD read lock reading data*/
+static void *fn_ofd_r(void *arg)
+{
+	struct param *pa = (struct param *)arg;
+	unsigned char buf[pa->length];
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+		.l_pid    = 0,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, 0, pa->length);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_flag = 1;
+				break;
+			}
+
+			int j = (i / (pa->length/4)) * pa->length/4;
+
+			if (buf[i] != buf[j]) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_flag = 1;
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_OFD_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	pthread_barrier_wait(&barrier);
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+/* POSIX read lock reading data */
+static void *fn_posix_r(void *arg)
+{
+	struct param *pa = (struct param *)arg;
+	unsigned char buf[pa->length];
+	int i;
+	int fd = SAFE_OPEN(fname, O_RDWR);
+
+	struct flock64 lck = {
+		.l_whence = SEEK_SET,
+		.l_start  = pa->offset,
+		.l_len    = pa->length,
+	};
+
+	while (loop_flag) {
+
+		memset(buf, 0, pa->length);
+
+		lck.l_type = F_RDLCK;
+		SAFE_FCNTL(fd, F_SETLKW, &lck);
+
+		/* rlock acquired */
+		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
+		SAFE_READ(1, fd, buf, pa->length);
+
+		/* Verifying data read */
+		for (i = 0; i < pa->length; i++) {
+
+			if (buf[i] < 1 || buf[i] > 254) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_flag = 1;
+				break;
+			}
+
+			int j = (i / (pa->length/4)) * pa->length/4;
+
+			if (buf[i] != buf[j]) {
+
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					pa->offset + i, buf[i]);
+				fail_flag = 1;
+				break;
+			}
+		}
+
+		lck.l_type = F_UNLCK;
+		SAFE_FCNTL(fd, F_SETLK, &lck);
+
+		sched_yield();
+	}
+
+	pthread_barrier_wait(&barrier);
+	SAFE_CLOSE(fd);
+	return NULL;
+}
+
+static void *fn_dummy(void *arg)
+{
+	arg = NULL;
+
+	pthread_barrier_wait(&barrier);
+	return arg;
+}
+
+/* Test different functions and verify data */
+static void test_fn(void *f0(void *), void *f1(void *),
+		    void *f2(void *), const char *msg)
+{
+	int i, k, fd;
+	pthread_t id0[thread_cnt];
+	pthread_t id1[thread_cnt];
+	pthread_t id2[thread_cnt];
+	struct param p0[thread_cnt];
+	struct param p1[thread_cnt];
+	struct param p2[thread_cnt];
+	unsigned char buf[write_size];
+
+	tst_res(TINFO, msg);
+
+	if (tst_fill_file(fname, 1, write_size, thread_cnt + 1))
+		tst_brk(TBROK, "Failed to create tst file");
+
+	if (pthread_barrier_init(&barrier, NULL, thread_cnt*3) != 0)
+		tst_brk(TBROK, "Failed to init pthread barrier");
+
+	for (i = 0; i < thread_cnt; i++) {
+
+		p0[i].offset = i * write_size;
+		p0[i].length = write_size;
+		p0[i].cnt = i + 2;
+
+		p1[i].offset = i * write_size + write_size / 4;
+		p1[i].length = write_size;
+		p1[i].cnt = i + 2;
+
+		p2[i].offset = i * write_size + write_size / 2;
+		p2[i].length = write_size;
+		p2[i].cnt = i + 2;
+	}
+
+	fail_flag = 0;
+	loop_flag = 1;
+
+	for (i = 0; i < thread_cnt; i++) {
+
+		SAFE_PTHREAD_CREATE(id0 + i, NULL, f0, (void *)&p0[i]);
+		SAFE_PTHREAD_CREATE(id1 + i, NULL, f1, (void *)&p1[i]);
+		SAFE_PTHREAD_CREATE(id2 + i, NULL, f2, (void *)&p2[i]);
+	}
+
+	sleep(1);
+	loop_flag = 0;
+
+	for (i = 0; i < thread_cnt; i++) {
+
+		SAFE_PTHREAD_JOIN(id0[i], NULL);
+		SAFE_PTHREAD_JOIN(id1[i], NULL);
+		SAFE_PTHREAD_JOIN(id2[i], NULL);
+	}
+
+	fd = SAFE_OPEN(fname, O_RDONLY);
+
+	for (i = 0; i < thread_cnt * 4; i++) {
+
+		SAFE_READ(1, fd, buf, write_size/4);
+
+		for (k = 0; k < write_size/4; k++) {
+
+			if (buf[k] < 2 || buf[k] > 254) {
+
+				if (i < 3 && buf[k] == 1)
+					continue;
+				tst_res(TFAIL, "Unexpected data "
+					"offset %ld value %d",
+					i * write_size / 4 + k, buf[k]);
+				SAFE_CLOSE(fd);
+				return;
+			}
+		}
+
+		for (k = 1; k < write_size/4; k++) {
+
+			if (buf[k] != buf[0]) {
+				tst_res(TFAIL, "Unexpected block read");
+				SAFE_CLOSE(fd);
+				return;
+			}
+		}
+	}
+
+	if (pthread_barrier_destroy(&barrier) != 0)
+		tst_brk(TBROK, "Failed to destroy pthread barrier");
+
+	SAFE_CLOSE(fd);
+	if (fail_flag == 0)
+		tst_res(TPASS, "Access between threads synchronized");
+}
+
+static struct tcase {
+
+	void *(*fn0)(void *);
+	void *(*fn1)(void *);
+	void *(*fn2)(void *);
+	const char *desc;
+
+} tcases[] = {
+
+	{fn_ofd_r, fn_ofd_w, fn_dummy, "OFD read lock vs OFD write lock"},
+
+	{fn_ofd_w, fn_posix_w, fn_dummy, "OFD write lock vs POSIX write lock"},
+
+	{fn_ofd_r, fn_posix_w, fn_dummy, "OFD read lock vs POSIX write lock"},
+
+	{fn_ofd_w, fn_posix_r, fn_dummy, "OFD write lock vs POSIX read lock"},
+
+	{fn_ofd_w, fn_ofd_w, fn_dummy, "OFD write lock vs OFD write lock"},
+
+	{fn_ofd_r, fn_ofd_w, fn_posix_w, "OFD r/w lock vs POSIX write lock"},
+
+	{fn_ofd_r, fn_ofd_w, fn_posix_r, "OFD r/w lock vs POSIX read lock"},
+};
+
+static void tests(unsigned int i)
+{
+	test_fn(tcases[i].fn0, tcases[i].fn1, tcases[i].fn2, tcases[i].desc);
+}
+
+static struct tst_test test = {
+	.min_kver = "3.15",
+	.needs_tmpdir = 1,
+	.test = tests,
+	.tcnt = ARRAY_SIZE(tcases),
+	.setup = setup
+};
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v7] syscalls/fcntl36: add tests for OFD locks
  2017-09-01  3:00                       ` [LTP] [PATCH v7] " Xiong Zhou
@ 2017-09-05 12:06                         ` Cyril Hrubis
  2017-09-05 13:13                           ` Xiong Zhou
  0 siblings, 1 reply; 17+ messages in thread
From: Cyril Hrubis @ 2017-09-05 12:06 UTC (permalink / raw)
  To: ltp

Hi!
Pushed with minor changes, mostly whitespace related, thanks.

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [LTP] [PATCH v7] syscalls/fcntl36: add tests for OFD locks
  2017-09-05 12:06                         ` Cyril Hrubis
@ 2017-09-05 13:13                           ` Xiong Zhou
  0 siblings, 0 replies; 17+ messages in thread
From: Xiong Zhou @ 2017-09-05 13:13 UTC (permalink / raw)
  To: ltp

On Tue, Sep 05, 2017 at 02:06:29PM +0200, Cyril Hrubis wrote:
> Hi!
> Pushed with minor changes, mostly whitespace related, thanks.

Thanks for reviewing!

> 
> -- 
> Cyril Hrubis
> chrubis@suse.cz

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2017-09-05 13:13 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-11  3:14 [LTP] [PATCH] syscalls/fcntl36: add tests for OFD locks Xiong Zhou
2017-08-14 16:02 ` Cyril Hrubis
2017-08-21 11:17   ` [LTP] [PATCH v2] " Xiong Zhou
2017-08-21 16:00     ` Cyril Hrubis
2017-08-22  8:35       ` [LTP] [PATCH v3] " Xiong Zhou
2017-08-22 14:23         ` Cyril Hrubis
2017-08-23  4:15           ` Xiong Zhou
2017-08-23  8:40             ` Cyril Hrubis
2017-08-23  9:42               ` [LTP] [PATCH v4] " Xiong Zhou
2017-08-23  9:53             ` [LTP] [PATCH v3] " Cyril Hrubis
2017-08-23 13:15               ` [LTP] [PATCH v5] " Xiong Zhou
2017-08-23 13:35                 ` Cyril Hrubis
2017-08-24  7:48                   ` [LTP] [PATCH v6] " Xiong Zhou
2017-08-29 14:27                     ` Cyril Hrubis
2017-09-01  3:00                       ` [LTP] [PATCH v7] " Xiong Zhou
2017-09-05 12:06                         ` Cyril Hrubis
2017-09-05 13:13                           ` Xiong Zhou

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.