From 382ec2940670c273b297a6803333237d5f4a90f5 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 21 Sep 2026 12:19:30 +0900 Subject: [PATCH] firewire: core: use workqueue to invoke transaction callback in some error cases Some error paths of __fw_send_request() invoke the transaction callback in the caller's context. Additionally, when a split transaction times out, the callback is invoked in softIRQ context by the timer wheel. These are the only cases where the callback is not guaranteed to be invoked in process context. Use a workqueue to invoke the callback in these cases. This may introduce additional delay when a split transaction times out, but the default timeout is 2 seconds, so the additional delay should be negligible. Link: https://lore.kernel.org/r/20260921031931.287429-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-transaction.c | 22 +++++++++++++++++++--- include/linux/firewire.h | 5 +++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 00291042f48e..de80dd160363 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -40,7 +40,7 @@ static int try_cancel_split_timeout(struct fw_transaction *t) { if (t->is_split_transaction) - return timer_delete(&t->split_timeout_timer); + return timer_delete(&t->split_timeout_timer) || disable_work(&t->error_work); else return 1; } @@ -158,6 +158,21 @@ int fw_cancel_transaction(struct fw_card *card, } EXPORT_SYMBOL(fw_cancel_transaction); +static void error_callback_work(struct work_struct *work) +{ + struct fw_transaction *t = from_work(t, work, error_work); + + invoke_callback(t, t->rcode, t->response_timestamp, NULL, 0); +} + +static void schedule_error_callback(struct fw_transaction *t, int rcode, u32 response_timestamp) +{ + t->rcode = rcode; + t->response_timestamp = response_timestamp; + + queue_work(t->card->async_wq, &t->error_work); +} + static void split_transaction_timeout_callback(struct timer_list *timer) { struct fw_transaction *t = timer_container_of(t, timer, split_timeout_timer); @@ -169,7 +184,7 @@ static void split_transaction_timeout_callback(struct timer_list *timer) remove_transaction_entry(card, t); } - invoke_callback(t, RCODE_CANCELLED, t->split_timeout_cycle, NULL, 0); + schedule_error_callback(t, RCODE_CANCELLED, t->split_timeout_cycle); } // card->transactions.lock should be acquired in advance for the linked list. @@ -387,6 +402,7 @@ void __fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode t->callback = callback; t->with_tstamp = with_tstamp; t->callback_data = callback_data; + INIT_WORK(&t->error_work, error_callback_work); /* * Allocate tlabel from the bitmap and put the transaction on @@ -406,7 +422,7 @@ void __fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode tstamp = cycle_time_to_ohci_tstamp(curr_cycle_time); t->packet.timestamp = tstamp; - invoke_callback(t, RCODE_SEND_ERROR, tstamp, NULL, 0); + schedule_error_callback(t, RCODE_SEND_ERROR, tstamp); return; } diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 4bca5af6793d..a0a41a320e46 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -370,6 +370,11 @@ struct fw_transaction { union fw_transaction_callback callback; bool with_tstamp; void *callback_data; + + // For some error cases. + struct work_struct error_work; + int rcode; + u32 response_timestamp; }; struct fw_address_handler {