Skip to content

Commit 4313c8d

Browse files
committed
io_uring/eventfd: move to more idiomatic RCU free usage
In some ways, it just "happens to work" currently with using the ops field for both the free and signaling bit. But it depends on ordering of operations in terms of freeing and signaling. Clean it up and use the usual refs == 0 under RCU read side lock to determine if the ev_fd is still valid, and use the reference to gate the freeing as well. Fixes: 21a091b ("io_uring: signal registered eventfd to process deferred task work") Signed-off-by: Jens Axboe <[email protected]>
1 parent 4faf204 commit 4313c8d

File tree

3 files changed

+31
-28
lines changed

3 files changed

+31
-28
lines changed

io_uring/io_uring.c

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -541,29 +541,33 @@ static __cold void io_queue_deferred(struct io_ring_ctx *ctx)
541541
}
542542
}
543543

544-
void io_eventfd_ops(struct rcu_head *rcu)
544+
void io_eventfd_free(struct rcu_head *rcu)
545545
{
546546
struct io_ev_fd *ev_fd = container_of(rcu, struct io_ev_fd, rcu);
547-
int ops = atomic_xchg(&ev_fd->ops, 0);
548547

549-
if (ops & BIT(IO_EVENTFD_OP_SIGNAL_BIT))
550-
eventfd_signal_mask(ev_fd->cq_ev_fd, EPOLL_URING_WAKE);
548+
eventfd_ctx_put(ev_fd->cq_ev_fd);
549+
kfree(ev_fd);
550+
}
551551

552-
/* IO_EVENTFD_OP_FREE_BIT may not be set here depending on callback
553-
* ordering in a race but if references are 0 we know we have to free
554-
* it regardless.
555-
*/
556-
if (atomic_dec_and_test(&ev_fd->refs)) {
557-
eventfd_ctx_put(ev_fd->cq_ev_fd);
558-
kfree(ev_fd);
559-
}
552+
void io_eventfd_do_signal(struct rcu_head *rcu)
553+
{
554+
struct io_ev_fd *ev_fd = container_of(rcu, struct io_ev_fd, rcu);
555+
556+
eventfd_signal_mask(ev_fd->cq_ev_fd, EPOLL_URING_WAKE);
557+
558+
if (atomic_dec_and_test(&ev_fd->refs))
559+
io_eventfd_free(rcu);
560560
}
561561

562562
static void io_eventfd_signal(struct io_ring_ctx *ctx)
563563
{
564564
struct io_ev_fd *ev_fd = NULL;
565565

566-
rcu_read_lock();
566+
if (READ_ONCE(ctx->rings->cq_flags) & IORING_CQ_EVENTFD_DISABLED)
567+
return;
568+
569+
guard(rcu)();
570+
567571
/*
568572
* rcu_dereference ctx->io_ev_fd once and use it for both for checking
569573
* and eventfd_signal
@@ -576,24 +580,23 @@ static void io_eventfd_signal(struct io_ring_ctx *ctx)
576580
* the function and rcu_read_lock.
577581
*/
578582
if (unlikely(!ev_fd))
579-
goto out;
580-
if (READ_ONCE(ctx->rings->cq_flags) & IORING_CQ_EVENTFD_DISABLED)
581-
goto out;
583+
return;
584+
if (!atomic_inc_not_zero(&ev_fd->refs))
585+
return;
582586
if (ev_fd->eventfd_async && !io_wq_current_is_worker())
583587
goto out;
584588

585589
if (likely(eventfd_signal_allowed())) {
586590
eventfd_signal_mask(ev_fd->cq_ev_fd, EPOLL_URING_WAKE);
587591
} else {
588-
atomic_inc(&ev_fd->refs);
589-
if (!atomic_fetch_or(BIT(IO_EVENTFD_OP_SIGNAL_BIT), &ev_fd->ops))
590-
call_rcu_hurry(&ev_fd->rcu, io_eventfd_ops);
591-
else
592-
atomic_dec(&ev_fd->refs);
592+
if (!atomic_fetch_or(BIT(IO_EVENTFD_OP_SIGNAL_BIT), &ev_fd->ops)) {
593+
call_rcu_hurry(&ev_fd->rcu, io_eventfd_do_signal);
594+
return;
595+
}
593596
}
594-
595597
out:
596-
rcu_read_unlock();
598+
if (atomic_dec_and_test(&ev_fd->refs))
599+
call_rcu(&ev_fd->rcu, io_eventfd_free);
597600
}
598601

599602
static void io_eventfd_flush_signal(struct io_ring_ctx *ctx)

io_uring/io_uring.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,10 @@ bool io_match_task_safe(struct io_kiocb *head, struct task_struct *task,
106106

107107
enum {
108108
IO_EVENTFD_OP_SIGNAL_BIT,
109-
IO_EVENTFD_OP_FREE_BIT,
110109
};
111110

112-
void io_eventfd_ops(struct rcu_head *rcu);
111+
void io_eventfd_do_signal(struct rcu_head *rcu);
112+
void io_eventfd_free(struct rcu_head *rcu);
113113
void io_activate_pollwq(struct io_ring_ctx *ctx);
114114

115115
static inline void io_lockdep_assert_cq_locked(struct io_ring_ctx *ctx)

io_uring/register.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ static int io_eventfd_register(struct io_ring_ctx *ctx, void __user *arg,
6363

6464
ev_fd->eventfd_async = eventfd_async;
6565
ctx->has_evfd = true;
66-
rcu_assign_pointer(ctx->io_ev_fd, ev_fd);
6766
atomic_set(&ev_fd->refs, 1);
6867
atomic_set(&ev_fd->ops, 0);
68+
rcu_assign_pointer(ctx->io_ev_fd, ev_fd);
6969
return 0;
7070
}
7171

@@ -78,8 +78,8 @@ int io_eventfd_unregister(struct io_ring_ctx *ctx)
7878
if (ev_fd) {
7979
ctx->has_evfd = false;
8080
rcu_assign_pointer(ctx->io_ev_fd, NULL);
81-
if (!atomic_fetch_or(BIT(IO_EVENTFD_OP_FREE_BIT), &ev_fd->ops))
82-
call_rcu(&ev_fd->rcu, io_eventfd_ops);
81+
if (atomic_dec_and_test(&ev_fd->refs))
82+
call_rcu(&ev_fd->rcu, io_eventfd_free);
8383
return 0;
8484
}
8585

0 commit comments

Comments
 (0)