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

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
Loading
Loading
Loading
Loading
+50 −23
Original line number Diff line number Diff line
@@ -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,9 +6923,10 @@ 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) {
		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);
@@ -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;