]> www.infradead.org Git - users/hch/misc.git/commitdiff
firewire: core: call FCP address handlers outside RCU read-side critical section
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Sun, 3 Aug 2025 12:20:14 +0000 (21:20 +0900)
committerTakashi Sakamoto <o-takashi@sakamocchi.jp>
Sun, 3 Aug 2025 12:20:14 +0000 (21:20 +0900)
The former commit added reference counting to ensure safe invocations of
address handlers. Unlike the exclusive-region address handlers, all FCP
address handlers should be called on receiving an FCP request.

This commit uses the part of kernel stack to collect address handlers up
to 4 within the section, then invoke them outside of the section.
Reference counting ensures that each handler remains valid and safe to
call.

Lifting the limitation of supporting only 4 handlers is left for next
work.

Link: https://lore.kernel.org/r/20250803122015.236493-4-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
drivers/firewire/core-transaction.c

index a742971c65fa6d0b25b634733749843ac2e0736b..7a62c660e912fa2b4d459d4682fee6736c0742e3 100644 (file)
@@ -950,13 +950,17 @@ static void handle_exclusive_region_request(struct fw_card *card,
        put_address_handler(handler);
 }
 
+// To use kmalloc allocator efficiently, this should be power of two.
+#define BUFFER_ON_KERNEL_STACK_SIZE    4
+
 static void handle_fcp_region_request(struct fw_card *card,
                                      struct fw_packet *p,
                                      struct fw_request *request,
                                      unsigned long long offset)
 {
-       struct fw_address_handler *handler;
-       int tcode, destination, source;
+       struct fw_address_handler *buffer_on_kernel_stack[BUFFER_ON_KERNEL_STACK_SIZE];
+       struct fw_address_handler *handler, **handlers;
+       int tcode, destination, source, i, count;
 
        if ((offset != (CSR_REGISTER_BASE | CSR_FCP_COMMAND) &&
             offset != (CSR_REGISTER_BASE | CSR_FCP_RESPONSE)) ||
@@ -977,18 +981,27 @@ static void handle_fcp_region_request(struct fw_card *card,
                return;
        }
 
+       count = 0;
+       handlers = buffer_on_kernel_stack;
        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);
+                               handlers[count] = handler;
+                               if (++count >= ARRAY_SIZE(buffer_on_kernel_stack))
+                                       break;
                        }
                }
        }
 
+       for (i = 0; i < count; ++i) {
+               handler = handlers[i];
+               handler->address_callback(card, request, tcode, destination, source,
+                                         p->generation, offset, request->data,
+                                         request->length, handler->callback_data);
+               put_address_handler(handler);
+       }
+
        fw_send_response(card, request, RCODE_COMPLETE);
 }