[PATCH 0/2] io_uring/cancel: cancel all poll matches in one pass

From: Shang En Sim

Date: Fri Oct 30 2026 - 01:04:14 EST


IORING_OP_ASYNC_CANCEL with IORING_ASYNC_CANCEL_ALL cancels poll
requests one at a time, and every lookup starts over at the beginning
of the poll hash. Cancelled requests stay hashed until their task work
runs, so each lookup also walks past the ones already cancelled. One
cancel that matches K of N pending polls costs about K*N/2 visits in a
single io_uring_enter(), with uring_lock held.

Patch 2 makes poll cancel all its matches in one pass, as futex and
waitid already do through io_cancel_remove(). Patch 1 is a prerequisite
and a fix on its own: __io_async_cancel() counts each io_try_cancel()
call as one, so CANCEL_ALL on futex or waitid requests reports 1 no
matter how many it cancelled. Patch 2 needs it to keep the poll count
right.

Reproducer, 80000 polls on one eventfd and a single CANCEL_FD|ALL:

#include <liburing.h>
#include <poll.h>
#include <sys/eventfd.h>

int main(void)
{
struct io_uring ring;
int fd = eventfd(0, 0), i;

io_uring_queue_init(4096, &ring, 0);
for (i = 0; i < 80000; i++) {
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);

if (!sqe) {
io_uring_submit(&ring);
sqe = io_uring_get_sqe(&ring);
}
io_uring_prep_poll_add(sqe, fd, POLLIN);
}
io_uring_prep_cancel_fd(io_uring_get_sqe(&ring), fd,
IORING_ASYNC_CANCEL_ALL);
io_uring_submit(&ring);
return 0;
}

In a 4 vCPU KVM guest, "time ./repro" takes 17.06s of system time on
next-20261029 (lazy preemption) and 0.02s with these patches. On
v6.18.59 with voluntary preemption it takes 36.24s, and the soft lockup
watchdog fires during the cancel.

Testing, on next-20261029 and v6.18.59: liburing's cancel tests
(poll-cancel-all, poll-cancel, poll-cancel-ton, cancel-fd-userdata,
io-cancel, sync-cancel, cancel-race, futex, waitid), plus three new
tests that I'll send to liburing separately: a futex CANCEL_ALL count
(fails before patch 1), and poll CANCEL_ALL by user_data alone and with
1000 polls on one fd. All pass, except waitid's pidfd test on
v6.18.59, which fails with and without these patches. Not tested:
lockdep, KASAN, and SQPOLL or DEFER_TASKRUN rings for the new cases.


Shang En Sim (2):
io_uring/cancel: count all requests that a CANCEL_ALL cancels
io_uring/poll: cancel all matching requests in one pass

io_uring/cancel.c | 3 ++-
io_uring/poll.c | 39 +++++++++++++++++++++++++++++++++++++--
2 files changed, 39 insertions(+), 3 deletions(-)


base-commit: 70866cfa169d5ee149364267289e71d50f9a7ae9
--
2.56.0