Signed-off-by: Bijan Mottahedeh <bijan.mottahedeh@xxxxxxxxxx> --- test/buffer-share.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/test/buffer-share.c b/test/buffer-share.c index cbfbfb2..2997333 100644 --- a/test/buffer-share.c +++ b/test/buffer-share.c @@ -72,6 +72,7 @@ unsigned long shm_sz = SZ_2M; int no_read; int warned; +int deadlock; int private; int verbose; @@ -1095,6 +1096,82 @@ test_exit(int expect) exit(ret); } + +/* + * Test for deadlock scenario raised by Pavel. + * Test hangs but is interruptible. + * + * [Pavel's comments] + * + * Ok, now the original io_uring instance will wait until the attached + * once get rid of their references. That's a versatile ground to have + * in kernel deadlocks. + * + * task1: uring1 = create() + * task2: uring2 = create() + * task1: uring3 = create(share=uring2); + * task2: uring4 = create(share=uring1); + * + * task1: io_sqe_buffers_unregister(uring1) + * task2: io_sqe_buffers_unregister(uring2) + * + * If I skimmed through the code right, that should hang unkillably. + */ +static int test_deadlock(void) +{ + int i, pid, ret, fd0, fd1; + struct io_uring rings[4]; + struct io_uring_params p; + + for (i = 0; i < 2; i++) { + memset(&p, 0, sizeof(p)); + p.flags = IORING_SETUP_SHARE_BUF; + ret = io_uring_queue_init_params(1, &rings[i], &p); + if (ret) { + verror("queue_init share"); + return ret; + } + } + + fd0 = rings[0].ring_fd; + fd1 = rings[1].ring_fd; + + pid = fork(); + + memset(&p, 0, sizeof(p)); + p.flags = IORING_SETUP_ATTACH_BUF; + + if (pid) { + p.wq_fd = fd1; + ret = io_uring_queue_init_params(1, &rings[2], &p); + } else { + p.wq_fd = fd0; + ret = io_uring_queue_init_params(1, &rings[3], &p); + } + + if (ret) { + verror("queue_init attach"); + return ret; + } + + vinfo(V1, "unregister\n"); + + if (pid) { + close(fd1); + ret = io_uring_unregister_buffers(&rings[0]); + } else { + close(fd0); + ret = io_uring_unregister_buffers(&rings[1]); + } + + vinfo(V1, "unregister done\n"); + + if (ret) + verror("unregister"); + + return ret; +} + /* * main() * -> shm_create() @@ -1122,8 +1199,11 @@ main(int argc, char *argv[]) int shmid, pid, ret = 0; char c; - while ((c = getopt(argc, argv, "ps:v:")) != -1) + while ((c = getopt(argc, argv, "dps:v:")) != -1) switch (c) { + case 'd': + deadlock++; + break; case 'p': private++; break; @@ -1146,6 +1226,9 @@ main(int argc, char *argv[]) if (!nids || nids > MAXID) exit(1); + if (deadlock) + exit(test_deadlock()); + shmstat = shmstat_create(); if (shmstat == MAP_FAILED) exit(1); -- 1.8.3.1