From 10315ae5291eb3f11f4ee9a94e18e963b663b48a Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 6 Jul 2015 10:49:47 +0200 Subject: [PATCH] tdb: Reproducer for Bug 11381 Signed-off-by: Volker Lendecke --- lib/tdb/test/run-allrecord-traverse-deadlock.c | 203 ++++++++++++++++++++++++ lib/tdb/wscript | 1 + 2 files changed, 204 insertions(+) create mode 100644 lib/tdb/test/run-allrecord-traverse-deadlock.c diff --git a/lib/tdb/test/run-allrecord-traverse-deadlock.c b/lib/tdb/test/run-allrecord-traverse-deadlock.c new file mode 100644 index 0000000..4c6c769 --- /dev/null +++ b/lib/tdb/test/run-allrecord-traverse-deadlock.c @@ -0,0 +1,203 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "../common/mutex.c" +#include "tap-interface.h" +#include +#include +#include +#include +#include "logging.h" + +static void do_allrecord_lock(const char *name, int tdb_flags, int up, + int down) +{ + struct tdb_context *tdb; + int ret; + ssize_t nread, nwritten; + char c = 0; + + tdb = tdb_open_ex(name, 3, tdb_flags, + O_RDWR|O_CREAT, 0755, &taplogctx, NULL); + ok(tdb, "tdb_open_ex should succeed"); + + ret = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false); + ok(ret == 0, "tdb_allrecord_lock should succeed"); + + nwritten = write(up, &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + nread = read(down, &c, sizeof(c)); + ok(nread == sizeof(c), "read should succeed"); + + ret = tdb_traverse(tdb, NULL, NULL); + ok(ret == -1, "do_allrecord_lock: traverse should fail"); + + nwritten = write(up, &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + exit(0); +} + +static void do_traverse(const char *name, int tdb_flags, int up, int down) +{ + struct tdb_context *tdb; + int ret; + ssize_t nread, nwritten; + char c = 0; + + tdb = tdb_open_ex(name, 3, tdb_flags, + O_RDWR|O_CREAT, 0755, &taplogctx, NULL); + ok(tdb, "tdb_open_ex should succeed"); + + ret = tdb_traverse(tdb, NULL, NULL); + ok(ret == 1, "do_traverse: tdb_traverse should return 1 record"); + + nwritten = write(up, &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + nread = read(down, &c, sizeof(c)); + ok(nread == sizeof(c), "read should succeed"); + + exit(0); +} + +/* + * Process 1: get the allrecord_lock on a tdb. + * Process 2: Start a traverse, this will stall waiting for first chainlock + * Process 1: Start a traverse: This will get EDEADLK in trying to + * get the TRANSACTION_LOCK. It will deadlock for mutexes + */ + +static int do_tests(const char *name, int tdb_flags) +{ + struct tdb_context *tdb; + int ret; + pid_t traverse_child, allrecord_child; + int traverse_down[2]; + int traverse_up[2]; + int allrecord_down[2]; + int allrecord_up[2]; + char c; + ssize_t nread, nwritten; + TDB_DATA key, data; + + key.dsize = strlen("hi"); + key.dptr = discard_const_p(uint8_t, "hi"); + data.dsize = strlen("world"); + data.dptr = discard_const_p(uint8_t, "world"); + + tdb = tdb_open_ex(name, 3, tdb_flags, + O_RDWR|O_CREAT, 0755, &taplogctx, NULL); + ok(tdb, "tdb_open_ex should succeed"); + + ret = tdb_store(tdb, key, data, TDB_INSERT); + ok(ret == 0, "tdb_store should succeed"); + + ret = pipe(traverse_down); + ok(ret == 0, "pipe should succeed"); + + ret = pipe(traverse_up); + ok(ret == 0, "pipe should succeed"); + + ret = pipe(allrecord_down); + ok(ret == 0, "pipe should succeed"); + + ret = pipe(allrecord_up); + ok(ret == 0, "pipe should succeed"); + + allrecord_child = fork(); + ok(allrecord_child != -1, "fork should succeed"); + + if (allrecord_child == 0) { + tdb_close(tdb); + close(traverse_up[0]); + close(traverse_up[1]); + close(traverse_down[0]); + close(traverse_down[1]); + close(allrecord_up[0]); + close(allrecord_down[1]); + do_allrecord_lock(name, tdb_flags, + allrecord_up[1], allrecord_down[0]); + exit(0); + } + close(allrecord_up[1]); + close(allrecord_down[0]); + + nread = read(allrecord_up[0], &c, sizeof(c)); + ok(nread == sizeof(c), "read should succeed"); + + traverse_child = fork(); + ok(traverse_child != -1, "fork should succeed"); + + if (traverse_child == 0) { + tdb_close(tdb); + close(traverse_up[0]); + close(traverse_down[1]); + close(allrecord_up[0]); + close(allrecord_up[1]); + close(allrecord_down[0]); + close(allrecord_down[1]); + do_traverse(name, tdb_flags, + traverse_up[1], traverse_down[0]); + exit(0); + } + close(traverse_up[1]); + close(traverse_down[0]); + + poll(NULL, 0, 1000); + + nwritten = write(allrecord_down[1], &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + nread = read(traverse_up[0], &c, sizeof(c)); + ok(nread == sizeof(c), "read should succeed"); + + nwritten = write(traverse_down[1], &c, sizeof(c)); + ok(nwritten == sizeof(c), "write should succeed"); + + nread = read(allrecord_up[0], &c, sizeof(c)); + ok(nread == sizeof(c), "ret should succeed"); + + close(traverse_up[0]); + close(traverse_down[1]); + close(allrecord_up[0]); + close(allrecord_down[1]); + diag("%s tests done", name); + return exit_status(); +} + +int main(int argc, char *argv[]) +{ + int ret; + bool mutex_support; + + mutex_support = tdb_runtime_check_for_robust_mutexes(); + + ret = do_tests("marklock-deadlock-fcntl.tdb", + TDB_CLEAR_IF_FIRST | + TDB_INCOMPATIBLE_HASH); + ok(ret == 0, "marklock-deadlock-fcntl.tdb tests should succeed"); + + if (!mutex_support) { + skip(1, "No robust mutex support, " + "skipping marklock-deadlock-mutex.tdb tests"); + return exit_status(); + } + + ret = do_tests("marklock-deadlock-mutex.tdb", + TDB_CLEAR_IF_FIRST | + TDB_MUTEX_LOCKING | + TDB_INCOMPATIBLE_HASH); + ok(ret == 0, "marklock-deadlock-mutex.tdb tests should succeed"); + + return exit_status(); +} diff --git a/lib/tdb/wscript b/lib/tdb/wscript index b283795..6d9791c 100644 --- a/lib/tdb/wscript +++ b/lib/tdb/wscript @@ -41,6 +41,7 @@ tdb1_unit_tests = [ 'run-wronghash-fail', 'run-zero-append', 'run-marklock-deadlock', + 'run-allrecord-traverse-deadlock', 'run-mutex-openflags2', 'run-mutex-trylock', 'run-mutex-allrecord-bench', -- 1.7.9.5