Skip to content

Commit 51a4cc1

Browse files
committed
io_uring: defer file table grabbing request cleanup for locked requests
If we're in the error path failing links and we have a link that has grabbed a reference to the fs_struct, then we cannot safely drop our reference to the table if we already hold the completion lock. This adds a hardirq dependency to the fs_struct->lock, which it currently doesn't have. Defer the final cleanup and free of such requests to avoid adding this dependency. Reported-by: [email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent 9b7adba commit 51a4cc1

File tree

1 file changed

+52
-10
lines changed

1 file changed

+52
-10
lines changed

fs/io_uring.c

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,10 +1108,16 @@ static void __io_commit_cqring(struct io_ring_ctx *ctx)
11081108
}
11091109
}
11101110

1111-
static void io_req_clean_work(struct io_kiocb *req)
1111+
/*
1112+
* Returns true if we need to defer file table putting. This can only happen
1113+
* from the error path with REQ_F_COMP_LOCKED set.
1114+
*/
1115+
static bool io_req_clean_work(struct io_kiocb *req)
11121116
{
11131117
if (!(req->flags & REQ_F_WORK_INITIALIZED))
1114-
return;
1118+
return false;
1119+
1120+
req->flags &= ~REQ_F_WORK_INITIALIZED;
11151121

11161122
if (req->work.mm) {
11171123
mmdrop(req->work.mm);
@@ -1124,6 +1130,9 @@ static void io_req_clean_work(struct io_kiocb *req)
11241130
if (req->work.fs) {
11251131
struct fs_struct *fs = req->work.fs;
11261132

1133+
if (req->flags & REQ_F_COMP_LOCKED)
1134+
return true;
1135+
11271136
spin_lock(&req->work.fs->lock);
11281137
if (--fs->users)
11291138
fs = NULL;
@@ -1132,7 +1141,8 @@ static void io_req_clean_work(struct io_kiocb *req)
11321141
free_fs_struct(fs);
11331142
req->work.fs = NULL;
11341143
}
1135-
req->flags &= ~REQ_F_WORK_INITIALIZED;
1144+
1145+
return false;
11361146
}
11371147

11381148
static void io_prep_async_work(struct io_kiocb *req)
@@ -1544,15 +1554,14 @@ static inline void io_put_file(struct io_kiocb *req, struct file *file,
15441554
fput(file);
15451555
}
15461556

1547-
static void io_dismantle_req(struct io_kiocb *req)
1557+
static bool io_dismantle_req(struct io_kiocb *req)
15481558
{
15491559
io_clean_op(req);
15501560

15511561
if (req->io)
15521562
kfree(req->io);
15531563
if (req->file)
15541564
io_put_file(req, req->file, (req->flags & REQ_F_FIXED_FILE));
1555-
io_req_clean_work(req);
15561565

15571566
if (req->flags & REQ_F_INFLIGHT) {
15581567
struct io_ring_ctx *ctx = req->ctx;
@@ -1564,22 +1573,55 @@ static void io_dismantle_req(struct io_kiocb *req)
15641573
wake_up(&ctx->inflight_wait);
15651574
spin_unlock_irqrestore(&ctx->inflight_lock, flags);
15661575
}
1576+
1577+
return io_req_clean_work(req);
15671578
}
15681579

1569-
static void __io_free_req(struct io_kiocb *req)
1580+
static void __io_free_req_finish(struct io_kiocb *req)
15701581
{
1571-
struct io_ring_ctx *ctx;
1582+
struct io_ring_ctx *ctx = req->ctx;
15721583

1573-
io_dismantle_req(req);
15741584
__io_put_req_task(req);
1575-
ctx = req->ctx;
15761585
if (likely(!io_is_fallback_req(req)))
15771586
kmem_cache_free(req_cachep, req);
15781587
else
15791588
clear_bit_unlock(0, (unsigned long *) &ctx->fallback_req);
15801589
percpu_ref_put(&ctx->refs);
15811590
}
15821591

1592+
static void io_req_task_file_table_put(struct callback_head *cb)
1593+
{
1594+
struct io_kiocb *req = container_of(cb, struct io_kiocb, task_work);
1595+
struct fs_struct *fs = req->work.fs;
1596+
1597+
spin_lock(&req->work.fs->lock);
1598+
if (--fs->users)
1599+
fs = NULL;
1600+
spin_unlock(&req->work.fs->lock);
1601+
if (fs)
1602+
free_fs_struct(fs);
1603+
req->work.fs = NULL;
1604+
__io_free_req_finish(req);
1605+
}
1606+
1607+
static void __io_free_req(struct io_kiocb *req)
1608+
{
1609+
if (!io_dismantle_req(req)) {
1610+
__io_free_req_finish(req);
1611+
} else {
1612+
int ret;
1613+
1614+
init_task_work(&req->task_work, io_req_task_file_table_put);
1615+
ret = task_work_add(req->task, &req->task_work, TWA_RESUME);
1616+
if (unlikely(ret)) {
1617+
struct task_struct *tsk;
1618+
1619+
tsk = io_wq_get_task(req->ctx->io_wq);
1620+
task_work_add(tsk, &req->task_work, 0);
1621+
}
1622+
}
1623+
}
1624+
15831625
static bool io_link_cancel_timeout(struct io_kiocb *req)
15841626
{
15851627
struct io_ring_ctx *ctx = req->ctx;
@@ -1868,7 +1910,7 @@ static void io_req_free_batch(struct req_batch *rb, struct io_kiocb *req)
18681910
req->flags &= ~REQ_F_TASK_PINNED;
18691911
}
18701912

1871-
io_dismantle_req(req);
1913+
WARN_ON_ONCE(io_dismantle_req(req));
18721914
rb->reqs[rb->to_free++] = req;
18731915
if (unlikely(rb->to_free == ARRAY_SIZE(rb->reqs)))
18741916
__io_req_free_batch_flush(req->ctx, rb);

0 commit comments

Comments
 (0)