summaryrefslogtreecommitdiff
path: root/fs/io_uring.c
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2021-04-06 09:49:31 -0600
committerJens Axboe <axboe@kernel.dk>2021-04-11 19:30:35 -0600
commitcb3b200e4f66524d03d6410dd51bcf42f265a4d0 (patch)
treed05a2a97170a702d71d888fe929acef1567e59d6 /fs/io_uring.c
parent417b5052be9ec892d06a45dbecf6d3f6ae211ae3 (diff)
downloadlwn-cb3b200e4f66524d03d6410dd51bcf42f265a4d0.tar.gz
lwn-cb3b200e4f66524d03d6410dd51bcf42f265a4d0.zip
io_uring: don't attempt re-add of multishot poll request if racing
We currently allow racy updates to multishot requests, but we can end up double adding the poll request if both completion and update does it. Ensure that we skip re-add on the update side if someone else is completing it. Fixes: b69de288e913 ("io_uring: allow events and user_data update of running poll requests") Reported-by: Joakim Hassila <joj@mac.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'fs/io_uring.c')
-rw-r--r--fs/io_uring.c30
1 files changed, 20 insertions, 10 deletions
diff --git a/fs/io_uring.c b/fs/io_uring.c
index ceb5ddd36826..0fcb0f477d9e 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -5432,6 +5432,7 @@ static int io_poll_update(struct io_kiocb *req)
{
struct io_ring_ctx *ctx = req->ctx;
struct io_kiocb *preq;
+ bool completing;
int ret;
spin_lock_irq(&ctx->completion_lock);
@@ -5444,17 +5445,22 @@ static int io_poll_update(struct io_kiocb *req)
ret = -EACCES;
goto err;
}
- if (!__io_poll_remove_one(preq, &preq->poll, false)) {
- if (preq->poll.events & EPOLLONESHOT) {
- ret = -EALREADY;
- goto err;
- }
+
+ /*
+ * Don't allow racy completion with singleshot, as we cannot safely
+ * update those. For multishot, if we're racing with completion, just
+ * let completion re-add it.
+ */
+ completing = !__io_poll_remove_one(preq, &preq->poll, false);
+ if (completing && (preq->poll.events & EPOLLONESHOT)) {
+ ret = -EALREADY;
+ goto err;
}
/* we now have a detached poll request. reissue. */
ret = 0;
err:
- spin_unlock_irq(&ctx->completion_lock);
if (ret < 0) {
+ spin_unlock_irq(&ctx->completion_lock);
req_set_fail_links(req);
io_req_complete(req, ret);
return 0;
@@ -5468,13 +5474,17 @@ err:
if (req->poll.update_user_data)
preq->user_data = req->poll.new_user_data;
+ spin_unlock_irq(&ctx->completion_lock);
+
/* complete update request, we're done with it */
io_req_complete(req, ret);
- ret = __io_poll_add(preq);
- if (ret < 0) {
- req_set_fail_links(preq);
- io_req_complete(preq, ret);
+ if (!completing) {
+ ret = __io_poll_add(preq);
+ if (ret < 0) {
+ req_set_fail_links(preq);
+ io_req_complete(preq, ret);
+ }
}
return 0;
}