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 <o-takashi@sakamocchi.jp>
This commit is contained in:
Takashi Sakamoto 2026-10-07 18:32:33 +09:00
parent 1d2f5118ba
commit 34dca2ecd4

View File

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