io_uring/net: don't retry a non-blocking connect() that fails with -EAGAIN

IORING_OP_CONNECT on an O_NONBLOCK AF_UNIX stream socket retries a
connect that fails with -EAGAIN because the listener's backlog is full.
Polling the client socket doesn't help: an unconnected stream socket
reports EPOLLOUT and EPOLLHUP right away, before the backlog drains. So
io_uring retries the connect until the apoll retry limit is hit, then
punts the request to io-wq. With a full backlog, io_connect() runs 128
times in the submitting task and once more in a new io-wq worker. That
worker stays in the task's thread group, so a later
unshare(CLONE_NEWUSER) fails with -EINVAL.

None of this changes the result. __sys_connect_file() adds the socket's
own O_NONBLOCK to every attempt, so the attempt from io-wq fails with
-EAGAIN as well. That -EAGAIN becomes the CQE result, and connect(2)
returns the same -EAGAIN.

Let's complete the request with -EAGAIN right away when the socket has
O_NONBLOCK set, like connect(2) does. io_accept() already completes
-EAGAIN this way for IORING_ACCEPT_DONTWAIT. -EINPROGRESS still waits
for the connection to complete. A blocking socket still ends up in
io-wq, where the connect sleeps until the listener accepts.

Fixes: f8e85cf255 ("io_uring: add support for IORING_OP_CONNECT")
Signed-off-by: Daan De Meyer <daan@amutable.com>
Link: https://patch.msgid.link/20261027-b4-io-uring-connect-eagain-v1-1-f4e3aaaa49ee@amutable.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Daan De Meyer 2026-10-27 16:09:37 +01:00 committed by Jens Axboe
parent 4a93427e99
commit 71537ed811

View File

@ -1878,6 +1878,17 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
if (connect->seen_econnaborted)
goto out;
connect->seen_econnaborted = true;
} else if (req->file->f_flags & O_NONBLOCK) {
/*
* __sys_connect_file() adds O_NONBLOCK from the socket
* to every attempt, so a retry from io-wq fails with
* -EAGAIN as well. A poll on the socket doesn't wait
* until a retry can succeed. For example, an AF_UNIX
* client reports EPOLLOUT right away while the
* listener's backlog is full. Complete the request
* with -EAGAIN, like connect(2) does.
*/
goto out;
}
return -EAGAIN;
}