xref: /minix3/tests/kernel/t_lockf.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc /*	$NetBSD: t_lockf.c,v 1.9 2013/10/19 17:45:00 christos Exp $	*/
211be35a1SLionel Sambuc 
311be35a1SLionel Sambuc /*-
411be35a1SLionel Sambuc  * Copyright (c) 2000 The NetBSD Foundation, Inc.
511be35a1SLionel Sambuc  * All rights reserved.
611be35a1SLionel Sambuc  *
711be35a1SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
811be35a1SLionel Sambuc  * modification, are permitted provided that the following conditions
911be35a1SLionel Sambuc  * are met:
1011be35a1SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
1111be35a1SLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
1211be35a1SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
1311be35a1SLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
1411be35a1SLionel Sambuc  *    documentation and/or other materials provided with the distribution.
1511be35a1SLionel Sambuc  *
1611be35a1SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1711be35a1SLionel Sambuc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1811be35a1SLionel Sambuc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1911be35a1SLionel Sambuc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2011be35a1SLionel Sambuc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2111be35a1SLionel Sambuc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2211be35a1SLionel Sambuc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2311be35a1SLionel Sambuc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2411be35a1SLionel Sambuc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2511be35a1SLionel Sambuc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2611be35a1SLionel Sambuc  * POSSIBILITY OF SUCH DAMAGE.
2711be35a1SLionel Sambuc  */
2811be35a1SLionel Sambuc 
2911be35a1SLionel Sambuc #include <atf-c.h>
3011be35a1SLionel Sambuc #include <err.h>
3111be35a1SLionel Sambuc #include <errno.h>
3211be35a1SLionel Sambuc #include <fcntl.h>
3311be35a1SLionel Sambuc #include <signal.h>
3411be35a1SLionel Sambuc #include <stdio.h>
3511be35a1SLionel Sambuc #include <stdlib.h>
3611be35a1SLionel Sambuc #include <string.h>
3711be35a1SLionel Sambuc #include <unistd.h>
3811be35a1SLionel Sambuc 
3911be35a1SLionel Sambuc #include <sys/types.h>
4011be35a1SLionel Sambuc #include <sys/wait.h>
4111be35a1SLionel Sambuc #include <sys/ptrace.h>
4211be35a1SLionel Sambuc 
4311be35a1SLionel Sambuc /*
4411be35a1SLionel Sambuc  * lockf1 regression test:
4511be35a1SLionel Sambuc  *
4611be35a1SLionel Sambuc  * Tests:
4711be35a1SLionel Sambuc  * Fork N child processes, each of which gets M random byte range locks
4811be35a1SLionel Sambuc  * on a common file.  We ignore all lock errors (practically speaking,
4911be35a1SLionel Sambuc  * this means EDEADLK or ENOLOCK), but we make numerous passes over all
5011be35a1SLionel Sambuc  * the children to make sure that they are still awake.  (We do this by
5111be35a1SLionel Sambuc  * verifying that we can ptrace(ATTACH/DETACH) to the children and get
5211be35a1SLionel Sambuc  * their status via waitpid().)
5311be35a1SLionel Sambuc  * When finished, reap all the children.
5411be35a1SLionel Sambuc  */
5511be35a1SLionel Sambuc 
5611be35a1SLionel Sambuc #define	nlocks		500	/* number of locks per thread */
5711be35a1SLionel Sambuc #define	nprocs		10	/* number of processes to spawn */
5811be35a1SLionel Sambuc #define	npasses		50	/* number of passes to make over the children */
5911be35a1SLionel Sambuc #define	sleeptime	150000	/* sleep time between locks, usec */
6011be35a1SLionel Sambuc #define	filesize 	8192	/* size of file to lock */
6111be35a1SLionel Sambuc 
6211be35a1SLionel Sambuc const char *lockfile = "lockf_test";
6311be35a1SLionel Sambuc 
6411be35a1SLionel Sambuc static u_int32_t
random_uint32(void)6511be35a1SLionel Sambuc random_uint32(void)
6611be35a1SLionel Sambuc {
6711be35a1SLionel Sambuc 	return lrand48();
6811be35a1SLionel Sambuc }
6911be35a1SLionel Sambuc 
7011be35a1SLionel Sambuc static void
trylocks(int id)7111be35a1SLionel Sambuc trylocks(int id)
7211be35a1SLionel Sambuc {
73*84d9c625SLionel Sambuc 	int i, fd;
7411be35a1SLionel Sambuc 
7511be35a1SLionel Sambuc 	srand48(getpid());
7611be35a1SLionel Sambuc 
7711be35a1SLionel Sambuc 	fd = open (lockfile, O_RDWR, 0);
7811be35a1SLionel Sambuc 
7911be35a1SLionel Sambuc 	if (fd < 0)
8011be35a1SLionel Sambuc 		err(1, "%s", lockfile);
8111be35a1SLionel Sambuc 
8211be35a1SLionel Sambuc 	printf("%d: start\n", id);
8311be35a1SLionel Sambuc 
8411be35a1SLionel Sambuc 	for (i = 0; i < nlocks; i++) {
8511be35a1SLionel Sambuc 		struct flock fl;
8611be35a1SLionel Sambuc 
8711be35a1SLionel Sambuc 		fl.l_start = random_uint32() % filesize;
8811be35a1SLionel Sambuc 		fl.l_len = random_uint32() % filesize;
8911be35a1SLionel Sambuc 		switch (random_uint32() % 3) {
9011be35a1SLionel Sambuc 		case 0:
9111be35a1SLionel Sambuc 			fl.l_type = F_RDLCK;
9211be35a1SLionel Sambuc 			break;
9311be35a1SLionel Sambuc 		case 1:
9411be35a1SLionel Sambuc 			fl.l_type = F_WRLCK;
9511be35a1SLionel Sambuc 			break;
9611be35a1SLionel Sambuc 		case 2:
9711be35a1SLionel Sambuc 			fl.l_type = F_UNLCK;
9811be35a1SLionel Sambuc 			break;
9911be35a1SLionel Sambuc 		}
10011be35a1SLionel Sambuc 		fl.l_whence = SEEK_SET;
10111be35a1SLionel Sambuc 
102*84d9c625SLionel Sambuc 		(void)fcntl(fd, F_SETLKW, &fl);
10311be35a1SLionel Sambuc 
10411be35a1SLionel Sambuc 		if (usleep(sleeptime) < 0)
10511be35a1SLionel Sambuc 		  err(1, "usleep");
10611be35a1SLionel Sambuc 	}
10711be35a1SLionel Sambuc 	printf("%d: done\n", id);
10811be35a1SLionel Sambuc 	close (fd);
10911be35a1SLionel Sambuc }
11011be35a1SLionel Sambuc 
11111be35a1SLionel Sambuc ATF_TC(randlock);
ATF_TC_HEAD(randlock,tc)11211be35a1SLionel Sambuc ATF_TC_HEAD(randlock, tc)
11311be35a1SLionel Sambuc {
11411be35a1SLionel Sambuc 
11511be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "timeout", "300");
11611be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) locking");
11711be35a1SLionel Sambuc }
11811be35a1SLionel Sambuc 
ATF_TC_BODY(randlock,tc)11911be35a1SLionel Sambuc ATF_TC_BODY(randlock, tc)
12011be35a1SLionel Sambuc {
12111be35a1SLionel Sambuc 	int i, j, fd;
12211be35a1SLionel Sambuc 	int pipe_fd[2];
12311be35a1SLionel Sambuc 	pid_t *pid;
12411be35a1SLionel Sambuc 	int status;
12511be35a1SLionel Sambuc 	char pipe_in, pipe_out;
12611be35a1SLionel Sambuc 	const char pipe_errmsg[] = "child: pipe write failed\n";
12711be35a1SLionel Sambuc 
12811be35a1SLionel Sambuc 	(void)unlink(lockfile);
12911be35a1SLionel Sambuc 
13011be35a1SLionel Sambuc 	fd = open (lockfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0666);
13111be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", lockfile, strerror(errno));
13211be35a1SLionel Sambuc 
13311be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(ftruncate(fd, filesize) >= 0,
13411be35a1SLionel Sambuc 	    "ftruncate(%s): %s", lockfile, strerror(errno));
13511be35a1SLionel Sambuc 
13611be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(pipe(pipe_fd) == 0, "pipe: %s", strerror(errno));
13711be35a1SLionel Sambuc 
13811be35a1SLionel Sambuc 	fsync(fd);
13911be35a1SLionel Sambuc 	close(fd);
14011be35a1SLionel Sambuc 
14111be35a1SLionel Sambuc 	pid = malloc(nprocs * sizeof(pid_t));
14211be35a1SLionel Sambuc 
14311be35a1SLionel Sambuc 	for (i = 0; i < nprocs; i++) {
14411be35a1SLionel Sambuc 		pipe_out = (char)('A' + i);
14511be35a1SLionel Sambuc 		pid[i] = fork();
14611be35a1SLionel Sambuc 		switch (pid[i]) {
14711be35a1SLionel Sambuc 		case 0:
14811be35a1SLionel Sambuc 			if (write(pipe_fd[1], &pipe_out, 1) != 1)
14911be35a1SLionel Sambuc 				write(STDERR_FILENO, pipe_errmsg,
15011be35a1SLionel Sambuc 				    __arraycount(pipe_errmsg) - 1);
15111be35a1SLionel Sambuc 			else
15211be35a1SLionel Sambuc 				trylocks(i);
15311be35a1SLionel Sambuc 			_exit(0);
15411be35a1SLionel Sambuc 			break;
15511be35a1SLionel Sambuc 		case -1:
15611be35a1SLionel Sambuc 			atf_tc_fail("fork %d failed", i);
15711be35a1SLionel Sambuc 			break;
15811be35a1SLionel Sambuc 		default:
15911be35a1SLionel Sambuc 			ATF_REQUIRE_MSG(read(pipe_fd[0], &pipe_in, 1) == 1,
16011be35a1SLionel Sambuc 			    "parent: read_pipe(%i): %s", i, strerror(errno));
16111be35a1SLionel Sambuc 			ATF_REQUIRE_MSG(pipe_in == pipe_out,
16211be35a1SLionel Sambuc 			    "parent: pipe does not match");
16311be35a1SLionel Sambuc 			break;
16411be35a1SLionel Sambuc 		}
16511be35a1SLionel Sambuc 	}
16611be35a1SLionel Sambuc 	for (j = 0; j < npasses; j++) {
16711be35a1SLionel Sambuc 		printf("parent: run %i\n", j+1);
16811be35a1SLionel Sambuc 		for (i = 0; i < nprocs; i++) {
16911be35a1SLionel Sambuc 			ATF_REQUIRE_MSG(ptrace(PT_ATTACH, pid[i], 0, 0) >= 0,
17011be35a1SLionel Sambuc 			    "ptrace attach %d", pid[i]);
17111be35a1SLionel Sambuc 			ATF_REQUIRE_MSG(waitpid(pid[i], &status, WUNTRACED) >= 0,
17211be35a1SLionel Sambuc 			    "waitpid(ptrace)");
17311be35a1SLionel Sambuc 			usleep(sleeptime / 3);
17411be35a1SLionel Sambuc 			ATF_REQUIRE_MSG(ptrace(PT_DETACH, pid[i], (caddr_t)1,
17511be35a1SLionel Sambuc 					       0) >= 0,
17611be35a1SLionel Sambuc 			    "ptrace detach %d", pid[i]);
17711be35a1SLionel Sambuc 			usleep(sleeptime / 3);
17811be35a1SLionel Sambuc 		}
17911be35a1SLionel Sambuc 	}
18011be35a1SLionel Sambuc 	for (i = 0; i < nprocs; i++) {
18111be35a1SLionel Sambuc 		printf("reap %d: ", i);
18211be35a1SLionel Sambuc 		fflush(stdout);
18311be35a1SLionel Sambuc 		kill(pid[i], SIGINT);
18411be35a1SLionel Sambuc 		waitpid(pid[i], &status, 0);
18511be35a1SLionel Sambuc 		printf(" status %d\n", status);
18611be35a1SLionel Sambuc 	}
18711be35a1SLionel Sambuc 	atf_tc_pass();
18811be35a1SLionel Sambuc }
18911be35a1SLionel Sambuc 
19011be35a1SLionel Sambuc static int
dolock(int fd,int op,off_t lk_off,off_t lk_size)19111be35a1SLionel Sambuc dolock(int fd, int op, off_t lk_off, off_t lk_size)
19211be35a1SLionel Sambuc {
19311be35a1SLionel Sambuc 	off_t result;
19411be35a1SLionel Sambuc 	int ret;
19511be35a1SLionel Sambuc 
19611be35a1SLionel Sambuc 	result = lseek(fd, lk_off, SEEK_SET);
19711be35a1SLionel Sambuc 	if (result == -1) {
19811be35a1SLionel Sambuc 		return errno;
19911be35a1SLionel Sambuc 	}
20011be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(result == lk_off, "lseek to wrong offset");
20111be35a1SLionel Sambuc 	ret = lockf(fd, op, lk_size);
20211be35a1SLionel Sambuc 	if (ret == -1) {
20311be35a1SLionel Sambuc 		return errno;
20411be35a1SLionel Sambuc 	}
20511be35a1SLionel Sambuc 	return 0;
20611be35a1SLionel Sambuc }
20711be35a1SLionel Sambuc 
20811be35a1SLionel Sambuc ATF_TC(deadlock);
ATF_TC_HEAD(deadlock,tc)20911be35a1SLionel Sambuc ATF_TC_HEAD(deadlock, tc)
21011be35a1SLionel Sambuc {
21111be35a1SLionel Sambuc 
21211be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "timeout", "30");
21311be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) deadlock detection");
21411be35a1SLionel Sambuc }
21511be35a1SLionel Sambuc 
ATF_TC_BODY(deadlock,tc)21611be35a1SLionel Sambuc ATF_TC_BODY(deadlock, tc)
21711be35a1SLionel Sambuc {
21811be35a1SLionel Sambuc 	int fd;
21911be35a1SLionel Sambuc 	int error;
22011be35a1SLionel Sambuc 	int ret;
22111be35a1SLionel Sambuc 	pid_t pid;
22211be35a1SLionel Sambuc 
22311be35a1SLionel Sambuc 	(void)unlink(lockfile);
22411be35a1SLionel Sambuc 
22511be35a1SLionel Sambuc 	fd = open (lockfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0666);
22611be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", lockfile, strerror(errno));
22711be35a1SLionel Sambuc 
22811be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(ftruncate(fd, filesize) >= 0,
22911be35a1SLionel Sambuc 	    "ftruncate(%s): %s", lockfile, strerror(errno));
23011be35a1SLionel Sambuc 
23111be35a1SLionel Sambuc 	fsync(fd);
23211be35a1SLionel Sambuc 
23311be35a1SLionel Sambuc 	error = dolock(fd, F_LOCK, 0, 1);
23411be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(error == 0, "initial dolock: %s", strerror(errno));
23511be35a1SLionel Sambuc 
23611be35a1SLionel Sambuc 	pid = fork();
23711be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno));
23811be35a1SLionel Sambuc 	if (pid == 0) {
23911be35a1SLionel Sambuc 		error = dolock(fd, F_LOCK, 1, 1);
24011be35a1SLionel Sambuc 		ATF_REQUIRE_MSG(error == 0, "child dolock: %s",
24111be35a1SLionel Sambuc 		    strerror(errno));
24211be35a1SLionel Sambuc 		dolock(fd, F_LOCK, 0, 1);	/* will block */
24311be35a1SLionel Sambuc 		atf_tc_fail("child did not block");
24411be35a1SLionel Sambuc 	}
24511be35a1SLionel Sambuc 	sleep(1);	/* give child time to grab its lock then block */
24611be35a1SLionel Sambuc 
24711be35a1SLionel Sambuc 	error = dolock(fd, F_LOCK, 1, 1);
24811be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(error == EDEADLK, "parent did not detect deadlock: %s",
24911be35a1SLionel Sambuc 	    strerror(errno));
25011be35a1SLionel Sambuc 	ret = kill(pid, SIGKILL);
25111be35a1SLionel Sambuc 	ATF_REQUIRE_MSG(ret != -1, "failed to kill child: %s", strerror(errno));
25211be35a1SLionel Sambuc 
25311be35a1SLionel Sambuc 	atf_tc_pass();
25411be35a1SLionel Sambuc }
25511be35a1SLionel Sambuc 
ATF_TP_ADD_TCS(tp)25611be35a1SLionel Sambuc ATF_TP_ADD_TCS(tp)
25711be35a1SLionel Sambuc {
25811be35a1SLionel Sambuc 	ATF_TP_ADD_TC(tp, randlock);
25911be35a1SLionel Sambuc 	ATF_TP_ADD_TC(tp, deadlock);
26011be35a1SLionel Sambuc 
26111be35a1SLionel Sambuc 	return atf_no_error();
26211be35a1SLionel Sambuc }
263