mirror of
https://github.com/torvalds/linux.git
synced 2026-09-14 08:01:12 +02:00
io_uring/rw: end write accounting from ->ki_complete
Commitb000145e99moved both the fsnotify calls and the write accounting out of the kiocb completion handler and into the io_req_rw_complete() task_work. However, only the fsnotify part actually needed to move as it may sleep. Ending the write accounting is just a percpu_up_read() on the superblock writers sem. Deferring it is a problem, because it makes dropping SB_FREEZE_WRITE protection depend on the ring owner getting to running task_work. But the task may be blocked in freeze_super(), causing it to never get to that: task io-wq worker -------------------------------------------------------------- io_write() io_kiocb_start_write() (takes sb_writers, hidden from lockdep by __sb_writers_release) write_iter() -> -EIOCBQUEUED ioctl(FS_IOC_SHUTDOWN) bdev_freeze() freeze_super() percpu_down_write() <- waits for the reader above io_write() kiocb_start_write() percpu_down_read() <- queued behind the writer <bio completes> io_complete_rw() queues io_req_rw_complete() <- never runs, task is in D state End the write from io_complete_rw() instead, and leave only the fsnotify calls in task_work. Reported-by: syzbot+2eb3d983669d3e49d4fa@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Fixes:b000145e99("io_uring/rw: defer fsnotify calls to task context") Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
2cf20c4e0f
commit
796aa05475
|
|
@ -517,20 +517,25 @@ static void io_req_end_write(struct io_kiocb *req)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger the notifications after having done some IO, and finish the write
|
||||
* accounting, if any.
|
||||
*/
|
||||
/* Trigger the notifications after having done some IO. */
|
||||
static void io_req_io_notify(struct io_kiocb *req)
|
||||
{
|
||||
struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
|
||||
|
||||
if (rw->kiocb.ki_flags & IOCB_WRITE)
|
||||
fsnotify_modify(req->file);
|
||||
else
|
||||
fsnotify_access(req->file);
|
||||
}
|
||||
|
||||
/* Finish write accounting and notify, for inline completions only. */
|
||||
static void io_req_io_end(struct io_kiocb *req)
|
||||
{
|
||||
struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
|
||||
|
||||
if (rw->kiocb.ki_flags & IOCB_WRITE) {
|
||||
if (rw->kiocb.ki_flags & IOCB_WRITE)
|
||||
io_req_end_write(req);
|
||||
fsnotify_modify(req->file);
|
||||
} else {
|
||||
fsnotify_access(req->file);
|
||||
}
|
||||
io_req_io_notify(req);
|
||||
}
|
||||
|
||||
static void __io_complete_rw_common(struct io_kiocb *req, long res)
|
||||
|
|
@ -563,7 +568,7 @@ void io_req_rw_complete(struct io_tw_req tw_req, io_tw_token_t tw)
|
|||
{
|
||||
struct io_kiocb *req = tw_req.req;
|
||||
|
||||
io_req_io_end(req);
|
||||
io_req_io_notify(req);
|
||||
|
||||
if (req->flags & (REQ_F_BUFFER_SELECTED|REQ_F_BUFFER_RING))
|
||||
req->cqe.flags |= io_put_kbuf(req, max(req->cqe.res, 0), NULL);
|
||||
|
|
@ -577,6 +582,10 @@ static void io_complete_rw(struct kiocb *kiocb, long res)
|
|||
struct io_rw *rw = container_of(kiocb, struct io_rw, kiocb);
|
||||
struct io_kiocb *req = cmd_to_io_kiocb(rw);
|
||||
|
||||
/* ring owner may block in freeze_super() before task_work runs */
|
||||
if (kiocb->ki_flags & IOCB_WRITE)
|
||||
io_req_end_write(req);
|
||||
|
||||
__io_complete_rw_common(req, res);
|
||||
io_req_set_res(req, io_fixup_rw_res(req, res), 0);
|
||||
req->io_task_work.func = io_req_rw_complete;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user