Skip to content

Commit d3101fc

Browse files
author
Xiaoguang Wang
committed
io_uring: fix io_kiocb.flags modification race in IOPOLL mode
to #28736503 commit 65a6543 upstream While testing io_uring in arm, we found sometimes io_sq_thread() keeps polling io requests even though there are not inflight io requests in block layer. After some investigations, found a possible race about io_kiocb.flags, see below race codes: 1) in the end of io_write() or io_read() req->flags &= ~REQ_F_NEED_CLEANUP; kfree(iovec); return ret; 2) in io_complete_rw_iopoll() if (res != -EAGAIN) req->flags |= REQ_F_IOPOLL_COMPLETED; In IOPOLL mode, io requests still maybe completed by interrupt, then above codes are not safe, concurrent modifications to req->flags, which is not protected by lock or is not atomic modifications. I also had disassemble io_complete_rw_iopoll() in arm: req->flags |= REQ_F_IOPOLL_COMPLETED; 0xffff000008387b18 <+76>: ldr w0, [x19,#104] 0xffff000008387b1c <+80>: orr w0, w0, #0x1000 0xffff000008387b20 <+84>: str w0, [x19,#104] Seems that the "req->flags |= REQ_F_IOPOLL_COMPLETED;" is load and modification, two instructions, which obviously is not atomic. To fix this issue, add a new iopoll_completed in io_kiocb to indicate whether io request is completed. Signed-off-by: Xiaoguang Wang <xiaoguang.wang@linux.alibaba.com> Acked-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Xiaoguang Wang <xiaoguang.wang@linux.alibaba.com> Acked-by: Joseph Qi <joseph.qi@linux.alibaba.com>
1 parent 9c90c6a commit d3101fc

File tree

1 file changed

+6
-6
lines changed

1 file changed

+6
-6
lines changed

fs/io_uring.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,6 @@ enum {
519519
REQ_F_INFLIGHT_BIT,
520520
REQ_F_CUR_POS_BIT,
521521
REQ_F_NOWAIT_BIT,
522-
REQ_F_IOPOLL_COMPLETED_BIT,
523522
REQ_F_LINK_TIMEOUT_BIT,
524523
REQ_F_TIMEOUT_BIT,
525524
REQ_F_ISREG_BIT,
@@ -564,8 +563,6 @@ enum {
564563
REQ_F_CUR_POS = BIT(REQ_F_CUR_POS_BIT),
565564
/* must not punt to workers */
566565
REQ_F_NOWAIT = BIT(REQ_F_NOWAIT_BIT),
567-
/* polled IO has completed */
568-
REQ_F_IOPOLL_COMPLETED = BIT(REQ_F_IOPOLL_COMPLETED_BIT),
569566
/* has linked timeout */
570567
REQ_F_LINK_TIMEOUT = BIT(REQ_F_LINK_TIMEOUT_BIT),
571568
/* timeout request */
@@ -630,6 +627,8 @@ struct io_kiocb {
630627
struct io_async_ctx *io;
631628
int cflags;
632629
u8 opcode;
630+
/* polled IO has completed */
631+
u8 iopoll_completed;
633632

634633
u16 buf_index;
635634

@@ -1789,7 +1788,7 @@ static int io_do_iopoll(struct io_ring_ctx *ctx, unsigned int *nr_events,
17891788
* If we find a request that requires polling, break out
17901789
* and complete those lists first, if we have entries there.
17911790
*/
1792-
if (req->flags & REQ_F_IOPOLL_COMPLETED) {
1791+
if (READ_ONCE(req->iopoll_completed)) {
17931792
list_move_tail(&req->list, &done);
17941793
continue;
17951794
}
@@ -1970,7 +1969,7 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res, long res2)
19701969
req_set_fail_links(req);
19711970
req->result = res;
19721971
if (res != -EAGAIN)
1973-
req->flags |= REQ_F_IOPOLL_COMPLETED;
1972+
WRITE_ONCE(req->iopoll_completed, 1);
19741973
}
19751974

19761975
/*
@@ -2003,7 +2002,7 @@ static void io_iopoll_req_issued(struct io_kiocb *req)
20032002
* For fast devices, IO may have already completed. If it has, add
20042003
* it to the front so we find it first.
20052004
*/
2006-
if (req->flags & REQ_F_IOPOLL_COMPLETED)
2005+
if (READ_ONCE(req->iopoll_completed))
20072006
list_add(&req->list, &ctx->poll_list);
20082007
else
20092008
list_add_tail(&req->list, &ctx->poll_list);
@@ -2131,6 +2130,7 @@ static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe,
21312130
kiocb->ki_flags |= IOCB_HIPRI;
21322131
kiocb->ki_complete = io_complete_rw_iopoll;
21332132
req->result = 0;
2133+
req->iopoll_completed = 0;
21342134
} else {
21352135
if (kiocb->ki_flags & IOCB_HIPRI)
21362136
return -EINVAL;

0 commit comments

Comments
 (0)