Skip to content

Commit f254ac0

Browse files
committed
io_uring: enable lookup of links holding inflight files
When a process exits, we cancel whatever requests it has pending that are referencing the file table. However, if a link is holding a reference, then we cannot find it by simply looking at the inflight list. Enable checking of the poll and timeout list to find the link, and cancel it appropriately. Cc: [email protected] Reported-by: Josef <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent a36da65 commit f254ac0

File tree

1 file changed

+87
-10
lines changed

1 file changed

+87
-10
lines changed

fs/io_uring.c

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4937,6 +4937,7 @@ static bool io_poll_remove_one(struct io_kiocb *req)
49374937
io_cqring_fill_event(req, -ECANCELED);
49384938
io_commit_cqring(req->ctx);
49394939
req->flags |= REQ_F_COMP_LOCKED;
4940+
req_set_fail_links(req);
49404941
io_put_req(req);
49414942
}
49424943

@@ -5109,14 +5110,30 @@ static enum hrtimer_restart io_timeout_fn(struct hrtimer *timer)
51095110
return HRTIMER_NORESTART;
51105111
}
51115112

5113+
static int __io_timeout_cancel(struct io_kiocb *req)
5114+
{
5115+
int ret;
5116+
5117+
list_del_init(&req->timeout.list);
5118+
5119+
ret = hrtimer_try_to_cancel(&req->io->timeout.timer);
5120+
if (ret == -1)
5121+
return -EALREADY;
5122+
5123+
req_set_fail_links(req);
5124+
req->flags |= REQ_F_COMP_LOCKED;
5125+
io_cqring_fill_event(req, -ECANCELED);
5126+
io_put_req(req);
5127+
return 0;
5128+
}
5129+
51125130
static int io_timeout_cancel(struct io_ring_ctx *ctx, __u64 user_data)
51135131
{
51145132
struct io_kiocb *req;
51155133
int ret = -ENOENT;
51165134

51175135
list_for_each_entry(req, &ctx->timeout_list, timeout.list) {
51185136
if (user_data == req->user_data) {
5119-
list_del_init(&req->timeout.list);
51205137
ret = 0;
51215138
break;
51225139
}
@@ -5125,15 +5142,7 @@ static int io_timeout_cancel(struct io_ring_ctx *ctx, __u64 user_data)
51255142
if (ret == -ENOENT)
51265143
return ret;
51275144

5128-
ret = hrtimer_try_to_cancel(&req->io->timeout.timer);
5129-
if (ret == -1)
5130-
return -EALREADY;
5131-
5132-
req_set_fail_links(req);
5133-
req->flags |= REQ_F_COMP_LOCKED;
5134-
io_cqring_fill_event(req, -ECANCELED);
5135-
io_put_req(req);
5136-
return 0;
5145+
return __io_timeout_cancel(req);
51375146
}
51385147

51395148
static int io_timeout_remove_prep(struct io_kiocb *req,
@@ -7935,6 +7944,71 @@ static bool io_wq_files_match(struct io_wq_work *work, void *data)
79357944
return work->files == files;
79367945
}
79377946

7947+
/*
7948+
* Returns true if 'preq' is the link parent of 'req'
7949+
*/
7950+
static bool io_match_link(struct io_kiocb *preq, struct io_kiocb *req)
7951+
{
7952+
struct io_kiocb *link;
7953+
7954+
if (!(preq->flags & REQ_F_LINK_HEAD))
7955+
return false;
7956+
7957+
list_for_each_entry(link, &preq->link_list, link_list) {
7958+
if (link == req)
7959+
return true;
7960+
}
7961+
7962+
return false;
7963+
}
7964+
7965+
/*
7966+
* We're looking to cancel 'req' because it's holding on to our files, but
7967+
* 'req' could be a link to another request. See if it is, and cancel that
7968+
* parent request if so.
7969+
*/
7970+
static bool io_poll_remove_link(struct io_ring_ctx *ctx, struct io_kiocb *req)
7971+
{
7972+
struct hlist_node *tmp;
7973+
struct io_kiocb *preq;
7974+
bool found = false;
7975+
int i;
7976+
7977+
spin_lock_irq(&ctx->completion_lock);
7978+
for (i = 0; i < (1U << ctx->cancel_hash_bits); i++) {
7979+
struct hlist_head *list;
7980+
7981+
list = &ctx->cancel_hash[i];
7982+
hlist_for_each_entry_safe(preq, tmp, list, hash_node) {
7983+
found = io_match_link(preq, req);
7984+
if (found) {
7985+
io_poll_remove_one(preq);
7986+
break;
7987+
}
7988+
}
7989+
}
7990+
spin_unlock_irq(&ctx->completion_lock);
7991+
return found;
7992+
}
7993+
7994+
static bool io_timeout_remove_link(struct io_ring_ctx *ctx,
7995+
struct io_kiocb *req)
7996+
{
7997+
struct io_kiocb *preq;
7998+
bool found = false;
7999+
8000+
spin_lock_irq(&ctx->completion_lock);
8001+
list_for_each_entry(preq, &ctx->timeout_list, timeout.list) {
8002+
found = io_match_link(preq, req);
8003+
if (found) {
8004+
__io_timeout_cancel(preq);
8005+
break;
8006+
}
8007+
}
8008+
spin_unlock_irq(&ctx->completion_lock);
8009+
return found;
8010+
}
8011+
79388012
static void io_uring_cancel_files(struct io_ring_ctx *ctx,
79398013
struct files_struct *files)
79408014
{
@@ -7989,6 +8063,9 @@ static void io_uring_cancel_files(struct io_ring_ctx *ctx,
79898063
}
79908064
} else {
79918065
io_wq_cancel_work(ctx->io_wq, &cancel_req->work);
8066+
/* could be a link, check and remove if it is */
8067+
if (!io_poll_remove_link(ctx, cancel_req))
8068+
io_timeout_remove_link(ctx, cancel_req);
79928069
io_put_req(cancel_req);
79938070
}
79948071

0 commit comments

Comments
 (0)