From 767e87bb6f0f721e0b97640780ff3ab1d413b329 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2026 15:30:54 +0900 Subject: [PATCH] firewire: core: use workqueue to cancel all pending transactions on card removal When a card is being removed, all pending transactions are canceled by calling fw_cancel_pending_transactions(). This function moves the entries for unfinished transactions to a local list so that their callbacks can be invoked without holding the transaction lock. However, this complicates the management of pending transactions. Use workqueue to invoke error callbacks for all pending transactions. Link: https://lore.kernel.org/r/20261018063056.329226-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-card.c | 4 +++- drivers/firewire/core-transaction.c | 37 ++++++++++++----------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 83c29a487d86..a43fc3a278f2 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -811,7 +811,9 @@ void fw_core_remove_card(struct fw_card *card) drain_workqueue(card->isoc_wq); drain_workqueue(card->async_wq); card->driver->disable(card); - fw_cancel_pending_transactions(card); + + scoped_guard(spinlock_irqsave, &card->transactions.lock) + fw_cancel_pending_transactions(card); scoped_guard(spinlock_irqsave, &card->lock) fw_destroy_nodes(card); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 08536275be12..8e4951631185 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -69,28 +69,6 @@ static void invoke_callback(struct fw_transaction *t, int rcode, u32 response_ts } } -// Must be called without holding card->transactions.lock. -void fw_cancel_pending_transactions(struct fw_card *card) -__must_not_hold(&card->transactions.lock) -{ - struct fw_transaction *t, *tmp; - LIST_HEAD(pending_list); - - lockdep_assert_not_held(&card->transactions.lock); - - scoped_guard(spinlock_irqsave, &card->transactions.lock) { - list_for_each_entry_safe(t, tmp, &card->transactions.list, link) { - if (try_cancel_split_timeout(t)) - list_move(&t->link, &pending_list); - } - } - - list_for_each_entry_safe(t, tmp, &pending_list, link) { - list_del(&t->link); - invoke_callback(t, RCODE_CANCELLED, 0, NULL, 0); - } -} - // card->transactions.lock must be acquired in advance. #define find_and_pop_transaction_entry(card, condition) \ ({ \ @@ -118,6 +96,21 @@ static void schedule_error_callback(struct fw_transaction *t, int rcode, u32 res queue_work(t->card->async_wq, &t->error_work); } +void fw_cancel_pending_transactions(struct fw_card *card) +__must_hold(&card->transactions.lock) +{ + struct fw_transaction *t, *tmp; + + lockdep_assert_held(&card->transactions.lock); + + list_for_each_entry_safe(t, tmp, &card->transactions.list, link) { + if (try_cancel_split_timeout(t)) { + remove_transaction_entry(card, t); + schedule_error_callback(t, RCODE_CANCELLED, 0); + } + } +} + /** * fw_cancel_transaction: cancel transaction. * @card: interface where the transaction has been requested