From dbda265605243916af82993ee7ef1e656f0520de Mon Sep 17 00:00:00 2001 From: Arsenova <7z0423arsenova@proton.me> Date: Sat, 24 Oct 2026 16:27:53 +0800 Subject: [PATCH] test/read-before-exit: wait for deferred ring frees on -ENOMEM Without CAP_IPC_LOCK, ring memory is accounted against RLIMIT_MEMLOCK. Every ring in this test exits with two timerfd reads still pending, so the kernel frees it asynchronously from the exit work. The test creates over 2000 rings back to back, faster than those frees complete, and as a regular user it runs into the limit: with the default 8 MiB limit, ring setup fails with -ENOMEM after about 1000 rings: queue_init: Cannot allocate memory Test IOPOLL failed loop 1 The test passes when run as root, since root rings are not accounted. When ring setup fails with -ENOMEM as a regular user, sleep and retry for up to a second to let the deferred frees catch up. Also print the loop index rather than the return value when the IOPOLL loop fails. Link: https://github.com/axboe/liburing/issues/1611 Signed-off-by: Arsenova <7z0423arsenova@proton.me> --- test/read-before-exit.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/test/read-before-exit.c b/test/read-before-exit.c index a539352ea..b1db2d008 100644 --- a/test/read-before-exit.c +++ b/test/read-before-exit.c @@ -57,16 +57,44 @@ static void *submit(void *data) return NULL; } +/* + * Without CAP_IPC_LOCK, ring memory counts against RLIMIT_MEMLOCK. Every + * ring here exits with requests still pending, which makes the kernel free + * it asynchronously, and the loops below create rings faster than those + * frees complete. Give them time to catch up rather than failing on + * -ENOMEM. + */ +static enum t_setup_ret create_ring(struct io_uring *ring, int flags) +{ + struct io_uring_params params; + int i, ret; + + for (i = 0; i < 100; i++) { + memset(¶ms, 0, sizeof(params)); + params.flags = flags; + ret = io_uring_queue_init_params(8, ring, ¶ms); + if (ret != -ENOMEM || !geteuid()) + break; + usleep(10000); + } + if (!ret) + return T_SETUP_OK; + + /* let the helper handle skips and report the error */ + memset(¶ms, 0, sizeof(params)); + params.flags = flags; + return t_create_ring_params(8, ring, ¶ms); +} + static int test(int flags) { - struct io_uring_params params = { .flags = flags, }; struct io_uring ring; struct data d = { .ring = &ring, }; pthread_t thread; void *res; int ret; - ret = t_create_ring_params(8, &ring, ¶ms); + ret = create_ring(&ring, flags); if (ret == T_SETUP_SKIP) return 0; else if (ret != T_SETUP_OK) @@ -110,7 +138,7 @@ int main(int argc, char *argv[]) for (i = 0; i < 1000; i++) { ret = test(IORING_SETUP_IOPOLL); if (ret) { - fprintf(stderr, "Test IOPOLL failed loop %d\n", ret); + fprintf(stderr, "Test IOPOLL failed loop %d\n", i); return ret; } if (no_iopoll)