From c19f2def62752bbc1a042138020c363906c6e28e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 Jan 2026 16:14:46 +0100 Subject: [PATCH] 6.1-stable patches added patches: btrfs-fix-deadlock-in-wait_current_trans-due-to-ignored-transaction-type.patch io_uring-move-local-task_work-in-exit-cancel-loop.patch --- ...rans-due-to-ignored-transaction-type.patch | 154 ++++++++++++++++++ ...-local-task_work-in-exit-cancel-loop.patch | 62 +++++++ queue-6.1/series | 2 + 3 files changed, 218 insertions(+) create mode 100644 queue-6.1/btrfs-fix-deadlock-in-wait_current_trans-due-to-ignored-transaction-type.patch create mode 100644 queue-6.1/io_uring-move-local-task_work-in-exit-cancel-loop.patch diff --git a/queue-6.1/btrfs-fix-deadlock-in-wait_current_trans-due-to-ignored-transaction-type.patch b/queue-6.1/btrfs-fix-deadlock-in-wait_current_trans-due-to-ignored-transaction-type.patch new file mode 100644 index 0000000000..7c60a08558 --- /dev/null +++ b/queue-6.1/btrfs-fix-deadlock-in-wait_current_trans-due-to-ignored-transaction-type.patch @@ -0,0 +1,154 @@ +From 5037b342825df7094a4906d1e2a9674baab50cb2 Mon Sep 17 00:00:00 2001 +From: Robbie Ko +Date: Thu, 11 Dec 2025 13:30:33 +0800 +Subject: btrfs: fix deadlock in wait_current_trans() due to ignored transaction type + +From: Robbie Ko + +commit 5037b342825df7094a4906d1e2a9674baab50cb2 upstream. + +When wait_current_trans() is called during start_transaction(), it +currently waits for a blocked transaction without considering whether +the given transaction type actually needs to wait for that particular +transaction state. The btrfs_blocked_trans_types[] array already defines +which transaction types should wait for which transaction states, but +this check was missing in wait_current_trans(). + +This can lead to a deadlock scenario involving two transactions and +pending ordered extents: + + 1. Transaction A is in TRANS_STATE_COMMIT_DOING state + + 2. A worker processing an ordered extent calls start_transaction() + with TRANS_JOIN + + 3. join_transaction() returns -EBUSY because Transaction A is in + TRANS_STATE_COMMIT_DOING + + 4. Transaction A moves to TRANS_STATE_UNBLOCKED and completes + + 5. A new Transaction B is created (TRANS_STATE_RUNNING) + + 6. The ordered extent from step 2 is added to Transaction B's + pending ordered extents + + 7. Transaction B immediately starts commit by another task and + enters TRANS_STATE_COMMIT_START + + 8. The worker finally reaches wait_current_trans(), sees Transaction B + in TRANS_STATE_COMMIT_START (a blocked state), and waits + unconditionally + + 9. However, TRANS_JOIN should NOT wait for TRANS_STATE_COMMIT_START + according to btrfs_blocked_trans_types[] + + 10. Transaction B is waiting for pending ordered extents to complete + + 11. Deadlock: Transaction B waits for ordered extent, ordered extent + waits for Transaction B + +This can be illustrated by the following call stacks: + CPU0 CPU1 + btrfs_finish_ordered_io() + start_transaction(TRANS_JOIN) + join_transaction() + # -EBUSY (Transaction A is + # TRANS_STATE_COMMIT_DOING) + # Transaction A completes + # Transaction B created + # ordered extent added to + # Transaction B's pending list + btrfs_commit_transaction() + # Transaction B enters + # TRANS_STATE_COMMIT_START + # waiting for pending ordered + # extents + wait_current_trans() + # waits for Transaction B + # (should not wait!) + +Task bstore_kv_sync in btrfs_commit_transaction waiting for ordered +extents: + + __schedule+0x2e7/0x8a0 + schedule+0x64/0xe0 + btrfs_commit_transaction+0xbf7/0xda0 [btrfs] + btrfs_sync_file+0x342/0x4d0 [btrfs] + __x64_sys_fdatasync+0x4b/0x80 + do_syscall_64+0x33/0x40 + entry_SYSCALL_64_after_hwframe+0x44/0xa9 + +Task kworker in wait_current_trans waiting for transaction commit: + + Workqueue: btrfs-syno_nocow btrfs_work_helper [btrfs] + __schedule+0x2e7/0x8a0 + schedule+0x64/0xe0 + wait_current_trans+0xb0/0x110 [btrfs] + start_transaction+0x346/0x5b0 [btrfs] + btrfs_finish_ordered_io.isra.0+0x49b/0x9c0 [btrfs] + btrfs_work_helper+0xe8/0x350 [btrfs] + process_one_work+0x1d3/0x3c0 + worker_thread+0x4d/0x3e0 + kthread+0x12d/0x150 + ret_from_fork+0x1f/0x30 + +Fix this by passing the transaction type to wait_current_trans() and +checking btrfs_blocked_trans_types[cur_trans->state] against the given +type before deciding to wait. This ensures that transaction types which +are allowed to join during certain blocked states will not unnecessarily +wait and cause deadlocks. + +Reviewed-by: Filipe Manana +Signed-off-by: Robbie Ko +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Cc: Motiejus Jakštys +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/transaction.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +--- a/fs/btrfs/transaction.c ++++ b/fs/btrfs/transaction.c +@@ -503,13 +503,14 @@ static inline int is_transaction_blocked + * when this is done, it is safe to start a new transaction, but the current + * transaction might not be fully on disk. + */ +-static void wait_current_trans(struct btrfs_fs_info *fs_info) ++static void wait_current_trans(struct btrfs_fs_info *fs_info, unsigned int type) + { + struct btrfs_transaction *cur_trans; + + spin_lock(&fs_info->trans_lock); + cur_trans = fs_info->running_transaction; +- if (cur_trans && is_transaction_blocked(cur_trans)) { ++ if (cur_trans && is_transaction_blocked(cur_trans) && ++ (btrfs_blocked_trans_types[cur_trans->state] & type)) { + refcount_inc(&cur_trans->use_count); + spin_unlock(&fs_info->trans_lock); + +@@ -661,12 +662,12 @@ again: + sb_start_intwrite(fs_info->sb); + + if (may_wait_transaction(fs_info, type)) +- wait_current_trans(fs_info); ++ wait_current_trans(fs_info, type); + + do { + ret = join_transaction(fs_info, type); + if (ret == -EBUSY) { +- wait_current_trans(fs_info); ++ wait_current_trans(fs_info, type); + if (unlikely(type == TRANS_ATTACH || + type == TRANS_JOIN_NOSTART)) + ret = -ENOENT; +@@ -948,7 +949,7 @@ out: + + void btrfs_throttle(struct btrfs_fs_info *fs_info) + { +- wait_current_trans(fs_info); ++ wait_current_trans(fs_info, TRANS_START); + } + + static bool should_end_transaction(struct btrfs_trans_handle *trans) diff --git a/queue-6.1/io_uring-move-local-task_work-in-exit-cancel-loop.patch b/queue-6.1/io_uring-move-local-task_work-in-exit-cancel-loop.patch new file mode 100644 index 0000000000..00b11c3170 --- /dev/null +++ b/queue-6.1/io_uring-move-local-task_work-in-exit-cancel-loop.patch @@ -0,0 +1,62 @@ +From da579f05ef0faada3559e7faddf761c75cdf85e1 Mon Sep 17 00:00:00 2001 +From: Ming Lei +Date: Wed, 14 Jan 2026 16:54:05 +0800 +Subject: io_uring: move local task_work in exit cancel loop + +From: Ming Lei + +commit da579f05ef0faada3559e7faddf761c75cdf85e1 upstream. + +With IORING_SETUP_DEFER_TASKRUN, task work is queued to ctx->work_llist +(local work) rather than the fallback list. During io_ring_exit_work(), +io_move_task_work_from_local() was called once before the cancel loop, +moving work from work_llist to fallback_llist. + +However, task work can be added to work_llist during the cancel loop +itself. There are two cases: + +1) io_kill_timeouts() is called from io_uring_try_cancel_requests() to +cancel pending timeouts, and it adds task work via io_req_queue_tw_complete() +for each cancelled timeout: + +2) URING_CMD requests like ublk can be completed via +io_uring_cmd_complete_in_task() from ublk_queue_rq() during canceling, +given ublk request queue is only quiesced when canceling the 1st uring_cmd. + +Since io_allowed_defer_tw_run() returns false in io_ring_exit_work() +(kworker != submitter_task), io_run_local_work() is never invoked, +and the work_llist entries are never processed. This causes +io_uring_try_cancel_requests() to loop indefinitely, resulting in +100% CPU usage in kworker threads. + +Fix this by moving io_move_task_work_from_local() inside the cancel +loop, ensuring any work on work_llist is moved to fallback before +each cancel attempt. + +Cc: stable@vger.kernel.org +Fixes: c0e0d6ba25f1 ("io_uring: add IORING_SETUP_DEFER_TASKRUN") +Signed-off-by: Ming Lei +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman +--- + io_uring/io_uring.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/io_uring/io_uring.c ++++ b/io_uring/io_uring.c +@@ -2867,11 +2867,11 @@ static __cold void io_ring_exit_work(str + * as nobody else will be looking for them. + */ + do { +- if (ctx->flags & IORING_SETUP_DEFER_TASKRUN) +- io_move_task_work_from_local(ctx); +- +- while (io_uring_try_cancel_requests(ctx, NULL, true)) ++ do { ++ if (ctx->flags & IORING_SETUP_DEFER_TASKRUN) ++ io_move_task_work_from_local(ctx); + cond_resched(); ++ } while (io_uring_try_cancel_requests(ctx, NULL, true)); + + if (ctx->sq_data) { + struct io_sq_data *sqd = ctx->sq_data; diff --git a/queue-6.1/series b/queue-6.1/series index 2e230652da..2f57ddd486 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -74,3 +74,5 @@ dmaengine-sh-rz-dmac-fix-rz_dmac_terminate_all.patch dmaengine-ti-dma-crossbar-fix-device-leak-on-dra7x-route-allocation.patch dmaengine-ti-dma-crossbar-fix-device-leak-on-am335x-route-allocation.patch dmaengine-ti-k3-udma-fix-device-leak-on-udma-lookup.patch +btrfs-fix-deadlock-in-wait_current_trans-due-to-ignored-transaction-type.patch +io_uring-move-local-task_work-in-exit-cancel-loop.patch -- 2.47.3