Skip to content

Commit 6e5aedb

Browse files
committed
io_uring/poll: attempt request issue after racy poll wakeup
If we have multiple requests waiting on the same target poll waitqueue, then it's quite possible to get a request triggered and get disappointed in not being able to make any progress with it. If we race in doing so, we'll potentially leave the poll request on the internal tables, but removed from the waitqueue. That means that any subsequent trigger of the poll waitqueue will not kick that request into action, causing an application to potentially wait for completion of a request that will never happen. Fix this by adding a new poll return state, IOU_POLL_REISSUE. Rather than have complicated logic for how to re-arm a given type of request, just punt it for a reissue. While in there, move the 'ret' variable to the only section where it gets used. This avoids confusion the scope of it. Cc: stable@vger.kernel.org Fixes: eb0089d ("io_uring: single shot poll removal optimisation") Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent ea97cbe commit 6e5aedb

File tree

1 file changed

+20
-11
lines changed

1 file changed

+20
-11
lines changed

io_uring/poll.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -223,21 +223,22 @@ enum {
223223
IOU_POLL_DONE = 0,
224224
IOU_POLL_NO_ACTION = 1,
225225
IOU_POLL_REMOVE_POLL_USE_RES = 2,
226+
IOU_POLL_REISSUE = 3,
226227
};
227228

228229
/*
229230
* All poll tw should go through this. Checks for poll events, manages
230231
* references, does rewait, etc.
231232
*
232-
* Returns a negative error on failure. IOU_POLL_NO_ACTION when no action require,
233-
* which is either spurious wakeup or multishot CQE is served.
234-
* IOU_POLL_DONE when it's done with the request, then the mask is stored in req->cqe.res.
235-
* IOU_POLL_REMOVE_POLL_USE_RES indicates to remove multishot poll and that the result
236-
* is stored in req->cqe.
233+
* Returns a negative error on failure. IOU_POLL_NO_ACTION when no action
234+
* require, which is either spurious wakeup or multishot CQE is served.
235+
* IOU_POLL_DONE when it's done with the request, then the mask is stored in
236+
* req->cqe.res. IOU_POLL_REMOVE_POLL_USE_RES indicates to remove multishot
237+
* poll and that the result is stored in req->cqe.
237238
*/
238239
static int io_poll_check_events(struct io_kiocb *req, bool *locked)
239240
{
240-
int v, ret;
241+
int v;
241242

242243
/* req->task == current here, checking PF_EXITING is safe */
243244
if (unlikely(req->task->flags & PF_EXITING))
@@ -276,10 +277,15 @@ static int io_poll_check_events(struct io_kiocb *req, bool *locked)
276277
if (!req->cqe.res) {
277278
struct poll_table_struct pt = { ._key = req->apoll_events };
278279
req->cqe.res = vfs_poll(req->file, &pt) & req->apoll_events;
280+
/*
281+
* We got woken with a mask, but someone else got to
282+
* it first. The above vfs_poll() doesn't add us back
283+
* to the waitqueue, so if we get nothing back, we
284+
* should be safe and attempt a reissue.
285+
*/
286+
if (unlikely(!req->cqe.res))
287+
return IOU_POLL_REISSUE;
279288
}
280-
281-
if ((unlikely(!req->cqe.res)))
282-
continue;
283289
if (req->apoll_events & EPOLLONESHOT)
284290
return IOU_POLL_DONE;
285291

@@ -294,7 +300,7 @@ static int io_poll_check_events(struct io_kiocb *req, bool *locked)
294300
return IOU_POLL_REMOVE_POLL_USE_RES;
295301
}
296302
} else {
297-
ret = io_poll_issue(req, locked);
303+
int ret = io_poll_issue(req, locked);
298304
if (ret == IOU_STOP_MULTISHOT)
299305
return IOU_POLL_REMOVE_POLL_USE_RES;
300306
if (ret < 0)
@@ -330,6 +336,9 @@ static void io_poll_task_func(struct io_kiocb *req, bool *locked)
330336

331337
poll = io_kiocb_to_cmd(req, struct io_poll);
332338
req->cqe.res = mangle_poll(req->cqe.res & poll->events);
339+
} else if (ret == IOU_POLL_REISSUE) {
340+
io_req_task_submit(req, locked);
341+
return;
333342
} else if (ret != IOU_POLL_REMOVE_POLL_USE_RES) {
334343
req->cqe.res = ret;
335344
req_set_fail(req);
@@ -342,7 +351,7 @@ static void io_poll_task_func(struct io_kiocb *req, bool *locked)
342351

343352
if (ret == IOU_POLL_REMOVE_POLL_USE_RES)
344353
io_req_task_complete(req, locked);
345-
else if (ret == IOU_POLL_DONE)
354+
else if (ret == IOU_POLL_DONE || ret == IOU_POLL_REISSUE)
346355
io_req_task_submit(req, locked);
347356
else
348357
io_req_defer_failed(req, ret);

0 commit comments

Comments
 (0)