[PATCH 2/3] firewire: core: use workqueue to invoke transaction callback in some error cases

From: Takashi Sakamoto

Date: Sun Sep 20 2026 - 23:21:07 EST


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.

Signed-off-by: Takashi Sakamoto <o-takashi@xxxxxxxxxxxxx>
---
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 0cad55763a3e..e59b347ff248 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;
}
@@ -154,6 +154,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);
@@ -165,7 +180,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.
@@ -383,6 +398,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
@@ -402,7 +418,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 2b065f03565d..a3a3a9ec1b21 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -362,6 +362,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 {
--
2.53.0