Skip to content

Commit e22f449

Browse files
axboegregkh
authored andcommitted
io_uring: defer eventfd signaling when queued from a wakeup handler
[ Upstream commit cd305ee ] io_req_local_work_add() signals the CQ ring eventfd inline when it is the one to push the first entry onto ->work_list. For DEFER_TASKRUN rings that add is frequently done from a waitqueue wakeup handler, where an arbitrary waitqueue lock is held. eventfd_signal_mask() only refuses to recurse when current->in_eventfd is set, but that bit is set by eventfd_signal_mask() itself. If the wake chain starts somewhere else, signal goes out inline and can feed back into epoll. Add IOU_F_TWQ_IN_WAKE, set it on the task_work add done from the three waitqueue callbacks, and use it to force io_eventfd_signal() down the existing call_rcu_hurry() deferral instead of signaling inline. Fixes: 21a091b ("io_uring: signal registered eventfd to process deferred task work") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/20260813133843.2933127-1-4ncienth@gmail.com/ Signed-off-by: Jens Axboe <axboe@kernel.dk> [ Relocated the `io_req_local_work_add()` hunk into io_uring.c and kept 6.18's `ctx->has_evfd` and open-coded `io_pollfree_wake()` context due to missing tw.c split and `io_poll_remove_waitq()`. ] Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 0bcec5d commit e22f449

7 files changed

Lines changed: 30 additions & 21 deletions

File tree

include/linux/io_uring_types.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ enum {
1717
* It's also ignored unless IORING_SETUP_DEFER_TASKRUN is set.
1818
*/
1919
IOU_F_TWQ_LAZY_WAKE = 1,
20+
21+
/*
22+
* Set when task_work is queued from a waitqueue wakeup handler, where
23+
* an arbitrary provider waitqueue lock is held. Signaling the CQ ring
24+
* eventfd inline from there can recurse back into that lock through
25+
* epoll, so the eventfd signal must be deferred.
26+
*/
27+
IOU_F_TWQ_IN_WAKE = 2,
2028
};
2129

2230
enum io_uring_cmd_flags {

io_uring/eventfd.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ static void io_eventfd_do_signal(struct rcu_head *rcu)
5050
/*
5151
* Returns true if the caller should put the ev_fd reference, false if not.
5252
*/
53-
static bool __io_eventfd_signal(struct io_ev_fd *ev_fd)
53+
static bool __io_eventfd_signal(struct io_ev_fd *ev_fd, bool defer)
5454
{
55-
if (eventfd_signal_allowed()) {
55+
if (!defer && eventfd_signal_allowed()) {
5656
eventfd_signal_mask(ev_fd->cq_ev_fd, EPOLL_URING_WAKE);
5757
return true;
5858
}
@@ -72,7 +72,7 @@ static bool io_eventfd_trigger(struct io_ev_fd *ev_fd)
7272
return !ev_fd->eventfd_async || io_wq_current_is_worker();
7373
}
7474

75-
void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event)
75+
void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event, bool defer)
7676
{
7777
bool skip = false;
7878
struct io_ev_fd *ev_fd;
@@ -112,7 +112,7 @@ void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event)
112112
spin_unlock(&ctx->completion_lock);
113113
}
114114

115-
if (skip || __io_eventfd_signal(ev_fd))
115+
if (skip || __io_eventfd_signal(ev_fd, defer))
116116
io_eventfd_put(ev_fd);
117117
}
118118

io_uring/eventfd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ int io_eventfd_register(struct io_ring_ctx *ctx, void __user *arg,
44
unsigned int eventfd_async);
55
int io_eventfd_unregister(struct io_ring_ctx *ctx);
66

7-
void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event);
7+
void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event, bool defer);

io_uring/futex.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ static void io_futex_wakev_fn(struct wake_q_head *wake_q, struct futex_q *q)
174174

175175
io_req_set_res(req, 0, 0);
176176
req->io_task_work.func = io_futexv_complete;
177-
io_req_task_work_add(req);
177+
__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
178178
}
179179

180180
int io_futexv_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
@@ -230,7 +230,7 @@ static void io_futex_wake_fn(struct wake_q_head *wake_q, struct futex_q *q)
230230

231231
io_req_set_res(req, 0, 0);
232232
req->io_task_work.func = io_futex_complete;
233-
io_req_task_work_add(req);
233+
__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
234234
}
235235

236236
int io_futexv_wait(struct io_kiocb *req, unsigned int issue_flags)

io_uring/io_uring.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ void __io_commit_cqring_flush(struct io_ring_ctx *ctx)
578578
if (ctx->off_timeout_used)
579579
io_flush_timeouts(ctx);
580580
if (ctx->has_evfd)
581-
io_eventfd_signal(ctx, true);
581+
io_eventfd_signal(ctx, true, false);
582582
}
583583

