summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2021-02-26 22:54:16 +0100
committerJens Axboe <axboe@kernel.dk>2021-03-04 14:35:00 +0100
commit1d5f360dd1a3c04e00a52af74dd84fdb0e1d454b (patch)
treea26bed315a950ca6ae429ce7a2922eb6b3e369b9 /fs
parentio-wq: provide an io_wq_put_and_exit() helper (diff)
downloadlinux-1d5f360dd1a3c04e00a52af74dd84fdb0e1d454b.tar.xz
linux-1d5f360dd1a3c04e00a52af74dd84fdb0e1d454b.zip
io_uring: fix race condition in task_work add and clear
We clear the bit marking the ctx task_work as active after having run the queued work, but we really should be clearing it before. Otherwise we can hit a tiny race ala: CPU0 CPU1 io_task_work_add() tctx_task_work() run_work add_to_list test_and_set_bit clear_bit already set and CPU0 will return thinking the task_work is queued, while in reality it's already being run. If we hit the condition after __tctx_task_work() found no more work, but before we've cleared the bit, then we'll end up thinking it's queued and will be run. In reality it is queued, but we didn't queue the ctx task_work to ensure that it gets run. Fixes: 7cbf1722d5fc ("io_uring: provide FIFO ordering for task_work") Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'fs')
-rw-r--r--fs/io_uring.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/fs/io_uring.c b/fs/io_uring.c
index cb65e54c1b09..83973f6b3c0a 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -1893,10 +1893,10 @@ static void tctx_task_work(struct callback_head *cb)
{
struct io_uring_task *tctx = container_of(cb, struct io_uring_task, task_work);
+ clear_bit(0, &tctx->task_state);
+
while (__tctx_task_work(tctx))
cond_resched();
-
- clear_bit(0, &tctx->task_state);
}
static int io_task_work_add(struct task_struct *tsk, struct io_kiocb *req,