Skip to content

Commit 59b735a

Browse files
Olivier Langloisaxboe
authored andcommitted
io_uring: reduce latency by reissueing the operation
It is quite frequent that when an operation fails and returns EAGAIN, the data becomes available between that failure and the call to vfs_poll() done by io_arm_poll_handler(). Detecting the situation and reissuing the operation is much faster than going ahead and push the operation to the io-wq. Performance improvement testing has been performed with: Single thread, 1 TCP connection receiving a 5 Mbps stream, no sqpoll. 4 measurements have been taken: 1. The time it takes to process a read request when data is already available 2. The time it takes to process by calling twice io_issue_sqe() after vfs_poll() indicated that data was available 3. The time it takes to execute io_queue_async_work() 4. The time it takes to complete a read request asynchronously 2.25% of all the read operations did use the new path. ready data (baseline) avg 3657.94182918628 min 580 max 20098 stddev 1213.15975908162 reissue completion average 7882.67567567568 min 2316 max 28811 stddev 1982.79172973284 insert io-wq time average 8983.82276995305 min 3324 max 87816 stddev 2551.60056552038 async time completion average 24670.4758861127 min 10758 max 102612 stddev 3483.92416873804 Conclusion: On average reissuing the sqe with the patch code is 1.1uSec faster and in the worse case scenario 59uSec faster than placing the request on io-wq On average completion time by reissuing the sqe with the patch code is 16.79uSec faster and in the worse case scenario 73.8uSec faster than async completion. Signed-off-by: Olivier Langlois <[email protected]> Reviewed-by: Pavel Begunkov <[email protected]> Link: https://lore.kernel.org/r/9e8441419bb1b8f3c3fcc607b2713efecdef2136.1624364038.git.olivier@trillion01.com Signed-off-by: Jens Axboe <[email protected]>
1 parent 22634bc commit 59b735a

File tree

1 file changed

+22
-9
lines changed

1 file changed

+22
-9
lines changed

fs/io_uring.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5160,7 +5160,13 @@ static __poll_t __io_arm_poll_handler(struct io_kiocb *req,
51605160
return mask;
51615161
}
51625162

5163-
static bool io_arm_poll_handler(struct io_kiocb *req)
5163+
enum {
5164+
IO_APOLL_OK,
5165+
IO_APOLL_ABORTED,
5166+
IO_APOLL_READY
5167+
};
5168+
5169+
static int io_arm_poll_handler(struct io_kiocb *req)
51645170
{
51655171
const struct io_op_def *def = &io_op_defs[req->opcode];
51665172
struct io_ring_ctx *ctx = req->ctx;
@@ -5170,22 +5176,22 @@ static bool io_arm_poll_handler(struct io_kiocb *req)
51705176
int rw;
51715177

51725178
if (!req->file || !file_can_poll(req->file))
5173-
return false;
5179+
return IO_APOLL_ABORTED;
51745180
if (req->flags & REQ_F_POLLED)
5175-
return false;
5181+
return IO_APOLL_ABORTED;
51765182
if (def->pollin)
51775183
rw = READ;
51785184
else if (def->pollout)
51795185
rw = WRITE;
51805186
else
5181-
return false;
5187+
return IO_APOLL_ABORTED;
51825188
/* if we can't nonblock try, then no point in arming a poll handler */
51835189
if (!io_file_supports_async(req, rw))
5184-
return false;
5190+
return IO_APOLL_ABORTED;
51855191

51865192
apoll = kmalloc(sizeof(*apoll), GFP_ATOMIC);
51875193
if (unlikely(!apoll))
5188-
return false;
5194+
return IO_APOLL_ABORTED;
51895195
apoll->double_poll = NULL;
51905196

51915197
req->flags |= REQ_F_POLLED;
@@ -5211,12 +5217,14 @@ static bool io_arm_poll_handler(struct io_kiocb *req)
52115217
if (ret || ipt.error) {
52125218
io_poll_remove_double(req);
52135219
spin_unlock_irq(&ctx->completion_lock);
5214-
return false;
5220+
if (ret)
5221+
return IO_APOLL_READY;
5222+
return IO_APOLL_ABORTED;
52155223
}
52165224
spin_unlock_irq(&ctx->completion_lock);
52175225
trace_io_uring_poll_arm(ctx, req, req->opcode, req->user_data,
52185226
mask, apoll->poll.events);
5219-
return true;
5227+
return IO_APOLL_OK;
52205228
}
52215229

52225230
static bool __io_poll_remove_one(struct io_kiocb *req,
@@ -6445,6 +6453,7 @@ static void __io_queue_sqe(struct io_kiocb *req)
64456453
struct io_kiocb *linked_timeout = io_prep_linked_timeout(req);
64466454
int ret;
64476455

6456+
issue_sqe:
64486457
ret = io_issue_sqe(req, IO_URING_F_NONBLOCK|IO_URING_F_COMPLETE_DEFER);
64496458

64506459
/*
@@ -6464,12 +6473,16 @@ static void __io_queue_sqe(struct io_kiocb *req)
64646473
io_put_req(req);
64656474
}
64666475
} else if (ret == -EAGAIN && !(req->flags & REQ_F_NOWAIT)) {
6467-
if (!io_arm_poll_handler(req)) {
6476+
switch (io_arm_poll_handler(req)) {
6477+
case IO_APOLL_READY:
6478+
goto issue_sqe;
6479+
case IO_APOLL_ABORTED:
64686480
/*
64696481
* Queued up for async execution, worker will release
64706482
* submit reference when the iocb is actually submitted.
64716483
*/
64726484
io_queue_async_work(req);
6485+
break;
64736486
}
64746487
} else {
64756488
io_req_complete_failed(req, ret);

0 commit comments

Comments
 (0)