io_uring/rw: don't reap io-wq IOPOLL completions while io-wq has a reference

IOPOLL requests issued from io-wq are reaped by io_do_iopoll() on the
submitter, with the same window as the task_work completions: the CQE
is posted while the worker may still hold its reference, and the file
is only dropped once it does.

Leave such a request on the iopoll list until the worker has dropped
its reference, the next pass then reaps it and the put from
io_free_batch_list() is the last one. Also move the reference handling
for the task_work case into a helper.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Jens Axboe 2026-09-09 13:22:28 -06:00
parent 5530a5102e
commit ecc4c37627
3 changed files with 33 additions and 15 deletions

View File

@ -1291,21 +1291,8 @@ void io_req_task_complete(struct io_tw_req tw_req, io_tw_token_t tw)
{
struct io_kiocb *req = tw_req.req;
/*
* io-wq may still hold a reference if the issue completed async.
* Defer completion to the last put, so that file drop and CQE
* visibility are ordered. The last reference stays for the free
* path to put, a linked timeout may still look at the request.
*/
if ((req->flags & REQ_F_REFCOUNT) && !(req->flags & REQ_F_REISSUE) &&
atomic_read(&req->refs) != 1) {
if (!req_ref_put_and_test(req))
return;
/* the other put raced us, ours was the last after all */
atomic_set(&req->refs, 1);
}
io_req_complete_defer(req);
if (io_req_complete_ready(req))
io_req_complete_defer(req);
}
/*

View File

@ -56,6 +56,33 @@ static inline void __io_req_set_refcount(struct io_kiocb *req, int nr)
}
}
/*
* io-wq may still hold a reference if the issue completed async. If so, the
* last put completes the request, so that file drop and CQE visibility are
* ordered. The last reference stays for the free path to put, a linked
* timeout may still look at the request until then.
*/
static inline bool io_req_complete_ready(struct io_kiocb *req)
{
if (!(req->flags & REQ_F_REFCOUNT) || (req->flags & REQ_F_REISSUE))
return true;
if (atomic_read(&req->refs) == 1)
return true;
if (!req_ref_put_and_test(req))
return false;
/* the other put raced us, ours was the last after all */
atomic_set(&req->refs, 1);
return true;
}
/* io-wq still holds a reference, the request can't be completed yet */
static inline bool io_req_shared(struct io_kiocb *req)
{
if (!(req->flags & REQ_F_REFCOUNT) || (req->flags & REQ_F_REISSUE))
return false;
return atomic_read(&req->refs) > 1;
}
static inline void io_req_set_refcount(struct io_kiocb *req)
{
__io_req_set_refcount(req, 1);

View File

@ -16,6 +16,7 @@
#include "filetable.h"
#include "io_uring.h"
#include "refs.h"
#include "opdef.h"
#include "kbuf.h"
#include "alloc_cache.h"
@ -1380,6 +1381,9 @@ int io_do_iopoll(struct io_ring_ctx *ctx, bool force_nonspin)
/* order with io_complete_rw_iopoll(), e.g. ->result updates */
if (!smp_load_acquire(&req->iopoll_completed))
continue;
/* io-wq still has a reference, reap it on the next pass */
if (io_req_shared(req))
continue;
list_del(&req->iopoll_node);
wq_list_add_tail(&req->comp_list, &ctx->submit_state.compl_reqs);
nr_events++;