From 34dca2ecd4021a26762f16f32fb94f8e591b8fba Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 7 Oct 2026 18:32:33 +0900 Subject: [PATCH] firewire: ohci: allow AT local packet cancellation The current implementation uses a different path for local packets than for remote packets, and local packets cannot be cancelled once they have been queued. Allow local packets to be cancelled. Link: https://lore.kernel.org/r/20261007093235.175742-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 896d0cb9a895..c06e8d87239c 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1597,7 +1597,7 @@ static void handle_at_local_packets(struct at_local *local, struct fw_ohci *ohci spin_lock(&local->lock); while ((packet = list_first_entry_or_null(&local->list, typeof(*packet), link_for_local))) { - list_del(&packet->link_for_local); + list_del_init(&packet->link_for_local); spin_unlock(&local->lock); if (unlikely(packet->ack != 0)) { @@ -1671,6 +1671,7 @@ static void at_context_transmit(struct at_context *ctx, struct fw_packet *packet // These members are used to cancel the packet. packet->driver_data = NULL; + INIT_LIST_HEAD(&packet->link_for_local); scoped_guard(spinlock_irqsave, &ohci->lock) { if (!destination_is_local(packet, ohci)) { @@ -2658,6 +2659,10 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) struct at_local *local = &ohci->at_request_local; int ret = -ENOENT; + // handle_at_request_local_packet() can reach here by calling fw_core_handle_response(). + if (current_work() != &local->work) + disable_work_sync(&local->work); + // Avoid dead lock due to programming mistake. if (WARN_ON_ONCE(current_work() == &ctx->work)) return 0; @@ -2681,6 +2686,11 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) scoped_guard(spinlock_irqsave, &local->lock) list_add_tail(&packet->link_for_local, &local->list); use_local_work = true; + } else { + scoped_guard(spinlock_irqsave, &local->lock) { + if (!list_empty(&packet->link_for_local)) + use_local_work = true; + } } } @@ -2690,11 +2700,13 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) // Timestamping on behalf of the hardware. packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ohci)); packet->ack = RCODE_CANCELLED; - queue_work(card->async_wq, &local->work); ret = 0; } + if (current_work() != &local->work) + enable_and_queue_work(card->async_wq, &local->work); + return ret; }