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 <o-takashi@sakamocchi.jp>
This commit is contained in:
Takashi Sakamoto 2026-10-18 15:30:54 +09:00
parent 3e7ed554a8
commit 767e87bb6f
2 changed files with 18 additions and 23 deletions

View File

@ -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);

View File

@ -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