Merge tag 'io_uring-5.16-2021-11-27' of git://git.kernel.dk/linux-block
Pull more io_uring fixes from Jens Axboe:
"The locking fixup that was applied earlier this rc has both a deadlock
and IRQ safety issue, let's get that ironed out before -rc3. This
contains:
- Link traversal locking fix (Pavel)
- Cancelation fix (Pavel)
- Relocate cond_resched() for huge buffer chain freeing, avoiding a
softlockup warning (Ye)
- Fix timespec validation (Ye)"
* tag 'io_uring-5.16-2021-11-27' of git://git.kernel.dk/linux-block:
io_uring: Fix undefined-behaviour in io_issue_sqe
io_uring: fix soft lockup when call __io_remove_buffers
io_uring: fix link traversal locking
io_uring: fail cancellation for EXITING tasks
This commit is contained in:
+50
-23
@@ -1278,6 +1278,7 @@ static void io_refs_resurrect(struct percpu_ref *ref, struct completion *compl)
|
||||
|
||||
static bool io_match_task(struct io_kiocb *head, struct task_struct *task,
|
||||
bool cancel_all)
|
||||
__must_hold(&req->ctx->timeout_lock)
|
||||
{
|
||||
struct io_kiocb *req;
|
||||
|
||||
@@ -1293,6 +1294,44 @@ static bool io_match_task(struct io_kiocb *head, struct task_struct *task,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool io_match_linked(struct io_kiocb *head)
|
||||
{
|
||||
struct io_kiocb *req;
|
||||
|
||||
io_for_each_link(req, head) {
|
||||
if (req->flags & REQ_F_INFLIGHT)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* As io_match_task() but protected against racing with linked timeouts.
|
||||
* User must not hold timeout_lock.
|
||||
*/
|
||||
static bool io_match_task_safe(struct io_kiocb *head, struct task_struct *task,
|
||||
bool cancel_all)
|
||||
{
|
||||
bool matched;
|
||||
|
||||
if (task && head->task != task)
|
||||
return false;
|
||||
if (cancel_all)
|
||||
return true;
|
||||
|
||||
if (head->flags & REQ_F_LINK_TIMEOUT) {
|
||||
struct io_ring_ctx *ctx = head->ctx;
|
||||
|
||||
/* protect against races with linked timeouts */
|
||||
spin_lock_irq(&ctx->timeout_lock);
|
||||
matched = io_match_linked(head);
|
||||
spin_unlock_irq(&ctx->timeout_lock);
|
||||
} else {
|
||||
matched = io_match_linked(head);
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
static inline bool req_has_async_data(struct io_kiocb *req)
|
||||
{
|
||||
return req->flags & REQ_F_ASYNC_DATA;
|
||||
@@ -4327,6 +4366,7 @@ static int __io_remove_buffers(struct io_ring_ctx *ctx, struct io_buffer *buf,
|
||||
kfree(nxt);
|
||||
if (++i == nbufs)
|
||||
return i;
|
||||
cond_resched();
|
||||
}
|
||||
i++;
|
||||
kfree(buf);
|
||||
@@ -5699,17 +5739,15 @@ static __cold bool io_poll_remove_all(struct io_ring_ctx *ctx,
|
||||
int posted = 0, i;
|
||||
|
||||
spin_lock(&ctx->completion_lock);
|
||||
spin_lock_irq(&ctx->timeout_lock);
|
||||
for (i = 0; i < (1U << ctx->cancel_hash_bits); i++) {
|
||||
struct hlist_head *list;
|
||||
|
||||
list = &ctx->cancel_hash[i];
|
||||
hlist_for_each_entry_safe(req, tmp, list, hash_node) {
|
||||
if (io_match_task(req, tsk, cancel_all))
|
||||
if (io_match_task_safe(req, tsk, cancel_all))
|
||||
posted += io_poll_remove_one(req);
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&ctx->timeout_lock);
|
||||
spin_unlock(&ctx->completion_lock);
|
||||
|
||||
if (posted)
|
||||
@@ -6158,6 +6196,9 @@ static int io_timeout_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe,
|
||||
if (get_timespec64(&data->ts, u64_to_user_ptr(sqe->addr)))
|
||||
return -EFAULT;
|
||||
|
||||
if (data->ts.tv_sec < 0 || data->ts.tv_nsec < 0)
|
||||
return -EINVAL;
|
||||
|
||||
data->mode = io_translate_timeout_mode(flags);
|
||||
hrtimer_init(&data->timer, io_timeout_get_clock(data), data->mode);
|
||||
|
||||
@@ -6882,10 +6923,11 @@ static inline struct file *io_file_get(struct io_ring_ctx *ctx,
|
||||
static void io_req_task_link_timeout(struct io_kiocb *req, bool *locked)
|
||||
{
|
||||
struct io_kiocb *prev = req->timeout.prev;
|
||||
int ret;
|
||||
int ret = -ENOENT;
|
||||
|
||||
if (prev) {
|
||||
ret = io_try_cancel_userdata(req, prev->user_data);
|
||||
if (!(req->task->flags & PF_EXITING))
|
||||
ret = io_try_cancel_userdata(req, prev->user_data);
|
||||
io_req_complete_post(req, ret ?: -ETIME, 0);
|
||||
io_put_req(prev);
|
||||
} else {
|
||||
@@ -9257,10 +9299,8 @@ static void io_destroy_buffers(struct io_ring_ctx *ctx)
|
||||
struct io_buffer *buf;
|
||||
unsigned long index;
|
||||
|
||||
xa_for_each(&ctx->io_buffers, index, buf) {
|
||||
xa_for_each(&ctx->io_buffers, index, buf)
|
||||
__io_remove_buffers(ctx, buf, index, -1U);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
static void io_req_caches_free(struct io_ring_ctx *ctx)
|
||||
@@ -9564,19 +9604,8 @@ static bool io_cancel_task_cb(struct io_wq_work *work, void *data)
|
||||
{
|
||||
struct io_kiocb *req = container_of(work, struct io_kiocb, work);
|
||||
struct io_task_cancel *cancel = data;
|
||||
bool ret;
|
||||
|
||||
if (!cancel->all && (req->flags & REQ_F_LINK_TIMEOUT)) {
|
||||
struct io_ring_ctx *ctx = req->ctx;
|
||||
|
||||
/* protect against races with linked timeouts */
|
||||
spin_lock_irq(&ctx->timeout_lock);
|
||||
ret = io_match_task(req, cancel->task, cancel->all);
|
||||
spin_unlock_irq(&ctx->timeout_lock);
|
||||
} else {
|
||||
ret = io_match_task(req, cancel->task, cancel->all);
|
||||
}
|
||||
return ret;
|
||||
return io_match_task_safe(req, cancel->task, cancel->all);
|
||||
}
|
||||
|
||||
static __cold bool io_cancel_defer_files(struct io_ring_ctx *ctx,
|
||||
@@ -9587,14 +9616,12 @@ static __cold bool io_cancel_defer_files(struct io_ring_ctx *ctx,
|
||||
LIST_HEAD(list);
|
||||
|
||||
spin_lock(&ctx->completion_lock);
|
||||
spin_lock_irq(&ctx->timeout_lock);
|
||||
list_for_each_entry_reverse(de, &ctx->defer_list, list) {
|
||||
if (io_match_task(de->req, task, cancel_all)) {
|
||||
if (io_match_task_safe(de->req, task, cancel_all)) {
|
||||
list_cut_position(&list, &ctx->defer_list, &de->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&ctx->timeout_lock);
|
||||
spin_unlock(&ctx->completion_lock);
|
||||
if (list_empty(&list))
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user