The dinning philosopher locking 'problem'. A given number of clients try to lock on a file its own region and than the region 'right' from its own region. Signed-off-by: Daniel Wagner <daniel.wagner@xxxxxxxxxxxx> --- Makefile.am | 2 +- posix04.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 posix04.c diff --git a/Makefile.am b/Makefile.am index 4a2ae98..c2eb816 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,6 +4,6 @@ ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = m4/gnulib-cache.m4 LDADD = lib/libgnu.a -lrt -bin_PROGRAMS = flock01 flock02 posix01 posix02 posix03 lease01 lease02 +bin_PROGRAMS = flock01 flock02 posix01 posix02 posix03 posix04 lease01 lease02 SUBDIRS = lib/ diff --git a/posix04.c b/posix04.c new file mode 100644 index 0000000..ed862f2 --- /dev/null +++ b/posix04.c @@ -0,0 +1,288 @@ +/* + * POSIX deadlock detection performance test + * + * The dinning philosopher locking 'problem'. A given number of + * clients try to lock on a file its own region and than the region + * 'right' from its own region. + * + * Obviously, We could just give them enough forks or a gun. Both + * solves the problem once for all but that's no fun. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <err.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <regex.h> +#include <assert.h> +#include <ctype.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdbool.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <errno.h> + +#include "timespec.h" + +#define DEFAULT_PROCESSES 10 +#define DEFAULT_ITERATIONS 10000 + +#define TFR TEMP_FAILURE_RETRY + +struct child_context +{ + pid_t pid; + struct timespec diff; + unsigned int deadlock; +}; + +static struct child_context *ctxs; + +/* Taken from TDB: LGPLv3 */ +static int fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag) +{ + struct flock fl; + + /* + fprintf(stderr, "%d: lock\tfd %d off %5zd len %5zd\n", + getpid(), fd, off, len); + */ + + fl.l_type = rw; + fl.l_whence = SEEK_SET; + fl.l_start = off; + fl.l_len = len; + fl.l_pid = 0; + + if (waitflag) + return fcntl(fd, F_SETLKW, &fl); + else + return fcntl(fd, F_SETLK, &fl); +} + +static int fcntl_unlock(int fd, off_t off, off_t len) +{ + struct flock fl; + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = off; + fl.l_len = len; + fl.l_pid = 0; + + /* + fprintf(stderr, "%d: unlock\tfd %d off %5zd len %5zd\n", + getpid(), fd, off, len); + */ + + return fcntl(fd, F_SETLKW, &fl); +} + +static int +lockunlock(int fd, int id, int nproc) +{ + int ret, nid; + off_t off, len; + off_t noff, nlen; + + nid = (id + 1) % nproc; + + off = id * 10; + len = id * 10 + 10; + + noff = nid * 10; + nlen = nid * 10 + 10; + + ret = fcntl_lock(fd, F_WRLCK, off, len, true); + if (ret) + return ret; + + ret = fcntl_lock(fd, F_WRLCK, noff, nlen, true); + if (ret) + goto err; + + if (fcntl_unlock(fd, noff, nlen)) + perror("unlock"); + +err: + if (fcntl_unlock(fd, off, len)) + perror("unlock"); + + return ret; +} + +static int do_child(int fd, int id, int nproc, int to_lockers, int from_lockers) +{ + struct timespec start, end; + unsigned char c; + int ret; + + while(TFR(read(to_lockers, &c, 1)) == 1) { + if (c != 'g') + return 0; + + ret = clock_gettime(CLOCK_MONOTONIC_RAW, &start); + if (ret) { + perror("clock_gettime"); + return ret; + } + + if (lockunlock(fd, id, nproc) != 0) + ctxs[id].deadlock++; + + ret = clock_gettime(CLOCK_MONOTONIC_RAW, &end); + if (ret) { + perror("clock_gettime"); + return ret; + } + + ctxs[id].diff = timespec_add(ctxs[id].diff, + timespec_sub(end, start)); + + if (TFR(write(from_lockers, &c, 1)) != 1) + fprintf(stderr, "Writing to parent"); + } + + return 0; +} + +static int +usage(char *argv0) +{ + errx(1, "Usage: %s [-i iterations] [-n nr_children] [-s] <filename>", argv0); +} + +int main(int argc, char *argv[]) +{ + int nproc = DEFAULT_PROCESSES, i, opt, valid = 0, stats = 0; + int iter = DEFAULT_ITERATIONS; + const char *filename; + int to_lockers[2], from_lockers[2], fd; + struct timespec total = { .tv_sec = 0, + .tv_nsec = 0 }; + unsigned char wc, rc; + unsigned int deadlock = 0; + + total.tv_sec = 0; + total.tv_nsec = 0; + + while ((opt = getopt(argc, argv, "i:n:s")) != -1) { + switch (opt) { + case 'i': + iter = atoi(optarg); + break; + case 'n': + nproc = atoi(optarg); + break; + case 's': + stats = 1; + break; + default: + usage(argv[0]); + } + } + + if (nproc < 2) { + fprintf(stderr, "Invalid argument: at least 2 child process needed\n"); + return 1; + } + + filename = argv[optind]; + if (!filename) + usage(argv[0]); + + ctxs = mmap(0, nproc * sizeof(*ctxs), PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, -1, 0); + + if (ctxs == (struct child_context *)-1) { + fprintf(stderr, "Unable to allocate child context array!"); + return 1; + } + + if (pipe(to_lockers)) + err(1, "pipe (to_lockers)"); + if (pipe(from_lockers)) + err(1, "pipe (from_lockers)"); + + fd = open(filename, O_CREAT|O_RDWR, 0644); + if (fd < 0) { + perror("open"); + return 1; + } + + for (i = 0; i < nproc; i++) { + ctxs[i].pid = fork(); + if (!ctxs[i].pid) + return do_child(fd, i, nproc, + to_lockers[0], from_lockers[1]); + } + + close(to_lockers[0]); + close(from_lockers[1]); + + wc = 'g'; /* go */ + while(iter--) { + for (i = 0; i < nproc; i++) { + if (TFR(write(to_lockers[1], &wc, 1)) != 1) { + perror("write"); + } + } + + for (i = 0; i < nproc; i++) { + if (TFR(read(from_lockers[0], &rc, 1)) != 1) + break; + } + } + + wc = '\0'; /* stop */ + for (i = 0; i < nproc; i++) { + if (TFR(write(to_lockers[1], &wc, 1)) != 1) { + perror("write"); + } + } + + for (i = 0; i < nproc; ++i) { + int status; + + if (ctxs[i].pid < 0) { + fprintf(stderr, "process %d failed to fork\n", i); + continue; + } + if (waitpid(ctxs[i].pid, &status, 0) < 0) { + fprintf(stderr, "unable to reap pid %d\n", ctxs[i].pid); + continue; + } + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + fprintf(stderr, "pid %d exited abnormally(0x%x)\n", + ctxs[i].pid, status); + continue; + } + total = timespec_add(total, ctxs[i].diff); + deadlock += ctxs[i].deadlock; + ++valid; + + } + + close(fd); + + if (valid != nproc) { + fprintf(stderr, "Some children didn't run properly -- " + "requested %d but only got %d\n", nproc, valid); + return 1; + } + + if (stats) + printf("deadlocks %u\n", deadlock); + printf("%ld.%09ld\n", total.tv_sec, total.tv_nsec); + + return 0; +} -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html