diff mbox series

[v3] Rewrite fcnt14 test

Message ID 20240515114339.19708-1-andrea.cervesato@suse.de
State Changes Requested
Headers show
Series [v3] Rewrite fcnt14 test | expand

Commit Message

Andrea Cervesato May 15, 2024, 11:43 a.m. UTC
From: Andrea Cervesato <andrea.cervesato@suse.com>

This test has been changed in order to generate testcases at runtime.

The new algorithm sets a random starting position on file using
lseek(), it randomly generates fcntl() parameters for parent and
child and it verifies that fcntl() will raise a blocking error on
child when it's supposed to.

The runtest file has been modified so we can run test with or without
mandatory locking using the new -l flag.

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
Renamed tst_results into tc_results

 runtest/syscalls                          |    6 +-
 testcases/kernel/syscalls/fcntl/fcntl14.c | 1335 +++++----------------
 2 files changed, 276 insertions(+), 1065 deletions(-)

Comments

Cyril Hrubis Aug. 30, 2024, 1:58 p.m. UTC | #1
Hi!
> +fcntl14_01 fcntl14
> +fcntl14_01_64 fcntl14_64
> +fcntl14_02 fcntl14 -l
> +fcntl14_02_64 fcntl14_64 -l

I would slightly prefer having a .test_variants than adding a command
line options.

> +#define CHECK_EQ_SILENT_(VAL_A, SVAL_A, VAL_B, SVAL_B, TYPE, PFS, FAILED) do {\
> +	TYPE tst_tmp_a__ = VAL_A; \
> +	TYPE tst_tmp_b__ = VAL_B; \
> +	if (tst_tmp_a__ != tst_tmp_b__) { \
> +		tst_res(TFAIL, \
> +			SVAL_A " == " SVAL_B " (" PFS ")", tst_tmp_a__); \
> +			*FAILED = 1; \
> +	} \
> +} while (0)
> +
> +#define CHECK_EQ_SILENT(VAL_A, VAL_B, FAILED) \
> +		CHECK_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, long long, "%lli", FAILED)

Maybe this should go to the tst_test_macros.h?

