[PATCH 3/3] firewire: core: call FCP address handlers outside RCU read-side critical section
Takashi Sakamoto <[email protected]> Mon, 28 Jul 2025 10:51:25 +0900
| Newsgroups | gmane.linux.kernel.firewire.devel,gmane.linux.kernel |
|---|---|
| Message-ID | <[email protected]> |
The previous commit added reference counting to ensure safe invocations of address handlers. This commit moves the invocation of FCP address handlers outside of the RCU read critical section. Unlike the exclusive-region address handlers, all FCP address handlers should be called on receiving an FCP request. An XArray is used to collect the FCP address handlers during the RCU read-side critical section, after which they are invoked. Reference counting ensures that each FCP address handler is called safely. Signed-off-by: Takashi Sakamoto <[email protected]> --- drivers/firewire/core-transaction.c | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index a742971c65fa..c5408c83709c 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -956,13 +956,14 @@ static void handle_fcp_region_request(struct fw_card *card, unsigned long long offset) { struct fw_address_handler *handler; + DEFINE_XARRAY_ALLOC(handlers); int tcode, destination, source; + unsigned long id; if ((offset != (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && offset != (CSR_REGISTER_BASE | CSR_FCP_RESPONSE)) || request->length > 0x200) { fw_send_response(card, request, RCODE_ADDRESS_ERROR); - return; } @@ -973,22 +974,39 @@ static void handle_fcp_region_request(struct fw_card *card, if (tcode != TCODE_WRITE_QUADLET_REQUEST && tcode != TCODE_WRITE_BLOCK_REQUEST) { fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + // Reserve an entry outside the RCU read-side critical section to cover most cases. + id = 0; + if (xa_reserve(&handlers, id, GFP_KERNEL) < 0) { + fw_send_response(card, request, RCODE_CONFLICT_ERROR); return; } scoped_guard(rcu) { list_for_each_entry_rcu(handler, &address_handler_list, link) { if (is_enclosing_handler(handler, offset, request->length)) { - get_address_handler(handler); - handler->address_callback(card, request, tcode, destination, source, - p->generation, offset, request->data, - request->length, handler->callback_data); - put_address_handler(handler); + // FCP is used for purposes unrelated to significant system + // resources (e.g. storage or networking), so allocation + // failures are not considered so critical. + void *ptr = xa_store(&handlers, id, handler, GFP_ATOMIC); + if (!xa_is_err(ptr)) { + ++id; + get_address_handler(handler); + } } } } + xa_for_each(&handlers, id, handler) { + // Outside the RCU read-side critical section. Without spinlock. With reference count. + handler->address_callback(card, request, tcode, destination, source, p->generation, + offset, request->data, request->length, handler->callback_data); + put_address_handler(handler); + } + + xa_destroy(&handlers); fw_send_response(card, request, RCODE_COMPLETE); } -- 2.48.1