Skip to content

Commit 4d9cb92

Browse files
isilenceaxboe
authored andcommitted
io_uring/rw: fix short rw error handling
We have a couple of problems, first reports of unexpected link breakage for reads when cqe->res indicates that the IO was done in full. The reason here is partial IO with retries. TL;DR; we compare the result in __io_complete_rw_common() against req->cqe.res, but req->cqe.res doesn't store the full length but rather the length left to be done. So, when we pass the full corrected result via kiocb_done() -> __io_complete_rw_common(), it fails. The second problem is that we don't try to correct res in io_complete_rw(), which, for instance, might be a problem for O_DIRECT but when a prefix of data was cached in the page cache. We also definitely don't want to pass a corrected result into io_rw_done(). The fix here is to leave __io_complete_rw_common() alone, always pass not corrected result into it and fix it up as the last step just before actually finishing the I/O. Cc: stable@vger.kernel.org Signed-off-by: Pavel Begunkov <asml.silence@gmail.com> Link: axboe/liburing#643 Reported-by: Beld Zhang <beldzhang@gmail.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 3c84005 commit 4d9cb92

File tree

1 file changed

+18
-12
lines changed

1 file changed

+18
-12
lines changed

io_uring/rw.c

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -206,14 +206,28 @@ static bool __io_complete_rw_common(struct io_kiocb *req, long res)
206206
return false;
207207
}
208208

209+
static inline unsigned io_fixup_rw_res(struct io_kiocb *req, unsigned res)
210+
{
211+
struct io_async_rw *io = req->async_data;
212+
213+
/* add previously done IO, if any */
214+
if (req_has_async_data(req) && io->bytes_done > 0) {
215+
if (res < 0)
216+
res = io->bytes_done;
217+
else
218+
res += io->bytes_done;
219+
}
220+
return res;
221+
}
222+
209223
static void io_complete_rw(struct kiocb *kiocb, long res)
210224
{
211225
struct io_rw *rw = container_of(kiocb, struct io_rw, kiocb);
212226
struct io_kiocb *req = cmd_to_io_kiocb(rw);
213227

214228
if (__io_complete_rw_common(req, res))
215229
return;
216-
io_req_set_res(req, res, 0);
230+
io_req_set_res(req, io_fixup_rw_res(req, res), 0);
217231
req->io_task_work.func = io_req_task_complete;
218232
io_req_task_work_add(req);
219233
}
@@ -240,22 +254,14 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res)
240254
static int kiocb_done(struct io_kiocb *req, ssize_t ret,
241255
unsigned int issue_flags)
242256
{
243-
struct io_async_rw *io = req->async_data;
244257
struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
245-
246-
/* add previously done IO, if any */
247-
if (req_has_async_data(req) && io->bytes_done > 0) {
248-
if (ret < 0)
249-
ret = io->bytes_done;
250-
else
251-
ret += io->bytes_done;
252-
}
258+
unsigned final_ret = io_fixup_rw_res(req, ret);
253259

254260
if (req->flags & REQ_F_CUR_POS)
255261
req->file->f_pos = rw->kiocb.ki_pos;
256262
if (ret >= 0 && (rw->kiocb.ki_complete == io_complete_rw)) {
257263
if (!__io_complete_rw_common(req, ret)) {
258-
io_req_set_res(req, req->cqe.res,
264+
io_req_set_res(req, final_ret,
259265
io_put_kbuf(req, issue_flags));
260266
return IOU_OK;
261267
}
@@ -268,7 +274,7 @@ static int kiocb_done(struct io_kiocb *req, ssize_t ret,
268274
if (io_resubmit_prep(req))
269275
io_req_task_queue_reissue(req);
270276
else
271-
io_req_task_queue_fail(req, ret);
277+
io_req_task_queue_fail(req, final_ret);
272278
}
273279
return IOU_ISSUE_SKIP_COMPLETE;
274280
}

0 commit comments

Comments
 (0)