Re: [BUG] RCU hang with io_uring nvme polling
Ben Carey <[email protected]>
| Newsgroups | org.kernel.vger.io-uring,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CA+KFGSpgN7DChCfMK4itc39MB9ubxacbY3sWTByOkG58umvPkQ@mail.gmail.com> |
On Fri, Jun 26, 2026 at 12:33 PM Keith Busch <[email protected]> wrote: > The test has 1 polling queue with 2 jobs dispatching. One of the job's > polled the completions for both. The other job is polling for no reason > at all with nothing outstanding. The only thing that can break us out of > that loop now is need_resched(), but that appears to never return true. Inspired by this I tried to find a place where one thread polls on a job that's already finished. I found that a race to io_check_iopoll causes one thread to enter the polling loop when another has already finished on it. Putting io_check_iopoll behind a spinlock seems to fix it, though I imagine a more elegant fix is out there (reusing a different lock, not using expensive locks, a smarter place to check for racing, etc.) The diff is as follows: diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h index 214fdbd49..e4f76fa74 100644 --- a/include/linux/io_uring_types.h +++ b/include/linux/io_uring_types.h @@ -406,6 +406,7 @@ struct io_ring_ctx { } ____cacheline_aligned_in_smp; spinlock_t completion_lock; + spinlock_t cq_poll_lock; struct list_head cq_overflow_list; diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index 4d7bcbb97..b65e2b11a 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -272,6 +272,7 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p) init_waitqueue_head(&ctx->cq_wait); init_waitqueue_head(&ctx->poll_wq); spin_lock_init(&ctx->completion_lock); + spin_lock_init(&ctx->cq_poll_lock); raw_spin_lock_init(&ctx->timeout_lock); INIT_LIST_HEAD(&ctx->iopoll_list); INIT_LIST_HEAD(&ctx->defer_list); @@ -1243,7 +1244,13 @@ static int io_iopoll_check(struct io_ring_ctx *ctx, unsigned int min_events) if (tail != ctx->cached_cq_tail || list_empty(&ctx->iopoll_list)) break; } - ret = io_do_iopoll(ctx, !min_events); + if (spin_trylock(&ctx->cq_poll_lock)) { + ret = io_do_iopoll(ctx, !min_events); + spin_unlock(&ctx->cq_poll_lock); + } else { + ret = 0; + } + if (unlikely(ret < 0)) return ret; Best wishes, Ben Carey