From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793210264; b=QtlHXJNWiOofeo71JSLk6jGO+E0cqNB0w4u3lPzzsWgiMXmears22YWMRlDfDWya0SHq WepYpq5JL+vglLWK2AeTtbf4Mp/fgUW25xqvZVPY6Sh+p2h6CQf950uslWQSfwZyGT31F fBlBZQJUpnQdGdwwSccZ6EB8xxmnU6+zocttxajBLJz0Eki3m77XFmT8gCBhikfMN3pyl R4flBbSymhr6nc72TMzokcEbrZ44zF11QgTTgRaaKb9DOfn18l4LFYRTVe4bsHMpzW1qI JTESCkAcW9Qmk9KkHh4/lHdsL9G5lTI59PObukVavtSkSOnslOPaiAgWV3hjnGeGaUQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793210264; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:User-Agent:X-stable:X-Patchwork-Hint: MIME-Version:Content-Transfer-Encoding; bh=4MxdmrtwcXZty5khO5SFrgPnGbKB40Cer/FAxUeT4qk=; b=24V1sBwIYHbdcakrouUlcOOZQNCZnMLTY4w0V5+rvruSuDjQuc3gYHKobYLFE+iHG+oS Lkq6qi6UH1VQZmEcxN0NlXUXZb7KRSyGnvQfWtUYNYqGIMRjhocEte2GBEBYb//DLIDAt Ez6HxH0uFvHW1HYQ3dxF6V9bLgXHjKF8bRNgAFD/bbP14rwROu3xQKNKAYdzKjburS/Ue jenZ5vwOYgj8nYDyyzgILpzjICGj7sb7LcubHZ1MegUB0HaoLPqGpBAO7E4SXBnhz8h+g oMtUsnRjk5Ms+DJ4AdeErNKWmYNlInXLxIa73PDqvOEZSZ6c3dQ/KtqmMvNW1CA3VnQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=KMRrQWjr; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3BBC4606A321; Wed, 28 Oct 2026 17:57:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=KMRrQWjr Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8BE5E1F01825; Wed, 28 Oct 2026 17:57:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1793210264; bh=4MxdmrtwcXZty5khO5SFrgPnGbKB40Cer/FAxUeT4qk=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=KMRrQWjrb04RZy52Ww3pCNcWhYHUW6uuUtrHvzQ8X/qYioA1sCrs9OBoYRiDtBxxq kQtp/gceEwZNuwqcobDIEmMQ54wVLa/1jQjLjyPPqYnzWh8SZi5dJ6GylX+7YvSVoS 64Wp1hdzELyuEMTqWJXiCk+yj5ZOR73bY6ghVMI8= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Josef Bacik , Jens Axboe , "Claude Fable 5.1" Subject: [PATCH 6.6 133/168] io_uring/io-wq: let cancel find picked up hashed work Date: Wed, 28 Oct 2026 18:01:09 +0100 Message-ID: <20261028165946.233915718@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261028165943.089486480@linuxfoundation.org> References: <20261028165943.089486480@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore X-Mailing-List: patches@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.6-stable review patch. If anyone has any objections, please let me know. ------------------ From: Josef Bacik Commit e9efeb4fc97b0879512a3c3b995ed448f2487e12 upstream. io_get_next_work() takes all the work items that share a hash off the work list in one go, and the worker runs them one after the other. Only the one it is working on is visible as ->cur_work. The others are neither on the list nor current, so io_wq_cancel_cb() can't find them. IORING_OP_ASYNC_CANCEL and IORING_REGISTER_SYNC_CANCEL return -ENOENT for a request that has not even been started, e.g. a buffered write queued behind another write to the same file that is blocked, and the request is carried out. The cancellation done for a task or ring that goes away gets there in the end, as it keeps trying, but it has to wait for each item to become current. Keep a pointer to the remainder of the chain next to ->cur_work, and have io_wq_worker_cancel() look at it. The items on it belong to the worker, which needs ->lock to move on to the next one. Matching work is flagged with IO_WQ_WORK_CANCEL and fails with -ECANCELED when its turn comes. The worker isn't signalled for it, that would interrupt the unrelated work it is busy with. As the request isn't gone yet, it is reported like running work, with -EALREADY. [ 6.6: ->next_work was the item being picked up, which io_wq_worker_cancel() checked next to ->cur_work. Set ->cur_work in that window instead, as upstream does, and give ->next_work the new meaning. work->flags is not atomic here ] Fixes: 86f3cd1b589a ("io-wq: handle hashed writes in chains") Assisted-by: LLM Signed-off-by: Josef Bacik Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-13-c1556f5d7bf5@toxicpanda.com Signed-off-by: Jens Axboe Co-Authored-By: Claude Fable 5.1 Signed-off-by: Greg Kroah-Hartman --- io_uring/io-wq.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) --- a/io_uring/io-wq.c +++ b/io_uring/io-wq.c @@ -54,6 +54,7 @@ struct io_worker { struct io_wq *wq; struct io_wq_work *cur_work; + /* rest of the hashed chain ->cur_work was taken off the list with */ struct io_wq_work *next_work; raw_spinlock_t lock; @@ -536,6 +537,17 @@ static struct io_wq_work *io_get_next_wo return NULL; } +static void __io_assign_current_work(struct io_worker *worker, + struct io_wq_work *work) +{ + raw_spin_lock(&worker->lock); + worker->cur_work = work; + /* stays valid while in between two items of a chain */ + if (work) + worker->next_work = wq_next_work(work); + raw_spin_unlock(&worker->lock); +} + static void io_assign_current_work(struct io_worker *worker, struct io_wq_work *work) { @@ -544,10 +556,7 @@ static void io_assign_current_work(struc cond_resched(); } - raw_spin_lock(&worker->lock); - worker->cur_work = work; - worker->next_work = NULL; - raw_spin_unlock(&worker->lock); + __io_assign_current_work(worker, work); } /* @@ -578,9 +587,7 @@ static void io_worker_handle_work(struct * work list, but isn't yet discoverable as the * current work item for this worker. */ - raw_spin_lock(&worker->lock); - worker->next_work = work; - raw_spin_unlock(&worker->lock); + __io_assign_current_work(worker, work); } raw_spin_unlock(&acct->lock); @@ -1083,15 +1090,28 @@ static bool __io_wq_worker_cancel(struct static bool io_wq_worker_cancel(struct io_worker *worker, void *data) { struct io_cb_cancel_data *match = data; + struct io_wq_work *work; /* * Hold the lock to avoid ->cur_work going out of scope, caller * may dereference the passed in work. */ raw_spin_lock(&worker->lock); - if (__io_wq_worker_cancel(worker, match, worker->cur_work) || - __io_wq_worker_cancel(worker, match, worker->next_work)) + if (__io_wq_worker_cancel(worker, match, worker->cur_work)) match->nr_running++; + /* + * Hashed work is taken off the work list a chain at a time, what the + * worker hasn't started yet can only be found here. It doesn't move on + * without the lock. Just mark the work, it fails once it is its turn. + */ + for (work = worker->next_work; work; work = wq_next_work(work)) { + if (match->nr_running && !match->cancel_all) + break; + if (match->fn(work, match->data)) { + work->flags |= IO_WQ_WORK_CANCEL; + match->nr_running++; + } + } raw_spin_unlock(&worker->lock); return match->nr_running && !match->cancel_all;