> +struct file_conf {
> +	short type;
> +	short whence;
> +	long start;
> +	long len;
>  };
>  
> -static testcase *thiscase;
> +struct testcase {
> +	struct file_conf parent;           /* parent parameters for fcntl() */
> +	struct file_conf child;            /* child parameters for fcntl() */
> +	short blocking;                    /* blocking/non-blocking flag */
> +	long pos;                          /* starting file position */
> +};
> +
> +struct tc_results {
> +	int num_pass;
> +	int last_failed;
> +};
> +
> +static const char filepath[] = "unlocked.txt";
> +static const char filedata[] = "Here some bytes!";
>  static struct flock flock;
> -static int parent, child, status, fail = 0;
> -static int got1 = 0;
> -static int fd;
> -static int test;
> -static char tmpname[40];
> +static char *str_op_nums;
> +static char *locking_file;
> +static int op_nums = 5000;
> +static int file_mode = 0777;
> +static struct tc_results *results;
>  
> -#define FILEDATA	"ten bytes!"
> +static void dochild(struct testcase *tc, const int fd, const pid_t parent_pid)
> +{
> +	results->last_failed = 0;
>  
> -void catch1(int sig);
> -void catch_alarm(int sig);
> +	flock.l_type = tc->child.type;
> +	flock.l_whence = tc->child.whence;
> +	flock.l_start = tc->child.start;
> +	flock.l_len = tc->child.len;
> +	flock.l_pid = 0;
>  
> -char *TCID = "fcntl14";
> -int TST_TOTAL = 1;
> -int NO_NFS = 1;
> +	SAFE_FCNTL(fd, F_GETLK, &flock);
>  
> -void cleanup(void)
> -{
> -	tst_rmdir();
> -}
> +	if (tc->blocking) {
> +		tst_res(TDEBUG, "Child: expecting blocked file by parent");
>  
> -void setup(void)
> -{
> -	struct sigaction act;
> -
> -	tst_sig(FORK, DEF_HANDLER, cleanup);
> -	signal(SIGHUP, SIG_IGN);
> -	umask(0);
> -	TEST_PAUSE;
> -	tst_tmpdir();
> -	parent = getpid();
> -
> -	sprintf(tmpname, "fcntl2.%d", parent);
> -
> -	/* setup signal handler for signal from child */
> -	memset(&act, 0, sizeof(act));
> -	act.sa_handler = catch1;
> -	sigemptyset(&act.sa_mask);
> -	sigaddset(&act.sa_mask, SIGUSR1);
> -	if ((sigaction(SIGUSR1, &act, NULL)) < 0) {
> -		tst_resm(TFAIL, "SIGUSR1 signal setup failed, errno = %d",
> -			 errno);
> -		cleanup();
> -	}
> +		CHECK_EQ_SILENT(flock.l_pid, parent_pid, &results->last_failed);
> +		if (results->last_failed)
> +			return;
>  
> -	memset(&act, 0, sizeof(act));
> -	act.sa_handler = catch_alarm;
> -	sigemptyset(&act.sa_mask);
> -	sigaddset(&act.sa_mask, SIGALRM);
> -	if ((sigaction(SIGALRM, &act, NULL)) < 0) {
> -		tst_resm(TFAIL, "SIGALRM signal setup failed");
> -		cleanup();
> -	}
> -}
> +		CHECK_EQ_SILENT(flock.l_type, tc->parent.type, &results->last_failed);
> +		if (results->last_failed)
> +			return;
>  
> -void wake_parent(void)
> -{
> -	if ((kill(parent, SIGUSR1)) < 0) {
> -		tst_resm(TFAIL, "Attempt to send signal to parent " "failed");
> -		tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, errno);
> -		fail = 1;
> +		flock.l_type = tc->child.type;
> +		flock.l_whence = tc->child.whence;
> +		flock.l_start = tc->child.start;
> +		flock.l_len = tc->child.len;
> +		flock.l_pid = 0;
> +
> +		TST_EXP_FAIL_SILENT(fcntl(fd, F_SETLK, &flock), EWOULDBLOCK);
> +		if (TST_RET != -1)
> +			results->last_failed = 1;
> +	} else {
> +		tst_res(TDEBUG, "Child: expecting no blocking errors");
> +
> +		CHECK_EQ_SILENT(flock.l_type, F_UNLCK, &results->last_failed);
> +		if (results->last_failed)
> +			return;
> +
> +		CHECK_EQ_SILENT(flock.l_whence, tc->child.whence, &results->last_failed);
> +		if (results->last_failed)
> +			return;
> +
> +		CHECK_EQ_SILENT(flock.l_start, tc->child.start, &results->last_failed);
> +		if (results->last_failed)
> +			return;
> +
> +		CHECK_EQ_SILENT(flock.l_len, tc->child.len, &results->last_failed);
> +		if (results->last_failed)
> +			return;
> +
> +		CHECK_EQ_SILENT(flock.l_pid, 0, &results->last_failed);
> +		if (results->last_failed)
> +			return;

Why do we have to exit after each failed check here? I would just a
bunch of the checks here without the returns. All that we need is to
make sure the last_failed is set if child fails so that parent can
detect it.


> +	/* set the initial parent lock on the file */
> +	flock.l_type = tc->parent.type;
> +	flock.l_whence = tc->parent.whence;
> +	flock.l_start = tc->parent.start;
> +	flock.l_len = tc->parent.len;
>  	flock.l_pid = 0;

I wonder if it would be better to use struct flock in the tc directory
so that we could just assign the structure here. I.e. we would do here
just:

	struct flock flock = tc->flock;

	flock.l_pid = 0;


> +	flock.l_type = F_UNLCK;
> +	flock.l_whence = 0;
> +	flock.l_start = 0;
> +	flock.l_len = 0;
>  	flock.l_pid = 0;
>  
> +	SAFE_FCNTL(fd, F_SETLK, &flock);
> +	SAFE_CLOSE(fd);

Why bother with unlocking? Aren't the locks dropped when we close the fd
here anyways?

> +}
> +
> +static void genconf(struct file_conf *conf, const int size, const long pos)
> +{
> +	conf->type = rand() % 2 ? F_RDLCK : F_WRLCK;
> +	conf->whence = SEEK_CUR;
> +
> +	if (pos > 0 && (rand() % 2)) {
> +		conf->start = -(rand() % pos);
> +		conf->len = rand() % (size + conf->start - 1) + 1;
>  	} else {
> -		exit(0);
> +		conf->start = rand() % (size - 1);
> +		conf->len = rand() % (size - conf->start - 1) + 1;
>  	}
>  }

We have a file in which we seek at pos offset and then need a start
relative to that and lenght so that it fits into the file.

The start should be just the whole available range, which is random
offset moved back by pos. Then we need a size which is limited to
whatever is left in the file and is not relative. So if I'm not mistaken
we need what is in the else branch but we have to move the start by pos
at the end, i.e. the whole start and len generation should look like:

	conf->start = rand() % (size - 1);
	conf->len = rand() % (size - conf->start - 1) + 1;
	conf->start -= pos;

That way we generate a valid part of the file and make it realtive to
the pos at the end.

> -void run_test(int file_flag, int file_mode, int seek, int start, int end)
> +static short fcntl_overlap(
> +	struct file_conf *parent,
> +	struct file_conf *child,
> +	const long pos)
>  {
> +	long start[2];
> +	long length[2];
> +	short overlap = 0;
> +
> +	if (parent->start > child->start) {
> +		start[0] = pos + child->start;
> +		start[1] = pos + parent->start;
> +		length[0] = child->len;
> +		length[1] = parent->len;
> +	} else {
> +		start[0] = pos + parent->start;
> +		start[1] = pos + child->start;
> +		length[0] = parent->len;
> +		length[1] = child->len;
>  	}

If you add pos to both of the starts you just move the two ranges by the
same amount. So the end result is the same regardless of if you add it
or not.

> +
> +	overlap = start[0] <= start[1] && start[1] < (start[0] + length[0]);

Isn't the start[0] <= start[1] because we made it so? We set the
start[0] to the smaller of the two. So all we need to do is to check
that that start[0] + lenght[0] does not end before the start[1] which is
the second part of the equation.

And we do not use lenght[1] either. So maybe we just need:

	if (child->start < parent->start)
		overlap = parent->start < (child->start + child->len);
	else
		overlap = child->start < (parent->start + parent->len);

> +	if (overlap)
> +		tst_res(TDEBUG, "child/parent fcntl() configurations overlap");
> +
> +	return overlap;
>  }
>
Andrea Cervesato Sept. 2, 2024, 8:30 a.m. UTC | #2
Hi!

On 8/30/24 15:58, Cyril Hrubis wrote:
> Hi!
>> +fcntl14_01 fcntl14
>> +fcntl14_01_64 fcntl14_64
>> +fcntl14_02 fcntl14 -l
>> +fcntl14_02_64 fcntl14_64 -l
> I would slightly prefer having a .test_variants than adding a command
> line options.
We can make it.
>
>> +#define CHECK_EQ_SILENT_(VAL_A, SVAL_A, VAL_B, SVAL_B, TYPE, PFS, FAILED) do {\
>> +	TYPE tst_tmp_a__ = VAL_A; \
>> +	TYPE tst_tmp_b__ = VAL_B; \
>> +	if (tst_tmp_a__ != tst_tmp_b__) { \
>> +		tst_res(TFAIL, \
>> +			SVAL_A " == " SVAL_B " (" PFS ")", tst_tmp_a__); \
>> +			*FAILED = 1; \
>> +	} \
>> +} while (0)
>> +
>> +#define CHECK_EQ_SILENT(VAL_A, VAL_B, FAILED) \
>> +		CHECK_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, long long, "%lli", FAILED)
> Maybe this should go to the tst_test_macros.h?
I don't know, is a silent version of EQ_LI really needed? It's a 
particular use case, where also *FAILED is set, so I don't know...
>
>> +struct file_conf {
>> +	short type;
>> +	short whence;
>> +	long start;
>> +	long len;
>>   };
>>   
>> -static testcase *thiscase;
>> +struct testcase {
>> +	struct file_conf parent;           /* parent parameters for fcntl() */
>> +	struct file_conf child;            /* child parameters for fcntl() */
>> +	short blocking;                    /* blocking/non-blocking flag */
>> +	long pos;                          /* starting file position */
>> +};
>> +
>> +struct tc_results {
>> +	int num_pass;
>> +	int last_failed;
>> +};
>> +
>> +static const char filepath[] = "unlocked.txt";
>> +static const char filedata[] = "Here some bytes!";
>>   static struct flock flock;
>> -static int parent, child, status, fail = 0;
>> -static int got1 = 0;
>> -static int fd;
>> -static int test;
>> -static char tmpname[40];
>> +static char *str_op_nums;
>> +static char *locking_file;
>> +static int op_nums = 5000;
>> +static int file_mode = 0777;
>> +static struct tc_results *results;
>>   
>> -#define FILEDATA	"ten bytes!"
>> +static void dochild(struct testcase *tc, const int fd, const pid_t parent_pid)
>> +{
>> +	results->last_failed = 0;
>>   
>> -void catch1(int sig);
>> -void catch_alarm(int sig);
>> +	flock.l_type = tc->child.type;
>> +	flock.l_whence = tc->child.whence;
>> +	flock.l_start = tc->child.start;
>> +	flock.l_len = tc->child.len;
>> +	flock.l_pid = 0;
>>   
>> -char *TCID = "fcntl14";
>> -int TST_TOTAL = 1;
>> -int NO_NFS = 1;
>> +	SAFE_FCNTL(fd, F_GETLK, &flock);
>>   
>> -void cleanup(void)
>> -{
>> -	tst_rmdir();
>> -}
>> +	if (tc->blocking) {
>> +		tst_res(TDEBUG, "Child: expecting blocked file by parent");
>>   
>> -void setup(void)
>> -{
>> -	struct sigaction act;
>> -
>> -	tst_sig(FORK, DEF_HANDLER, cleanup);
>> -	signal(SIGHUP, SIG_IGN);
>> -	umask(0);
>> -	TEST_PAUSE;
>> -	tst_tmpdir();
>> -	parent = getpid();
>> -
>> -	sprintf(tmpname, "fcntl2.%d", parent);
>> -
>> -	/* setup signal handler for signal from child */
>> -	memset(&act, 0, sizeof(act));
>> -	act.sa_handler = catch1;
>> -	sigemptyset(&act.sa_mask);
>> -	sigaddset(&act.sa_mask, SIGUSR1);
>> -	if ((sigaction(SIGUSR1, &act, NULL)) < 0) {
>> -		tst_resm(TFAIL, "SIGUSR1 signal setup failed, errno = %d",
>> -			 errno);
>> -		cleanup();
>> -	}
>> +		CHECK_EQ_SILENT(flock.l_pid, parent_pid, &results->last_failed);
>> +		if (results->last_failed)
>> +			return;
>>   
>> -	memset(&act, 0, sizeof(act));
>> -	act.sa_handler = catch_alarm;
>> -	sigemptyset(&act.sa_mask);
>> -	sigaddset(&act.sa_mask, SIGALRM);
>> -	if ((sigaction(SIGALRM, &act, NULL)) < 0) {
>> -		tst_resm(TFAIL, "SIGALRM signal setup failed");
>> -		cleanup();
>> -	}
>> -}
>> +		CHECK_EQ_SILENT(flock.l_type, tc->parent.type, &results->last_failed);
>> +		if (results->last_failed)
>> +			return;
>>   
>> -void wake_parent(void)
>> -{
>> -	if ((kill(parent, SIGUSR1)) < 0) {
>> -		tst_resm(TFAIL, "Attempt to send signal to parent " "failed");
>> -		tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, errno);
>> -		fail = 1;
>> +		flock.l_type = tc->child.type;
>> +		flock.l_whence = tc->child.whence;
>> +		flock.l_start = tc->child.start;
>> +		flock.l_len = tc->child.len;
>> +		flock.l_pid = 0;
>> +
>> +		TST_EXP_FAIL_SILENT(fcntl(fd, F_SETLK, &flock), EWOULDBLOCK);
>> +		if (TST_RET != -1)
>> +			results->last_failed = 1;
>> +	} else {
>> +		tst_res(TDEBUG, "Child: expecting no blocking errors");
>> +
>> +		CHECK_EQ_SILENT(flock.l_type, F_UNLCK, &results->last_failed);
>> +		if (results->last_failed)
>> +			return;
>> +
>> +		CHECK_EQ_SILENT(flock.l_whence, tc->child.whence, &results->last_failed);
>> +		if (results->last_failed)
>> +			return;
>> +
>> +		CHECK_EQ_SILENT(flock.l_start, tc->child.start, &results->last_failed);
>> +		if (results->last_failed)
>> +			return;
>> +
>> +		CHECK_EQ_SILENT(flock.l_len, tc->child.len, &results->last_failed);
>> +		if (results->last_failed)
>> +			return;
>> +
>> +		CHECK_EQ_SILENT(flock.l_pid, 0, &results->last_failed);
>> +		if (results->last_failed)
>> +			return;
> Why do we have to exit after each failed check here? I would just a
> bunch of the checks here without the returns. All that we need is to
> make sure the last_failed is set if child fails so that parent can
> detect it.
>
>
>> +	/* set the initial parent lock on the file */
>> +	flock.l_type = tc->parent.type;
>> +	flock.l_whence = tc->parent.whence;
>> +	flock.l_start = tc->parent.start;
>> +	flock.l_len = tc->parent.len;
>>   	flock.l_pid = 0;
> I wonder if it would be better to use struct flock in the tc directory
> so that we could just assign the structure here. I.e. we would do here
> just:
>
> 	struct flock flock = tc->flock;
>
> 	flock.l_pid = 0;
>
>
>> +	flock.l_type = F_UNLCK;
>> +	flock.l_whence = 0;
>> +	flock.l_start = 0;
>> +	flock.l_len = 0;
>>   	flock.l_pid = 0;
>>   
>> +	SAFE_FCNTL(fd, F_SETLK, &flock);
>> +	SAFE_CLOSE(fd);
> Why bother with unlocking? Aren't the locks dropped when we close the fd
> here anyways?
>
>> +}
>> +
>> +static void genconf(struct file_conf *conf, const int size, const long pos)
>> +{
>> +	conf->type = rand() % 2 ? F_RDLCK : F_WRLCK;
>> +	conf->whence = SEEK_CUR;
>> +
>> +	if (pos > 0 && (rand() % 2)) {
>> +		conf->start = -(rand() % pos);
>> +		conf->len = rand() % (size + conf->start - 1) + 1;
>>   	} else {
>> -		exit(0);
>> +		conf->start = rand() % (size - 1);
>> +		conf->len = rand() % (size - conf->start - 1) + 1;
>>   	}
>>   }
> We have a file in which we seek at pos offset and then need a start
> relative to that and lenght so that it fits into the file.
>
> The start should be just the whole available range, which is random
> offset moved back by pos. Then we need a size which is limited to
> whatever is left in the file and is not relative. So if I'm not mistaken
> we need what is in the else branch but we have to move the start by pos
> at the end, i.e. the whole start and len generation should look like:
>
> 	conf->start = rand() % (size - 1);
> 	conf->len = rand() % (size - conf->start - 1) + 1;
> 	conf->start -= pos;
>
> That way we generate a valid part of the file and make it realtive to
> the pos at the end.
>
>> -void run_test(int file_flag, int file_mode, int seek, int start, int end)
>> +static short fcntl_overlap(
>> +	struct file_conf *parent,
>> +	struct file_conf *child,
>> +	const long pos)
>>   {
>> +	long start[2];
>> +	long length[2];
>> +	short overlap = 0;
>> +
>> +	if (parent->start > child->start) {
>> +		start[0] = pos + child->start;
>> +		start[1] = pos + parent->start;
>> +		length[0] = child->len;
>> +		length[1] = parent->len;
>> +	} else {
>> +		start[0] = pos + parent->start;
>> +		start[1] = pos + child->start;
>> +		length[0] = parent->len;
>> +		length[1] = child->len;
>>   	}
> If you add pos to both of the starts you just move the two ranges by the
> same amount. So the end result is the same regardless of if you add it
> or not.
>
>> +
>> +	overlap = start[0] <= start[1] && start[1] < (start[0] + length[0]);
> Isn't the start[0] <= start[1] because we made it so? We set the
> start[0] to the smaller of the two. So all we need to do is to check
> that that start[0] + lenght[0] does not end before the start[1] which is
> the second part of the equation.
>
> And we do not use lenght[1] either. So maybe we just need:
>
> 	if (child->start < parent->start)
> 		overlap = parent->start < (child->start + child->len);
> 	else
> 		overlap = child->start < (parent->start + parent->len);
>
>> +	if (overlap)
>> +		tst_res(TDEBUG, "child/parent fcntl() configurations overlap");
>> +
>> +	return overlap;
>>   }
>>   
Andrea
Cyril Hrubis Sept. 2, 2024, 1:36 p.m. UTC | #3
Hi!
> I don't know, is a silent version of EQ_LI really needed? It's a 
> particular use case, where also *FAILED is set, so I don't know...

Contrary I would say that this may be a common pattern when we want to
avoid spamming the test output:

	unsigned int failcnt = 0;

	TST_EXP_EQ_LI_SILENT(..., &failcnt);
	TST_EXP_EQ_LI_SILENT(..., &failcnt);
	TST_EXP_EQ_LI_SILENT(..., &failcnt);
	TST_EXP_EQ_LI_SILENT(..., &failcnt);

	if (!failcnt)
		tst_res(TPASS, "All values were fine");


Anyways, we can as well pull the code to the library when we get a
second user.
diff mbox series

Patch

diff --git a/runtest/syscalls b/runtest/syscalls
index cf06ee563..52977634d 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -303,8 +303,10 @@  fcntl12 fcntl12
 fcntl12_64 fcntl12_64
 fcntl13 fcntl13
 fcntl13_64 fcntl13_64
-fcntl14 fcntl14
-fcntl14_64 fcntl14_64
+fcntl14_01 fcntl14
+fcntl14_01_64 fcntl14_64
+fcntl14_02 fcntl14 -l
+fcntl14_02_64 fcntl14_64 -l
 fcntl15 fcntl15
 fcntl15_64 fcntl15_64
 fcntl16 fcntl16
diff --git a/testcases/kernel/syscalls/fcntl/fcntl14.c b/testcases/kernel/syscalls/fcntl/fcntl14.c
index d38220951..66609b7cb 100644
--- a/testcases/kernel/syscalls/fcntl/fcntl14.c
+++ b/testcases/kernel/syscalls/fcntl/fcntl14.c
@@ -1,1115 +1,324 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
- *
- *   Copyright (c) International Business Machines  Corp., 2001
- *
- *   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 will be useful,
- *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
- *   the GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program;  if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Copyright (c) International Business Machines  Corp., 2001
+ *     07/2001 Ported by Wayne Boyer
+ * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com
  */
 
-/*
- * NAME
- *	fcntl14.c
+/*\
+ * [Description]
  *
- * DESCRIPTION
- *	File locking test cases for fcntl. In Linux, S_ENFMT is not implemented
- *	in the kernel. However all standard Unix kernels define S_ENFMT as
- *	S_ISGID. So this test defines S_ENFMT as S_ISGID.
- *
- * ALGORITHM
- *	Various test cases are used to lock a file opened without mandatory
- *	locking, with mandatory locking and mandatory locking with NOBLOCK
- *
- * USAGE
- *	fcntl14
- *
- * HISTORY
- *	07/2001 Ported by Wayne Boyer
- *
- * RESTRICTIONS
- *	None
+ * This test is checking fcntl() syscall locking mechanism between two
+ * processes.
+ * The test sets a random starting position on file using lseek(), it randomly
+ * generates fcntl() parameters for parent and child and it verifies that
+ * fcntl() will raise a blocking error on child when it's supposed to.
  */
-#define _GNU_SOURCE 1
+
 #include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <inttypes.h>
-#include "test.h"
-#include "safe_macros.h"
-
-#define SKIP 0x0c00
-#if SKIP == F_RDLCK || SKIP== F_WRLCK
-#error invalid value for SKIP, must be distinct from F_RDLCK and F_WRLCK
-#endif
+#include <stdlib.h>
+#include "tst_test.h"
+
 #ifndef S_ENFMT
-#define S_ENFMT S_ISGID
+# define S_ENFMT S_ISGID
 #endif
 
-/* NOBLOCK - immediate success */
-#define NOBLOCK 2
-
-/* WILLBLOCK - blocks, then succeeds (parent must unlock records) */
-#define WILLBLOCK 3
-
-#define TIME_OUT 60
-
-typedef struct {
-	short a_type;
-	short a_whence;
-	long a_start;
-	long a_len;
-	short b_type;		/* SKIP means suppress fcntl call */
-	short b_whence;
-	long b_start;
-	long b_len;
-	short c_type;
-	int c_whence;
-	long c_start;
-	long c_len;
-	short c_flag;
-} testcase;
-
-static testcase testcases[] = {
-	/* Test cases: entire boundary */
-	/* #1 Parent making a write lock on entire file */
-	{F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock on entire file */
-	 F_RDLCK, 0, 0L, 0L, WILLBLOCK},
-
-	/* #2 Parent making a write lock on entire file */
-	{F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock on entire file */
-	 F_WRLCK, 0, 0L, 0L, WILLBLOCK},
-
-	/* #3 Parent making a read lock on entire file */
-	{F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock on entire file */
-	 F_RDLCK, 0, 0L, 0L, NOBLOCK},
-
-	/* #4 Parent making a read lock on entire file */
-	{F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock on entire file */
-	 F_WRLCK, 0, 0L, 0L, WILLBLOCK},
-
-	/* Test case: start boundary */
-	/* #5 Parent making a write lock on entire file */
-	{F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /*
-	  * Child attempting a read lock from beginning of
-	  * file for 5 bytes
-	  */
-	 F_RDLCK, 0, 0L, 5L, WILLBLOCK},
-
-	/* #6 Parent making a write lock on entire file */
-	{F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /*
-	  * Child attempting a write lock from beginning of
-	  * file for 5 bytes
-	  */
-	 F_WRLCK, 0, 0L, 5L, WILLBLOCK},
-
-	/* #7 Parent making a read lock on entire file */
-	{F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /*
-	  * Child attempting a read lock from beginning of
-	  * file for 5 bytes
-	  */
-	 F_RDLCK, 0, 0L, 5L, NOBLOCK},
-
-	/* #8 Parent making a read lock on entire file */
-	{F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /*
-	  * Child attempting a write lock from beginning of
-	  * file for 5 bytes
-	  */
-	 F_WRLCK, 0, 0L, 5L, WILLBLOCK},
-
-	/* Test cases: end boundary */
-	/* #9 Parent making a write lock on entire file */
-	{F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from byte 7 to end of file */
-	 F_RDLCK, 0, 7L, 0L, WILLBLOCK},
-
-	/* #10 Parent making a write lock on entire file */
-	{F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 7 to end of file */
-	 F_WRLCK, 0, 7L, 0L, WILLBLOCK},
-
-	/* #11 Parent making a read lock on entire file */
-	{F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from byte 7 to end of file */
-	 F_RDLCK, 0, 7L, 0L, NOBLOCK},
-
-	/* #12 Parent making a read lock on entire file */
-	{F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 7 to end of file */
-	 F_WRLCK, 0, 7L, 0L, WILLBLOCK},
-
-	/* Test cases: entire boundary ( less than entire file) */
-	/*
-	 * #13 Parent making a write lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /*
-	  * Child attempting a read lock from beginning of
-	  * file for 5 bytes
-	  */
-	 F_RDLCK, 0, 0L, 5L, WILLBLOCK},
-
-	/*
-	 * #14 Parent making a write lock from beginning of file
-	 * for 5 bytes
-	 */
-	{F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /*
-	  * Child attempting a write lock from beginning of
-	  * file for 5 bytes
-	  */
-	 F_WRLCK, 0, 0L, 5L, WILLBLOCK},
-
-	/*
-	 * #15 Parent making a read lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /*
-	  * Child attempting a read lock from beginning of
-	  * file for 5 bytes
-	  */
-	 F_RDLCK, 0, 0L, 5L, NOBLOCK},
-
-	/*
-	 * #16 Parent making a read lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /*
-	  * Child attempting a write lock from beginning
-	  * of file for 5 bytes
-	  */
-	 F_WRLCK, 0, 0L, 5L, WILLBLOCK},
-
-	/* Test cases: inside boundary */
-	/*
-	 * #17 Parent making a write lock from beginning
-	 * of file for 5 bytes
-	 */
-	{F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from byte 2 to byte 4 */
-	 F_RDLCK, 0, 1L, 3L, WILLBLOCK},
-
-	/*
-	 * #18 Parent making a write lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 2 to byte 4 */
-	 F_WRLCK, 0, 1L, 3L, WILLBLOCK},
-
-	/*
-	 * #19 Parent making a read lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from byte 2 to byte 4 */
-	 F_RDLCK, 0, 1L, 3L, NOBLOCK},
-
-	/*
-	 * #20 Parent making a read lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 2 to byte 4 */
-	 F_WRLCK, 0, 1L, 3L, WILLBLOCK},
-
-	/* Test cases: cross boundary (inside to after) */
-	/*
-	 * #21 Parent making a write lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from byte 3 to byte 7 */
-	 F_RDLCK, 0, 2L, 5L, WILLBLOCK},
-
-	/*
-	 * #22 Parent making a write lock from beginning
-	 * of file for 5 bytes
-	 */
-	{F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 3 to byte 7 */
-	 F_WRLCK, 0, 2L, 5L, WILLBLOCK},
-
-	/*
-	 * #23 Parent making a read lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from byte 3 to byte 7 */
-	 F_RDLCK, 0, 2L, 5L, NOBLOCK},
-
-	/*
-	 * #24 Parent making a read lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 3 to byte 7 */
-	 F_WRLCK, 0, 2L, 5L, WILLBLOCK},
-
-	/* Test cases: outside boundary (after) */
-
-	/*
-	 * #25 Parent making a write lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /*  Child attempting a read lock from byte 7 to end of file */
-	 F_RDLCK, 0, 6L, 0L, NOBLOCK},
-
-	/*
-	 * #26 Parent making a write lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 7 to end of file */
-	 F_WRLCK, 0, 6L, 0L, NOBLOCK},
-
-	/*
-	 * #27 Parent making a read lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from byte 7 to end of file */
-	 F_RDLCK, 0, 6L, 0L, NOBLOCK},
-
-	/*
-	 * #28 Parent making a read lock from beginning of
-	 * file for 5 bytes
-	 */
-	{F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 7 to end of file */
-	 F_WRLCK, 0, 6L, 0L, NOBLOCK},
-
-	/* Test cases: outside boundary (before) */
-
-	/* #29 Parent making a write lock from byte 3 to byte 7 */
-	{F_WRLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from beginning of file to byte 2 */
-	 F_RDLCK, 0, 0L, 2L, NOBLOCK},
-
-	/* #30 Parent making a write lock from byte 3 to byte 7 */
-	{F_WRLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from beginning of file to byte 2 */
-	 F_WRLCK, 0, 0L, 2L, NOBLOCK},
-
-	/* #31 Parent making a write lock from byte 3 to byte 7 */
-	{F_RDLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from beginning of file to byte 2 */
-	 F_RDLCK, 0, 0L, 2L, NOBLOCK},
-
-	/* #32 Parent making a write lock from byte 3 to byte 7 */
-	{F_RDLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from beginning of file to byte 2 */
-	 F_WRLCK, 0, 0L, 2L, NOBLOCK},
-
-	/* Test cases: cross boundary (before to inside) */
-	/* #33 Parent making a write lock from byte 5 to end of file */
-	{F_WRLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from byte 3 to byte 7 */
-	 F_RDLCK, 0, 2L, 5L, WILLBLOCK},
-
-	/* #34 Parent making a write lock from byte 5 to end of file */
-	{F_WRLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 3 to byte 7 */
-	 F_WRLCK, 0, 2L, 5L, WILLBLOCK},
-
-	/* #35 Parent making a read lock from byte 5 to end of file */
-	{F_RDLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a read lock from byte 3 to byte 7 */
-	 F_RDLCK, 0, 2L, 5L, NOBLOCK},
-
-	/* #36 Parent making a read lock from byte 5 to end of file */
-	{F_RDLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting a write lock from byte 3 to byte 7 */
-	 F_WRLCK, 0, 2L, 5L, WILLBLOCK},
-
-	/* Start of negative L_start and L_len test cases */
-	/*
-	 * #37 Parent making write lock from byte 2 to byte 3
-	 * with L_start = -3
-	 */
-	{F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 1 */
-	 F_WRLCK, 0, 1L, 1L, NOBLOCK},
-
-	/*
-	 * #38 Parent making write lock from byte 2 to byte 3
-	 * with L_start = -3
-	 */
-	{F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 4 */
-	 F_WRLCK, 0, 4L, 1L, NOBLOCK},
-
-	/*
-	 * #39 Parent making write lock from byte 2 to byte 3
-	 * with L_start = -3
-	 */
-	{F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 2 */
-	 F_WRLCK, 0, 2L, 1L, WILLBLOCK},
-
-	/*
-	 * #40 Parent making write lock from byte 2 to byte 3
-	 * with L_start = -3
-	 */
-	{F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 3 */
-	 F_WRLCK, 0, 3L, 1L, WILLBLOCK},
-
-	/*
-	 * #41 Parent making write lock from byte 2 to byte 6
-	 * with L_start = -3
-	 */
-	{F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 1 */
-	 F_WRLCK, 0, 1L, 1L, NOBLOCK},
-
-	/*
-	 * #42 Parent making write lock from byte 2 to byte 6
-	 * with L_start = -3
-	 */
-	{F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 7 */
-	 F_WRLCK, 0, 1L, 1L, NOBLOCK},
-
-	/*
-	 * #43 Parent making write lock from byte 2 to byte 6
-	 * with L_start = -3
-	 */
-	{F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 2 */
-	 F_WRLCK, 0, 2L, 1L, WILLBLOCK},
-
-	/*
-	 * #44 Parent making write lock from byte 2 to byte 6
-	 * with L_start = -3
-	 */
-	{F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 5 */
-	 F_WRLCK, 0, 5L, 1L, WILLBLOCK},
-
-	/*
-	 * #45 Parent making write lock from byte 2 to byte 6
-	 * with L_start = -3
-	 */
-	{F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 6 */
-	 F_WRLCK, 0, 6L, 1L, WILLBLOCK},
-
-	/*
-	 * #46 Parent making write lock from byte 2 to byte 3 with
-	 * L_start = -2 and L_len = -2
-	 */
-	{F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 1 */
-	 F_WRLCK, 0, 1L, 1L, NOBLOCK},
-
-	/*
-	 * #47 Parent making write lock from byte 2 to byte 3 with
-	 * L_start = -2 and L_len = -2
-	 */
-	{F_WRLCK, 1, -2L, -2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 4 */
-	 F_WRLCK, 0, 4L, 1L, NOBLOCK},
-
-	/*
-	 * #48 Parent making write lock from byte 2 to byte 3 with
-	 * L_start = -2 and L_len = -2
-	 */
-	{F_WRLCK, 1, -2L, -2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 2 */
-	 F_WRLCK, 0, 2L, 1L, WILLBLOCK},
-
-	/*
-	 * #49 Parent making write lock from byte 2 to byte 3 with
-	 * L_start = -2 and L_len = -2
-	 */
-	{F_WRLCK, 1, -2L, -2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 3 */
-	 F_WRLCK, 0, 3L, 1L, WILLBLOCK},
-
-	/*
-	 * #50 Parent making write lock from byte 6 to byte 7 with
-	 * L_start = 2 and L_len = -2
-	 */
-	{F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 5 */
-	 F_WRLCK, 0, 5L, 1L, NOBLOCK},
-
-	/*
-	 * #51 Parent making write lock from byte 6 to byte 7 with
-	 * L_start = 2 and L_len = -2
-	 */
-	{F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 8 */
-	 F_WRLCK, 0, 8L, 1L, NOBLOCK},
-
-	/*
-	 * #52 Parent making write lock from byte 6 to byte 7 with
-	 * L_start = 2 and L_len = -2
-	 */
-	{F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 6 */
-	 F_WRLCK, 0, 6L, 1L, WILLBLOCK},
-
-	/*
-	 * #53 Parent making write lock from byte 6 to byte 7 with
-	 * L_start = 2 and L_len = -2
-	 */
-	{F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 7 */
-	 F_WRLCK, 0, 7L, 1L, WILLBLOCK},
-
-	/*
-	 * #54 Parent making write lock from byte 3 to byte 7 with
-	 * L_start = 2 and L_len = -5
-	 */
-	{F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 2 */
-	 F_WRLCK, 0, 2L, 1L, NOBLOCK},
-
-	/*
-	 * #55 Parent making write lock from byte 3 to byte 7 with
-	 * L_start = 2 and L_len = -5
-	 */
-	{F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 8 */
-	 F_WRLCK, 0, 8L, 1L, NOBLOCK},
-
-	/*
-	 * #56 Parent making write lock from byte 3 to byte 7 with
-	 * L_start = 2 and L_len = -5
-	 */
-	{F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 3 */
-	 F_WRLCK, 0, 3L, 1L, WILLBLOCK},
-
-	/*
-	 * #57 Parent making write lock from byte 3 to byte 7 with
-	 * L_start = 2 and L_len = -5
-	 */
-	{F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 5 */
-	 F_WRLCK, 0, 5L, 1L, WILLBLOCK},
-
-	/*
-	 * #58 Parent making write lock from byte 3 to byte 7 with
-	 * L_start = 2 and L_len = -5
-	 */
-	{F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 7 */
-	 F_WRLCK, 0, 7L, 1L, WILLBLOCK},
-
-	/* Test case for block 4 */
-	/* #59 Parent making write lock on entire file */
-	{F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L,
-	 /* Child attempting write lock on byte 15 to end of file */
-	 F_WRLCK, 0, 15L, 0L, WILLBLOCK},
+#define CHECK_EQ_SILENT_(VAL_A, SVAL_A, VAL_B, SVAL_B, TYPE, PFS, FAILED) do {\
+	TYPE tst_tmp_a__ = VAL_A; \
+	TYPE tst_tmp_b__ = VAL_B; \
+	if (tst_tmp_a__ != tst_tmp_b__) { \
+		tst_res(TFAIL, \
+			SVAL_A " == " SVAL_B " (" PFS ")", tst_tmp_a__); \
+			*FAILED = 1; \
+	} \
+} while (0)
+
+#define CHECK_EQ_SILENT(VAL_A, VAL_B, FAILED) \
+		CHECK_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, long long, "%lli", FAILED)
+
+struct file_conf {
+	short type;
+	short whence;
+	long start;
+	long len;
 };
 
