| From 3cc7fdb9f90a25ae92250bf9e6cf3b9556b230e9 Mon Sep 17 00:00:00 2001 |
| From: Pavel Begunkov <asml.silence@gmail.com> |
| Date: Sun, 9 Jan 2022 00:53:22 +0000 |
| Subject: io_uring: fix not released cached task refs |
| |
| From: Pavel Begunkov <asml.silence@gmail.com> |
| |
| commit 3cc7fdb9f90a25ae92250bf9e6cf3b9556b230e9 upstream. |
| |
| tctx_task_work() may get run after io_uring cancellation and so there |
| will be no one to put cached in tctx task refs that may have been added |
| back by tw handlers using inline completion infra, Call |
| io_uring_drop_tctx_refs() at the end of the main tw handler to release |
| them. |
| |
| Cc: stable@vger.kernel.org # 5.15+ |
| Reported-by: Lukas Bulwahn <lukas.bulwahn@gmail.com> |
| Fixes: e98e49b2bbf7 ("io_uring: extend task put optimisations") |
| Signed-off-by: Pavel Begunkov <asml.silence@gmail.com> |
| Link: https://lore.kernel.org/r/69f226b35fbdb996ab799a8bbc1c06bf634ccec1.1641688805.git.asml.silence@gmail.com |
| Signed-off-by: Jens Axboe <axboe@kernel.dk> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/io_uring.c | 34 +++++++++++++++++++++------------- |
| 1 file changed, 21 insertions(+), 13 deletions(-) |
| |
| --- a/fs/io_uring.c |
| +++ b/fs/io_uring.c |
| @@ -1830,6 +1830,18 @@ static inline void io_get_task_refs(int |
| io_task_refs_refill(tctx); |
| } |
| |
| +static __cold void io_uring_drop_tctx_refs(struct task_struct *task) |
| +{ |
| + struct io_uring_task *tctx = task->io_uring; |
| + unsigned int refs = tctx->cached_refs; |
| + |
| + if (refs) { |
| + tctx->cached_refs = 0; |
| + percpu_counter_sub(&tctx->inflight, refs); |
| + put_task_struct_many(task, refs); |
| + } |
| +} |
| + |
| static bool io_cqring_event_overflow(struct io_ring_ctx *ctx, u64 user_data, |
| s32 res, u32 cflags) |
| { |
| @@ -2250,6 +2262,10 @@ static void tctx_task_work(struct callba |
| } |
| |
| ctx_flush_and_put(ctx, &locked); |
| + |
| + /* relaxed read is enough as only the task itself sets ->in_idle */ |
| + if (unlikely(atomic_read(&tctx->in_idle))) |
| + io_uring_drop_tctx_refs(current); |
| } |
| |
| static void io_req_task_work_add(struct io_kiocb *req) |
| @@ -9818,18 +9834,6 @@ static s64 tctx_inflight(struct io_uring |
| return percpu_counter_sum(&tctx->inflight); |
| } |
| |
| -static __cold void io_uring_drop_tctx_refs(struct task_struct *task) |
| -{ |
| - struct io_uring_task *tctx = task->io_uring; |
| - unsigned int refs = tctx->cached_refs; |
| - |
| - if (refs) { |
| - tctx->cached_refs = 0; |
| - percpu_counter_sub(&tctx->inflight, refs); |
| - put_task_struct_many(task, refs); |
| - } |
| -} |
| - |
| /* |
| * Find any io_uring ctx that this task has registered or done IO on, and cancel |
| * requests. @sqd should be not-null IFF it's an SQPOLL thread cancellation. |
| @@ -9887,10 +9891,14 @@ static __cold void io_uring_cancel_gener |
| schedule(); |
| finish_wait(&tctx->wait, &wait); |
| } while (1); |
| - atomic_dec(&tctx->in_idle); |
| |
| io_uring_clean_tctx(tctx); |
| if (cancel_all) { |
| + /* |
| + * We shouldn't run task_works after cancel, so just leave |
| + * ->in_idle set for normal exit. |
| + */ |
| + atomic_dec(&tctx->in_idle); |
| /* for exec all current's requests should be gone, kill tctx */ |
| __io_uring_free(current); |
| } |