584584
static inline void __io_cq_lock(struct io_ring_ctx *ctx)
@@ -1312,7 +1312,7 @@ static void io_req_local_work_add(struct io_kiocb *req, unsigned flags)
13121312
if (!head) {
13131313
io_ctx_mark_taskrun(ctx);
13141314
if (ctx->has_evfd)
1315-
io_eventfd_signal(ctx, false);
1315+
io_eventfd_signal(ctx, false, flags & IOU_F_TWQ_IN_WAKE);
13161316
}
13171317

13181318
nr_wait = atomic_read(&ctx->cq_wait_nr);

io_uring/poll.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -190,24 +190,25 @@ enum {
190190
IOU_POLL_REQUEUE = 4,
191191
};
192192

193-
static void __io_poll_execute(struct io_kiocb *req, int mask)
193+
static void __io_poll_execute(struct io_kiocb *req, int mask, unsigned tw_flags)
194194
{
195-
unsigned flags = 0;
195+
unsigned flags = tw_flags;
196196

197197
io_req_set_res(req, mask, 0);
198198
req->io_task_work.func = io_poll_task_func;
199199

200200
trace_io_uring_task_add(req, mask);
201201

202202
if (!(req->flags & REQ_F_POLL_NO_LAZY))
203-
flags = IOU_F_TWQ_LAZY_WAKE;
203+
flags |= IOU_F_TWQ_LAZY_WAKE;
204204
__io_req_task_work_add(req, flags);
205205
}
206206

207-
static inline void io_poll_execute(struct io_kiocb *req, int res)
207+
static inline void io_poll_execute(struct io_kiocb *req, int res,
208+
unsigned tw_flags)
208209
{
209210
if (io_poll_get_ownership(req))
210-
__io_poll_execute(req, res);
211+
__io_poll_execute(req, res, tw_flags);
211212
}
212213

213214
/*
@@ -323,7 +324,7 @@ void io_poll_task_func(struct io_kiocb *req, io_tw_token_t tw)
323324
if (ret == IOU_POLL_NO_ACTION) {
324325
return;
325326
} else if (ret == IOU_POLL_REQUEUE) {
326-
__io_poll_execute(req, 0);
327+
__io_poll_execute(req, 0, 0);
327328
return;
328329
}
329330
io_poll_remove_entries(req);
@@ -362,7 +363,7 @@ static void io_poll_cancel_req(struct io_kiocb *req)
362363
{
363364
io_poll_mark_cancelled(req);
364365
/* kick tw, which should complete the request */
365-
io_poll_execute(req, 0);
366+
io_poll_execute(req, 0, 0);
366367
}
367368

368369
#define IO_ASYNC_POLL_COMMON (EPOLLONESHOT | EPOLLPRI)
@@ -371,7 +372,7 @@ static __cold int io_pollfree_wake(struct io_kiocb *req, struct io_poll *poll)
371372
{
372373
io_poll_mark_cancelled(req);
373374
/* we have to kick tw in case it's not already */
374-
io_poll_execute(req, 0);
375+
io_poll_execute(req, 0, IOU_F_TWQ_IN_WAKE);
375376

376377
/*
377378
* If the waitqueue is being freed early but someone is already
@@ -426,7 +427,7 @@ static int io_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync,
426427
else
427428
req->flags &= ~REQ_F_SINGLE_POLL;
428429
}
429-
__io_poll_execute(req, mask);
430+
__io_poll_execute(req, mask, IOU_F_TWQ_IN_WAKE);
430431
}
431432
return 1;
432433
}
@@ -614,7 +615,7 @@ static int __io_arm_poll_handler(struct io_kiocb *req,
614615

615616
if (mask && (poll->events & EPOLLET) &&
616617
io_poll_can_finish_inline(req, ipt)) {
617-
__io_poll_execute(req, mask);
618+
__io_poll_execute(req, mask, 0);
618619
return 0;
619620
}
620621
io_napi_add(req);
@@ -625,7 +626,7 @@ static int __io_arm_poll_handler(struct io_kiocb *req,
625626
* poll was waken up, queue up a tw, it'll deal with it.
626627
*/
627628
if (atomic_cmpxchg(&req->poll_refs, 1, 0) != 1)
628-
__io_poll_execute(req, 0);
629+
__io_poll_execute(req, 0, 0);
629630
}
630631
return 0;
631632
}

io_uring/waitid.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ static int io_waitid_wait(struct wait_queue_entry *wait, unsigned mode,
237237
return 1;
238238

239239
req->io_task_work.func = io_waitid_cb;
240-
io_req_task_work_add(req);
240+
__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
241241
return 1;
242242
}
243243

0 commit comments

Comments
 (0)