-static testcase *thiscase;
+struct testcase {
+	struct file_conf parent;           /* parent parameters for fcntl() */
+	struct file_conf child;            /* child parameters for fcntl() */
+	short blocking;                    /* blocking/non-blocking flag */
+	long pos;                          /* starting file position */
+};
+
+struct tc_results {
+	int num_pass;
+	int last_failed;
+};
+
+static const char filepath[] = "unlocked.txt";
+static const char filedata[] = "Here some bytes!";
 static struct flock flock;
-static int parent, child, status, fail = 0;
-static int got1 = 0;
-static int fd;
-static int test;
-static char tmpname[40];
+static char *str_op_nums;
+static char *locking_file;
+static int op_nums = 5000;
+static int file_mode = 0777;
+static struct tc_results *results;
 
-#define FILEDATA	"ten bytes!"
+static void dochild(struct testcase *tc, const int fd, const pid_t parent_pid)
+{
+	results->last_failed = 0;
 
-void catch1(int sig);
-void catch_alarm(int sig);
+	flock.l_type = tc->child.type;
+	flock.l_whence = tc->child.whence;
+	flock.l_start = tc->child.start;
+	flock.l_len = tc->child.len;
+	flock.l_pid = 0;
 
-char *TCID = "fcntl14";
-int TST_TOTAL = 1;
-int NO_NFS = 1;
+	SAFE_FCNTL(fd, F_GETLK, &flock);
 
-void cleanup(void)
-{
-	tst_rmdir();
-}
+	if (tc->blocking) {
+		tst_res(TDEBUG, "Child: expecting blocked file by parent");
 
-void setup(void)
-{
-	struct sigaction act;
-
-	tst_sig(FORK, DEF_HANDLER, cleanup);
-	signal(SIGHUP, SIG_IGN);
-	umask(0);
-	TEST_PAUSE;
-	tst_tmpdir();
-	parent = getpid();
-
-	sprintf(tmpname, "fcntl2.%d", parent);
-
-	/* setup signal handler for signal from child */
-	memset(&act, 0, sizeof(act));
-	act.sa_handler = catch1;
-	sigemptyset(&act.sa_mask);
-	sigaddset(&act.sa_mask, SIGUSR1);
-	if ((sigaction(SIGUSR1, &act, NULL)) < 0) {
-		tst_resm(TFAIL, "SIGUSR1 signal setup failed, errno = %d",
-			 errno);
-		cleanup();
-	}
+		CHECK_EQ_SILENT(flock.l_pid, parent_pid, &results->last_failed);
+		if (results->last_failed)
+			return;
 
-	memset(&act, 0, sizeof(act));
-	act.sa_handler = catch_alarm;
-	sigemptyset(&act.sa_mask);
-	sigaddset(&act.sa_mask, SIGALRM);
-	if ((sigaction(SIGALRM, &act, NULL)) < 0) {
-		tst_resm(TFAIL, "SIGALRM signal setup failed");
-		cleanup();
-	}
-}
+		CHECK_EQ_SILENT(flock.l_type, tc->parent.type, &results->last_failed);
+		if (results->last_failed)
+			return;
 
-void wake_parent(void)
-{
-	if ((kill(parent, SIGUSR1)) < 0) {
-		tst_resm(TFAIL, "Attempt to send signal to parent " "failed");
-		tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, errno);
-		fail = 1;
+		flock.l_type = tc->child.type;
+		flock.l_whence = tc->child.whence;
+		flock.l_start = tc->child.start;
+		flock.l_len = tc->child.len;
+		flock.l_pid = 0;
+
+		TST_EXP_FAIL_SILENT(fcntl(fd, F_SETLK, &flock), EWOULDBLOCK);
+		if (TST_RET != -1)
+			results->last_failed = 1;
+	} else {
+		tst_res(TDEBUG, "Child: expecting no blocking errors");
+
+		CHECK_EQ_SILENT(flock.l_type, F_UNLCK, &results->last_failed);
+		if (results->last_failed)
+			return;
+
+		CHECK_EQ_SILENT(flock.l_whence, tc->child.whence, &results->last_failed);
+		if (results->last_failed)
+			return;
+
+		CHECK_EQ_SILENT(flock.l_start, tc->child.start, &results->last_failed);
+		if (results->last_failed)
+			return;
+
+		CHECK_EQ_SILENT(flock.l_len, tc->child.len, &results->last_failed);
+		if (results->last_failed)
+			return;
+
+		CHECK_EQ_SILENT(flock.l_pid, 0, &results->last_failed);
+		if (results->last_failed)
+			return;
+
+		TST_EXP_PASS_SILENT(fcntl(fd, F_SETLK, &flock));
+		if (TST_RET == -1)
+			results->last_failed = 1;
 	}
 }
 
-void do_usleep_child(void)
+static void run_testcase(struct testcase *tc, const int file_mode)
 {
-	usleep(100000);		/* XXX how long is long enough? */
-	wake_parent();
-	exit(0);
-}
+	pid_t parent_pid;
+	pid_t child_pid;
+	int fd;
 
-void dochild(void)
-{
-	int rc;
-	pid_t pid;
+	tst_res(TDEBUG, "Parent: locking file");
 
-	flock.l_type = thiscase->c_type;
-	flock.l_whence = thiscase->c_whence;
-	flock.l_start = thiscase->c_start;
-	flock.l_len = thiscase->c_len;
+	/* open file and move cursor according with the test */
+	fd = SAFE_OPEN(filepath, O_RDWR, file_mode);
+	SAFE_LSEEK(fd, tc->pos, 0);
+
+	/* set the initial parent lock on the file */
+	flock.l_type = tc->parent.type;
+	flock.l_whence = tc->parent.whence;
+	flock.l_start = tc->parent.start;
+	flock.l_len = tc->parent.len;
 	flock.l_pid = 0;
-	fail = 0;
-
-	/*
-	 * Check to see if child lock will succeed. If it will, FLOCK
-	 * structure will return with l_type changed to F_UNLCK. If it will
-	 * not, the parent pid will be returned in l_pid and the type of
-	 * lock that will block it in l_type.
-	 */
-	if ((rc = fcntl(fd, F_GETLK, &flock)) < 0) {
-		tst_resm(TFAIL, "Attempt to check lock status failed");
-		tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, errno);
-		fail = 1;
-	} else {
 
-		if ((thiscase->c_flag) == NOBLOCK) {
-			if (flock.l_type != F_UNLCK) {
-				tst_resm(TFAIL,
-					 "Test case %d, GETLK: type = %d, "
-					 "%d was expected", test + 1,
-					 flock.l_type, F_UNLCK);
-				fail = 1;
-			}
-
-			if (flock.l_whence != thiscase->c_whence) {
-				tst_resm(TFAIL,
-					 "Test case %d, GETLK: whence = %d, "
-					 "should have remained %d", test + 1,
-					 flock.l_whence, thiscase->c_whence);
-				fail = 1;
-			}
-
-			if (flock.l_start != thiscase->c_start) {
-				tst_resm(TFAIL,
-					 "Test case %d, GETLK: start = %" PRId64
-					 ", " "should have remained %" PRId64,
-					 test + 1, (int64_t) flock.l_start,
-					 (int64_t) thiscase->c_start);
-				fail = 1;
-			}
-
-			if (flock.l_len != thiscase->c_len) {
-				tst_resm(TFAIL,
-					 "Test case %d, GETLK: len = %" PRId64
-					 ", " "should have remained %" PRId64,
-					 test + 1, (int64_t) flock.l_len,
-					 (int64_t) thiscase->c_len);
-				fail = 1;
-			}
-
-			if (flock.l_pid != 0) {
-				tst_resm(TFAIL,
-					 "Test case %d, GETLK: pid = %d, "
-					 "should have remained 0", test + 1,
-					 flock.l_pid);
-				fail = 1;
-			}
-		} else {
-			if (flock.l_pid != parent) {
-				tst_resm(TFAIL,
-					 "Test case %d, GETLK: pid = %d, "
-					 "should be parent's id of %d",
-					 test + 1, flock.l_pid, parent);
-				fail = 1;
-			}
-
-			if (flock.l_type != thiscase->a_type) {
-				tst_resm(TFAIL,
-					 "Test case %d, GETLK: type = %d, "
-					 "should be parent's first lock type of %d",
-					 test + 1, flock.l_type,
-					 thiscase->a_type);
-				fail = 1;
-			}
-		}
+	SAFE_FCNTL(fd, F_SETLK, &flock);
+
+	/* set the child lock on the file */
+	parent_pid = getpid();
+	child_pid = SAFE_FORK();
+
+	if (!child_pid) {
+		dochild(tc, fd, parent_pid);
+		exit(0);
 	}
 
-	/*
-	 * now try to set the lock, nonblocking
-	 * This will succeed for NOBLOCK,
-	 * fail for WILLBLOCK
-	 */
-	flock.l_type = thiscase->c_type;
-	flock.l_whence = thiscase->c_whence;
-	flock.l_start = thiscase->c_start;
-	flock.l_len = thiscase->c_len;
+	tst_reap_children();
+
+	flock.l_type = F_UNLCK;
+	flock.l_whence = 0;
+	flock.l_start = 0;
+	flock.l_len = 0;
 	flock.l_pid = 0;
 
-	if ((rc = fcntl(fd, F_SETLK, &flock)) < 0) {
-		if ((thiscase->c_flag) == NOBLOCK) {
-			tst_resm(TFAIL, "Attempt to set child NONBLOCKING "
-				 "lock failed");
-			tst_resm(TFAIL, "Test case %d, errno = %d",
-				 test + 1, errno);
-			fail = 1;
-		}
-	}
+	SAFE_FCNTL(fd, F_SETLK, &flock);
+	SAFE_CLOSE(fd);
+}
 
-	if ((thiscase->c_flag) == WILLBLOCK) {
-		if (rc != -1 || (errno != EACCES && errno != EAGAIN)) {
-			tst_resm(TFAIL,
-				 "SETLK: rc = %d, errno = %d, -1/EAGAIN or EACCES "
-				 "was expected", rc, errno);
-			fail = 1;
-		}
-		if (rc == 0) {
-			/* accidentally got the lock */
-			/* XXX how to clean up? */
-			(void)fcntl(fd, F_UNLCK, &flock);
-		}
-		/*
-		 * Lock should succeed after blocking and parent releases
-		 * lock, tell the parent to release the locks.
-		 * Do the lock in this process, send the signal in a child
-		 * process, so that the SETLKW actually uses the blocking
-		 * mechanism in the kernel.
-		 *
-		 * XXX inherent race: we want to wait until the
-		 * F_SETLKW has started, but we don't have a way to
-		 * check that reliably in the child.  (We'd
-		 * need some way to have fcntl() atomically unblock a
-		 * signal and wait for the lock.)
-		 */
-		pid = tst_fork();
-		switch (pid) {
-		case -1:
-			tst_resm(TFAIL, "Fork failed");
-			fail = 1;
-			break;
-		case 0:
-			do_usleep_child();
-			break;
-
-		default:
-			if ((rc = fcntl(fd, F_SETLKW, &flock)) < 0) {
-				tst_resm(TFAIL, "Attempt to set child BLOCKING "
-					 "lock failed");
-				tst_resm(TFAIL, "Test case %d, errno = %d",
-					 test + 1, errno);
-				fail = 1;
-			}
-			waitpid(pid, &status, 0);
-			break;
-		}
-	}
-	if (fail) {
-		exit(1);
+static void genconf(struct file_conf *conf, const int size, const long pos)
+{
+	conf->type = rand() % 2 ? F_RDLCK : F_WRLCK;
+	conf->whence = SEEK_CUR;
+
+	if (pos > 0 && (rand() % 2)) {
+		conf->start = -(rand() % pos);
+		conf->len = rand() % (size + conf->start - 1) + 1;
 	} else {
-		exit(0);
+		conf->start = rand() % (size - 1);
+		conf->len = rand() % (size - conf->start - 1) + 1;
 	}
 }
 
-void run_test(int file_flag, int file_mode, int seek, int start, int end)
+static short fcntl_overlap(
+	struct file_conf *parent,
+	struct file_conf *child,
+	const long pos)
 {
-	fail = 0;
-
-	for (test = start; test < end; test++) {
-		fd = SAFE_OPEN(cleanup, tmpname, file_flag, file_mode);
-
-		if (write(fd, FILEDATA, 10) < 0)
-			tst_brkm(TBROK, cleanup, "write() failed");
-
-		if (seek) {
-			SAFE_LSEEK(cleanup, fd, seek, 0);
-		}
-
-		thiscase = &testcases[test];
-		flock.l_type = thiscase->a_type;
-		flock.l_whence = thiscase->a_whence;
-		flock.l_start = thiscase->a_start;
-		flock.l_len = thiscase->a_len;
-
-		/* set the initial parent lock on the file */
-		if ((fcntl(fd, F_SETLK, &flock)) < 0) {
-			tst_resm(TFAIL, "First parent lock failed");
-			tst_resm(TFAIL, "Test case %d, errno = %d",
-				 test + 1, errno);
-			fail = 1;
-		}
-
-		if ((thiscase->b_type) != SKIP) {
-			flock.l_type = thiscase->b_type;
-			flock.l_whence = thiscase->b_whence;
-			flock.l_start = thiscase->b_start;
-			flock.l_len = thiscase->b_len;
-
-			/* set the second parent lock */
-			if ((fcntl(fd, F_SETLK, &flock)) < 0) {
-				tst_resm(TFAIL, "Second parent lock failed");
-				tst_resm(TFAIL, "Test case %d, errno = %d",
-					 test + 1, errno);
-				fail = 1;
-			}
-		}
-		if ((thiscase->c_type) == SKIP) {
-			close(fd);
-			tst_resm(TINFO, "skipping test %d", test + 1);
-			continue;
-		}
-
-		/* Mask SIG_USR1 before forking child, to avoid race */
-		(void)sighold(SIGUSR1);
-
-		/* flush the stdout to avoid garbled output */
-		fflush(stdout);
-
-		if ((child = tst_fork()) == 0)
-			dochild();
-
-		if (child < 0)
-			tst_brkm(TBROK|TERRNO, cleanup, "Fork failed");
-
-		if ((thiscase->c_flag) == WILLBLOCK) {
-			/*
-			 * Wait for a signal from the child then remove
-			 * blocking lock. Set a 60 second alarm to break the
-			 * pause just in case the child never signals us.
-			 */
-			alarm(TIME_OUT);
-			sigpause(SIGUSR1);
-
-			/* turn off the alarm timer */
-			alarm((unsigned)0);
-			if (got1 != 1)
-				tst_resm(TINFO, "Pause terminated without "
-					 "signal SIGUSR1 from child");
-			got1 = 0;
-
-			/*
-			 * setup lock structure for parent to delete
-			 * blocking lock then wait for child to exit
-			 */
-			flock.l_type = F_UNLCK;
-			flock.l_whence = 0;
-			flock.l_start = 0L;
-			flock.l_len = 0L;
-			if ((fcntl(fd, F_SETLK, &flock)) < 0) {
-				tst_resm(TFAIL, "Attempt to release parent "
-					 "lock failed");
-				tst_resm(TFAIL, "Test case %d, errno = %d",
-					 test + 1, errno);
-				fail = 1;
-			}
-		}
-		/*
-		 * set a 60 second alarm to break the wait just in case the
-		 * child doesn't terminate on its own accord
-		 */
-		alarm(TIME_OUT);
-
-		/* wait for the child to terminate and close the file */
-		waitpid(child, &status, 0);
-		/* turn off the alarm clock */
-		alarm((unsigned)0);
-		if (status != 0) {
-			tst_resm(TFAIL, "tchild returned status 0x%x", status);
-			fail = 1;
-		}
-		close(fd);
-		if (fail)
-			tst_resm(TFAIL, "testcase:%d FAILED", test + 1);
-		else
-			tst_resm(TPASS, "testcase:%d PASSED", test + 1);
+	long start[2];
+	long length[2];
+	short overlap = 0;
+
+	if (parent->start > child->start) {
+		start[0] = pos + child->start;
+		start[1] = pos + parent->start;
+		length[0] = child->len;
+		length[1] = parent->len;
+	} else {
+		start[0] = pos + parent->start;
+		start[1] = pos + child->start;
+		length[0] = parent->len;
+		length[1] = child->len;
 	}
-	unlink(tmpname);
+
+	overlap = start[0] <= start[1] && start[1] < (start[0] + length[0]);
+
+	if (overlap)
+		tst_res(TDEBUG, "child/parent fcntl() configurations overlap");
+
+	return overlap;
 }
 
-void catch_alarm(int sig)
+static void gentestcase(struct testcase *tc)
 {
-	/*
-	 * Timer has runout and child has not signaled, need
-	 * to kill off the child as it appears it will not
-	 * on its own accord. Check that it is still around
-	 * as it may have terminated abnormally while parent
-	 * was waiting for SIGUSR1 signal from it.
-	 */
-	if (kill(child, 0) == 0) {
-		kill(child, SIGKILL);
-		perror("The child didnot terminate on its own accord");
-	}
+	struct file_conf *parent = &tc->parent;
+	struct file_conf *child = &tc->child;
+	int size;
+
+	size = strlen(filedata);
+	tc->pos = rand() % size;
+
+	genconf(parent, size, tc->pos);
+	genconf(child, size, tc->pos);
+
+	tc->blocking = fcntl_overlap(parent, child, tc->pos);
+
+	if (parent->type == F_RDLCK && child->type == F_RDLCK)
+		tc->blocking = 0;
 }
 
-void catch1(int sig)
+static void print_testcase(struct testcase *tc)
 {
-	struct sigaction act;
-
-	/*
-	 * Set flag to let parent know that child is ready to have lock
-	 * removed
-	 */
-	memset(&act, 0, sizeof(act));
-	act.sa_handler = catch1;
-	sigemptyset(&act.sa_mask);
-	sigaddset(&act.sa_mask, SIGUSR1);
-	sigaction(SIGUSR1, &act, NULL);
-	got1++;
+	tst_res(TDEBUG, "Starting read/write position: %ld", tc->pos);
+
+	tst_res(TDEBUG,
+		"Parent: type=%s whence=%s start=%ld len=%ld",
+		tc->parent.type == F_RDLCK ? "F_RDLCK" : "F_WRLCK",
+		tc->parent.whence == SEEK_SET ? "SEEK_SET" : "SEEK_CUR",
+		tc->parent.start,
+		tc->parent.len);
+
+	tst_res(TDEBUG,
+		"Child: type=%s whence=%s start=%ld len=%ld",
+		tc->child.type == F_RDLCK ? "F_RDLCK" : "F_WRLCK",
+		tc->child.whence == SEEK_SET ? "SEEK_SET" : "SEEK_CUR",
+		tc->child.start,
+		tc->child.len);
+
+	tst_res(TDEBUG, "Expencted %s test",
+		tc->blocking ? "blocking" : "non-blocking");
 }
 
-static void testcheck_end(int check_fail, char *msg)
+static void run(void)
 {
-	if (check_fail)
-		tst_resm(TFAIL, "%s FAILED", msg);
-	else
-		tst_resm(TPASS, "%s PASSED", msg);
+	struct testcase tc;
+
+	results->num_pass = 0;
+
+	for (int i = 0; i < op_nums; i++) {
+		gentestcase(&tc);
+		print_testcase(&tc);
+
+		tst_res(TDEBUG, "Running test #%u", i);
+		run_testcase(&tc, file_mode);
+
+		if (results->last_failed)
+			return;
+
+		results->num_pass++;
+	}
+
+	if (results->num_pass == op_nums)
+		tst_res(TPASS, "All %d test file operations passed", op_nums);
 }
 
-int main(int ac, char **av)
+static void setup(void)
 {
-	int lc;
-
-	tst_parse_opts(ac, av, NULL, NULL);
-
-	setup();
-
-	if (tst_fs_type(cleanup, ".") == TST_NFS_MAGIC)
-		NO_NFS = 0;
-
-	for (lc = 0; TEST_LOOPING(lc); lc++) {
-		tst_count = 0;
-
-/* //block1: */
-		tst_resm(TINFO, "Enter block 1: without mandatory locking");
-		fail = 0;
-		/*
-		 * try various file locks on an ordinary file without
-		 * mandatory locking
-		 */
-		(void)run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 0, 0, 36);
-		testcheck_end(fail, "Block 1, test 1");
-
-		/* Now try with negative values for L_start and L_len */
-		(void)run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 5, 36, 45);
-		testcheck_end(fail, "Block 1, test 2");
-
-		tst_resm(TINFO, "Exit block 1");
-
-/* //block2: */
-		/*
-		 * Skip block2 if test on NFS, since NFS does not support
-		 * mandatory locking
-		 */
-		tst_resm(TINFO, "Enter block 2: with mandatory locking");
-		if (NO_NFS) {
-			fail = 0;
-			/*
-			 * Try various locks on a file with mandatory
-			 * record locking this should behave the same
-			 * as an ordinary file
-			 */
-			(void)run_test(O_CREAT | O_RDWR | O_TRUNC,
-				S_ENFMT | S_IRUSR | S_IWUSR, 0, 0, 36);
-			testcheck_end(fail, "Block 2, test 1");
-
-			/* Now try negative values for L_start and L_len */
-			(void)run_test(O_CREAT | O_RDWR | O_TRUNC,
-				S_ENFMT | S_IRUSR | S_IWUSR, 5, 36, 45);
-			testcheck_end(fail, "Block 2, test 2");
-		} else {
-			tst_resm(TCONF, "Skip block 2 as NFS does not"
-				" support mandatory locking");
-		}
-
-		tst_resm(TINFO, "Exit block 2");
-
-/* //block3: */
-		tst_resm(TINFO, "Enter block 3");
-		fail = 0;
-		/*
-		 * Check that proper error status is returned when invalid
-		 * argument used for WHENCE (negative value)
-		 */
-
-		fd = SAFE_OPEN(cleanup, tmpname, O_CREAT | O_RDWR | O_TRUNC,
-			       0777);
-
-		if (write(fd, FILEDATA, 10) < 0)
-			tst_brkm(TBROK, cleanup, "write failed");
-
-		flock.l_type = F_WRLCK;
-		flock.l_whence = -1;
-		flock.l_start = 0L;
-		flock.l_len = 0L;
-
-		if ((fcntl(fd, F_SETLK, &flock)) < 0) {
-			if (errno != EINVAL) {
-				tst_resm(TFAIL, "Expected %d got %d",
-					 EINVAL, errno);
-				fail = 1;
-			}
-		} else {
-			tst_resm(TFAIL, "Lock succeeded when it should have "
-				 "failed");
-			fail = 1;
-		}
-
-		close(fd);
-		unlink(tmpname);
-
-		testcheck_end(fail, "Test with negative whence locking");
-		tst_resm(TINFO, "Exit block 3");
-
-/* //block4: */
-		tst_resm(TINFO, "Enter block 4");
-		fail = 0;
-		/*
-		 * Check that a lock on end of file is still valid when
-		 * additional data is appended to end of file and a new
-		 * process attempts to lock new data
-		 */
-		fd = SAFE_OPEN(cleanup, tmpname, O_CREAT | O_RDWR | O_TRUNC,
-			       0777);
-
-		if (write(fd, FILEDATA, 10) < 0)
-			tst_brkm(TBROK, cleanup, "write failed");
-
-		thiscase = &testcases[58];
-		flock.l_type = thiscase->a_type;
-		flock.l_whence = thiscase->a_whence;
-		flock.l_start = thiscase->a_start;
-		flock.l_len = thiscase->a_len;
-
-		/* Set the initial parent lock on the file */
-		if ((fcntl(fd, F_SETLK, &flock)) < 0) {
-			tst_resm(TFAIL, "First parent lock failed");
-			tst_resm(TFAIL, "Test case %d, errno = %d", 58, errno);
-			fail = 1;
-		}
-
-		/* Write some additional data to end of file */
-		if (write(fd, FILEDATA, 10) < 0)
-			tst_brkm(TBROK, cleanup, "write failed");
-
-		/* Mask signal to avoid race */
-		if (sighold(SIGUSR1) < 0)
-			tst_brkm(TBROK, cleanup, "sighold failed");
-
-		if ((child = tst_fork()) == 0)
-			dochild();
-
-		if (child < 0)
-			tst_brkm(TBROK|TERRNO, cleanup, "Fork failed");
-
-		/*
-		 * Wait for a signal from the child then remove blocking lock.
-		 * Set a 60 sec alarm to break the pause just in case the
-		 * child doesn't terminate on its own accord
-		 */
-		(void)alarm(TIME_OUT);
-
-		(void)sigpause(SIGUSR1);
-
-		/* turn off the alarm timer */
-		(void)alarm((unsigned)0);
-		if (got1 != 1) {
-			tst_resm(TINFO, "Pause terminated without signal "
-				 "SIGUSR1 from child");
-		}
-		got1 = 0;
-
-		/*
-		 * Set up lock structure for parent to delete
-		 * blocking lock then wait for child to exit
-		 */
-		flock.l_type = F_UNLCK;
-		flock.l_whence = 0;
-		flock.l_start = 0L;
-		flock.l_len = 0L;
-		if ((fcntl(fd, F_SETLK, &flock)) < 0) {
-			tst_resm(TFAIL, "Attempt to release parent lock "
-				 "failed");
-			tst_resm(TFAIL, "Test case %d, errno = %d", test + 1,
-				 errno);
-			fail = 1;
-		}
-
-		/*
-		 * set a 60 sec alarm to break the wait just in case the
-		 * child doesn't terminate on its own accord
-		 */
-		(void)alarm(TIME_OUT);
-
-		waitpid(child, &status, 0);
-		if (WEXITSTATUS(status) != 0) {
-			fail = 1;
-			tst_resm(TFAIL, "child returned bad exit status");
-		}
-
-		/* turn off the alarm clock */
-		(void)alarm((unsigned)0);
-		if (status != 0) {
-			tst_resm(TFAIL, "child returned status 0x%x", status);
-			fail = 1;
-		}
-		close(fd);
-		unlink(tmpname);
-
-		testcheck_end(fail, "Test of locks on file");
-		tst_resm(TINFO, "Exit block 4");
+	int fd;
+
+	if (tst_parse_int(str_op_nums, &op_nums, 1, INT_MAX))
+		tst_brk(TBROK, "Invalid number of operations '%s'", str_op_nums);
+
+	if (locking_file) {
+		tst_res(TINFO, "Requested mandatory locking");
+
+		file_mode = S_ENFMT | 0600;
 	}
-	cleanup();
-	tst_exit();
+
+	fd = SAFE_OPEN(filepath, O_CREAT | O_RDWR | O_TRUNC, 0777);
+	SAFE_WRITE(SAFE_WRITE_ALL, fd, filedata, strlen(filedata));
+	SAFE_CLOSE(fd);
+
+	srand(time(0));
+
+	results = SAFE_MMAP(
+		NULL,
+		sizeof(struct tc_results),
+	    PROT_READ | PROT_WRITE,
+		MAP_ANONYMOUS | MAP_SHARED,
+		-1, 0);
 }
+
+static void cleanup(void)
+{
+	if (results)
+		SAFE_MUNMAP(results, sizeof(struct tc_results));
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.setup = setup,
+	.cleanup = cleanup,
+	.forks_child = 1,
+	.needs_tmpdir = 1,
+	.options = (struct tst_option[]) {
+		{ "n:", &str_op_nums, "Total # operations to do (default 5000)" },
+		{ "l", &locking_file, "Set locking flag on file" },
+		{},
+	},
+	.skip_filesystems = (const char *const []) {
+		"nfs",
+		NULL
+	},
+};