diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 65bd9db996c0..9869ea3fd9fc 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -790,6 +790,7 @@ 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->lock) fw_destroy_nodes(card); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 642387f0d7ca..7be4ae3b36b5 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -51,6 +51,34 @@ static void remove_transaction_entry(struct fw_card *card, struct fw_transaction card->transactions.tlabel_mask &= ~(1ULL << entry->tlabel); } +// Must be called without holding card->transactions.lock. +void fw_cancel_pending_transactions(struct fw_card *card) +{ + struct fw_transaction *t, *tmp; + LIST_HEAD(pending_list); + + // NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for + // local destination never runs in any type of IRQ context. + 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); + + if (!t->with_tstamp) { + t->callback.without_tstamp(card, RCODE_CANCELLED, NULL, 0, + t->callback_data); + } else { + t->callback.with_tstamp(card, RCODE_CANCELLED, t->packet.timestamp, 0, + NULL, 0, t->callback_data); + } + } +} + // card->transactions.lock must be acquired in advance. #define find_and_pop_transaction_entry(card, condition) \ ({ \ diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 903812b6bb3f..41fb39d9a4e6 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -287,6 +287,8 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header, void fw_request_get(struct fw_request *request); void fw_request_put(struct fw_request *request); +void fw_cancel_pending_transactions(struct fw_card *card); + // Convert the value of IEEE 1394 CYCLE_TIME register to the format of timeStamp field in // descriptors of 1394 OHCI. static inline u32 cycle_time_to_ohci_tstamp(u32 tstamp) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 0625d11dbd74..e3e78dc42530 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -3719,11 +3719,6 @@ static void pci_remove(struct pci_dev *dev) fw_core_remove_card(&ohci->card); - /* - * FIXME: Fail all pending packets here, now that the upper - * layers can't queue any more. - */ - software_reset(ohci); irq = pci_irq_vector(dev, 0);