Commit 86799cdf authored by Linus Torvalds's avatar Linus Torvalds

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
parents 650c8edf f6223ff7
...@@ -1278,6 +1278,7 @@ static void io_refs_resurrect(struct percpu_ref *ref, struct completion *compl) ...@@ -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, static bool io_match_task(struct io_kiocb *head, struct task_struct *task,
bool cancel_all) bool cancel_all)
__must_hold(&req->ctx->timeout_lock)
{ {
struct io_kiocb *req; struct io_kiocb *req;
...@@ -1293,6 +1294,44 @@ static bool io_match_task(struct io_kiocb *head, struct task_struct *task, ...@@ -1293,6 +1294,44 @@ static bool io_match_task(struct io_kiocb *head, struct task_struct *task,
return false; 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) static inline bool req_has_async_data(struct io_kiocb *req)
{ {
return req->flags & REQ_F_ASYNC_DATA; 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, ...@@ -4327,6 +4366,7 @@ static int __io_remove_buffers(struct io_ring_ctx *ctx, struct io_buffer *buf,
kfree(nxt); kfree(nxt);
if (++i == nbufs) if (++i == nbufs)
return i; return i;
cond_resched();
} }
i++; i++;
kfree(buf); kfree(buf);
...@@ -5699,17 +5739,15 @@ static __cold bool io_poll_remove_all(struct io_ring_ctx *ctx, ...@@ -5699,17 +5739,15 @@ static __cold bool io_poll_remove_all(struct io_ring_ctx *ctx,
int posted = 0, i; int posted = 0, i;
spin_lock(&ctx->completion_lock); spin_lock(&ctx->completion_lock);
spin_lock_irq(&ctx->timeout_lock);
for (i = 0; i < (1U << ctx->cancel_hash_bits); i++) { for (i = 0; i < (1U << ctx->cancel_hash_bits); i++) {
struct hlist_head *list; struct hlist_head *list;
list = &ctx->cancel_hash[i]; list = &ctx->cancel_hash[i];
hlist_for_each_entry_safe(req, tmp, list, hash_node) { 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); posted += io_poll_remove_one(req);
} }
} }
spin_unlock_irq(&ctx->timeout_lock);
spin_unlock(&ctx->completion_lock); spin_unlock(&ctx->completion_lock);
if (posted) if (posted)
...@@ -6158,6 +6196,9 @@ static int io_timeout_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe, ...@@ -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))) if (get_timespec64(&data->ts, u64_to_user_ptr(sqe->addr)))
return -EFAULT; return -EFAULT;
if (data->ts.tv_sec < 0 || data->ts.tv_nsec < 0)
return -EINVAL;
data->mode = io_translate_timeout_mode(flags); data->mode = io_translate_timeout_mode(flags);
hrtimer_init(&data->timer, io_timeout_get_clock(data), data->mode); 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, ...@@ -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) static void io_req_task_link_timeout(struct io_kiocb *req, bool *locked)
{ {
struct io_kiocb *prev = req->timeout.prev; struct io_kiocb *prev = req->timeout.prev;
int ret; int ret = -ENOENT;
if (prev) { 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_req_complete_post(req, ret ?: -ETIME, 0);
io_put_req(prev); io_put_req(prev);
} else { } else {
...@@ -9257,10 +9299,8 @@ static void io_destroy_buffers(struct io_ring_ctx *ctx) ...@@ -9257,10 +9299,8 @@ static void io_destroy_buffers(struct io_ring_ctx *ctx)
struct io_buffer *buf; struct io_buffer *buf;
unsigned long index; 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); __io_remove_buffers(ctx, buf, index, -1U);
cond_resched();
}
} }
static void io_req_caches_free(struct io_ring_ctx *ctx) 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) ...@@ -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_kiocb *req = container_of(work, struct io_kiocb, work);
struct io_task_cancel *cancel = data; 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 */ return io_match_task_safe(req, cancel->task, cancel->all);
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;
} }
static __cold bool io_cancel_defer_files(struct io_ring_ctx *ctx, 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, ...@@ -9587,14 +9616,12 @@ static __cold bool io_cancel_defer_files(struct io_ring_ctx *ctx,
LIST_HEAD(list); LIST_HEAD(list);
spin_lock(&ctx->completion_lock); spin_lock(&ctx->completion_lock);
spin_lock_irq(&ctx->timeout_lock);
list_for_each_entry_reverse(de, &ctx->defer_list, list) { 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); list_cut_position(&list, &ctx->defer_list, &de->list);
break; break;
} }
} }
spin_unlock_irq(&ctx->timeout_lock);
spin_unlock(&ctx->completion_lock); spin_unlock(&ctx->completion_lock);
if (list_empty(&list)) if (list_empty(&list))
return false; return false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment