Skip to content

Commit 4e86a2c

Browse files
Hao Xuaxboe
authored andcommitted
io_uring: implement multishot mode for accept
Refactor io_accept() to support multishot mode. theoretical analysis: 1) when connections come in fast - singleshot: add accept sqe(userspace) --> accept inline ^ | |-----------------| - multishot: add accept sqe(userspace) --> accept inline ^ | |--*--| we do accept repeatedly in * place until get EAGAIN 2) when connections come in at a low pressure similar thing like 1), we reduce a lot of userspace-kernel context switch and useless vfs_poll() tests: Did some tests, which goes in this way: server client(multiple) accept connect read write write read close close Basically, raise up a number of clients(on same machine with server) to connect to the server, and then write some data to it, the server will write those data back to the client after it receives them, and then close the connection after write return. Then the client will read the data and then close the connection. Here I test 10000 clients connect one server, data size 128 bytes. And each client has a go routine for it, so they come to the server in short time. test 20 times before/after this patchset, time spent:(unit cycle, which is the return value of clock()) before: 1930136+1940725+1907981+1947601+1923812+1928226+1911087+1905897+1941075 +1934374+1906614+1912504+1949110+1908790+1909951+1941672+1969525+1934984 +1934226+1914385)/20.0 = 1927633.75 after: 1858905+1917104+1895455+1963963+1892706+1889208+1874175+1904753+1874112 +1874985+1882706+1884642+1864694+1906508+1916150+1924250+1869060+1889506 +1871324+1940803)/20.0 = 1894750.45 (1927633.75 - 1894750.45) / 1927633.75 = 1.65% Signed-off-by: Hao Xu <howeyxu@tencent.com> Link: https://lore.kernel.org/r/20220514142046.58072-5-haoxu.linux@gmail.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent dbc2564 commit 4e86a2c

File tree

1 file changed

+48
-6
lines changed

1 file changed

+48
-6
lines changed

fs/io_uring.c

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,7 @@ static const struct io_op_def io_op_defs[] = {
11481148
.unbound_nonreg_file = 1,
11491149
.pollin = 1,
11501150
.poll_exclusive = 1,
1151+
.ioprio = 1, /* used for flags */
11511152
},
11521153
[IORING_OP_ASYNC_CANCEL] = {
11531154
.audit_skip = 1,
@@ -5762,6 +5763,7 @@ static int io_recv(struct io_kiocb *req, unsigned int issue_flags)
57625763
static int io_accept_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
57635764
{
57645765
struct io_accept *accept = &req->accept;
5766+
unsigned flags;
57655767

57665768
if (sqe->len || sqe->buf_index)
57675769
return -EINVAL;
@@ -5770,26 +5772,38 @@ static int io_accept_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
57705772
accept->addr_len = u64_to_user_ptr(READ_ONCE(sqe->addr2));
57715773
accept->flags = READ_ONCE(sqe->accept_flags);
57725774
accept->nofile = rlimit(RLIMIT_NOFILE);
5775+
flags = READ_ONCE(sqe->ioprio);
5776+
if (flags & ~IORING_ACCEPT_MULTISHOT)
5777+
return -EINVAL;
57735778

57745779
accept->file_slot = READ_ONCE(sqe->file_index);
5775-
if (accept->file_slot && (accept->flags & SOCK_CLOEXEC))
5776-
return -EINVAL;
5780+
if (accept->file_slot) {
5781+
if (accept->flags & SOCK_CLOEXEC)
5782+
return -EINVAL;
5783+
if (flags & IORING_ACCEPT_MULTISHOT &&
5784+
accept->file_slot != IORING_FILE_INDEX_ALLOC)
5785+
return -EINVAL;
5786+
}
57775787
if (accept->flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
57785788
return -EINVAL;
57795789
if (SOCK_NONBLOCK != O_NONBLOCK && (accept->flags & SOCK_NONBLOCK))
57805790
accept->flags = (accept->flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
5791+
if (flags & IORING_ACCEPT_MULTISHOT)
5792+
req->flags |= REQ_F_APOLL_MULTISHOT;
57815793
return 0;
57825794
}
57835795

57845796
static int io_accept(struct io_kiocb *req, unsigned int issue_flags)
57855797
{
5798+
struct io_ring_ctx *ctx = req->ctx;
57865799
struct io_accept *accept = &req->accept;
57875800
bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
57885801
unsigned int file_flags = force_nonblock ? O_NONBLOCK : 0;
57895802
bool fixed = !!accept->file_slot;
57905803
struct file *file;
57915804
int ret, fd;
57925805

5806+
retry:
57935807
if (!fixed) {
57945808
fd = __get_unused_fd_flags(accept->flags, accept->nofile);
57955809
if (unlikely(fd < 0))
@@ -5801,8 +5815,17 @@ static int io_accept(struct io_kiocb *req, unsigned int issue_flags)
58015815
if (!fixed)
58025816
put_unused_fd(fd);
58035817
ret = PTR_ERR(file);
5804-
if (ret == -EAGAIN && force_nonblock)
5805-
return -EAGAIN;
5818+
if (ret == -EAGAIN && force_nonblock) {
5819+
/*
5820+
* if it's multishot and polled, we don't need to
5821+
* return EAGAIN to arm the poll infra since it
5822+
* has already been done
5823+
*/
5824+
if ((req->flags & IO_APOLL_MULTI_POLLED) ==
5825+
IO_APOLL_MULTI_POLLED)
5826+
ret = 0;
5827+
return ret;
5828+
}
58065829
if (ret == -ERESTARTSYS)
58075830
ret = -EINTR;
58085831
req_set_fail(req);
@@ -5813,8 +5836,27 @@ static int io_accept(struct io_kiocb *req, unsigned int issue_flags)
58135836
ret = io_fixed_fd_install(req, issue_flags, file,
58145837
accept->file_slot);
58155838
}
5816-
__io_req_complete(req, issue_flags, ret, 0);
5817-
return 0;
5839+
5840+
if (!(req->flags & REQ_F_APOLL_MULTISHOT)) {
5841+
__io_req_complete(req, issue_flags, ret, 0);
5842+
return 0;
5843+
}
5844+
if (ret >= 0) {
5845+
bool filled;
5846+
5847+
spin_lock(&ctx->completion_lock);
5848+
filled = io_fill_cqe_aux(ctx, req->cqe.user_data, ret,
5849+
IORING_CQE_F_MORE);
5850+
io_commit_cqring(ctx);
5851+
spin_unlock(&ctx->completion_lock);
5852+
if (filled) {
5853+
io_cqring_ev_posted(ctx);
5854+
goto retry;
5855+
}
5856+
ret = -ECANCELED;
5857+
}
5858+
5859+
return ret;
58185860
}
58195861

58205862
static int io_connect_prep_async(struct io_kiocb *req)

0 commit comments

Comments
 (0)