Skip to content

Commit aa96bf8

Browse files
committed
io_uring: use io-wq manager as backup task if task is exiting
If the original task is (or has) exited, then the task work will not get queued properly. Allow for using the io-wq manager task to queue this work for execution, and ensure that the io-wq manager notices and runs this work if woken up (or exiting). Reported-by: Dan Melnic <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 3537b6a commit aa96bf8

File tree

3 files changed

+23
-4
lines changed

3 files changed

+23
-4
lines changed

fs/io-wq.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/kthread.h>
1818
#include <linux/rculist_nulls.h>
1919
#include <linux/fs_struct.h>
20+
#include <linux/task_work.h>
2021

2122
#include "io-wq.h"
2223

@@ -716,6 +717,9 @@ static int io_wq_manager(void *data)
716717
complete(&wq->done);
717718

718719
while (!kthread_should_stop()) {
720+
if (current->task_works)
721+
task_work_run();
722+
719723
for_each_node(node) {
720724
struct io_wqe *wqe = wq->wqes[node];
721725
bool fork_worker[2] = { false, false };
@@ -738,6 +742,9 @@ static int io_wq_manager(void *data)
738742
schedule_timeout(HZ);
739743
}
740744

745+
if (current->task_works)
746+
task_work_run();
747+
741748
return 0;
742749
err:
743750
set_bit(IO_WQ_BIT_ERROR, &wq->state);
@@ -1124,3 +1131,8 @@ void io_wq_destroy(struct io_wq *wq)
11241131
if (refcount_dec_and_test(&wq->use_refs))
11251132
__io_wq_destroy(wq);
11261133
}
1134+
1135+
struct task_struct *io_wq_get_task(struct io_wq *wq)
1136+
{
1137+
return wq->manager;
1138+
}

fs/io-wq.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ typedef bool (work_cancel_fn)(struct io_wq_work *, void *);
136136
enum io_wq_cancel io_wq_cancel_cb(struct io_wq *wq, work_cancel_fn *cancel,
137137
void *data);
138138

139+
struct task_struct *io_wq_get_task(struct io_wq *wq);
140+
139141
#if defined(CONFIG_IO_WQ)
140142
extern void io_wq_worker_sleeping(struct task_struct *);
141143
extern void io_wq_worker_running(struct task_struct *);

fs/io_uring.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4120,6 +4120,7 @@ static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll,
41204120
__poll_t mask, task_work_func_t func)
41214121
{
41224122
struct task_struct *tsk;
4123+
int ret;
41234124

41244125
/* for instances that support it check for an event match first: */
41254126
if (mask && !(mask & poll->events))
@@ -4133,11 +4134,15 @@ static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll,
41334134
req->result = mask;
41344135
init_task_work(&req->task_work, func);
41354136
/*
4136-
* If this fails, then the task is exiting. If that is the case, then
4137-
* the exit check will ultimately cancel these work items. Hence we
4138-
* don't need to check here and handle it specifically.
4137+
* If this fails, then the task is exiting. Punt to one of the io-wq
4138+
* threads to ensure the work gets run, we can't always rely on exit
4139+
* cancelation taking care of this.
41394140
*/
4140-
task_work_add(tsk, &req->task_work, true);
4141+
ret = task_work_add(tsk, &req->task_work, true);
4142+
if (unlikely(ret)) {
4143+
tsk = io_wq_get_task(req->ctx->io_wq);
4144+
task_work_add(tsk, &req->task_work, true);
4145+
}
41414146
wake_up_process(tsk);
41424147
return 1;
41434148
}

0 commit comments

Comments
 (0)