@@ -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/
new file mode 100644
@@ -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;
+}
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@bmw-carit.de> --- Makefile.am | 2 +- posix04.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 posix04.c