]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
firewire: core: reallocate buffer for FCP address handlers when more than 4 are regis...
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Sun, 3 Aug 2025 12:20:15 +0000 (21:20 +0900)
committerTakashi Sakamoto <o-takashi@sakamocchi.jp>
Sun, 3 Aug 2025 12:20:15 +0000 (21:20 +0900)
The former commit has a limitation that only up to 4 FCP address
handlers could be processed per request. Although it suffices for most
use cases, it is technically a regression.

This commit lifts the restriction by reallocating the buffer from kernel
heap when more than 4 handlers are registered. The allocation is performed
within RCU read-side critical section, thus it uses GCP_ATOMIC flag. The
buffer size is rounded up to the next power of two to align with kmalloc
allocation units.

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

index 7a62c660e912fa2b4d459d4682fee6736c0742e3..1d1c2d8f85aec8a09bf570f388cfc998addc8b1f 100644 (file)
@@ -960,7 +960,7 @@ static void handle_fcp_region_request(struct fw_card *card,
 {
        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;
+       int tcode, destination, source, i, count, buffer_size;
 
        if ((offset != (CSR_REGISTER_BASE | CSR_FCP_COMMAND) &&
             offset != (CSR_REGISTER_BASE | CSR_FCP_RESPONSE)) ||
@@ -983,13 +983,38 @@ static void handle_fcp_region_request(struct fw_card *card,
 
        count = 0;
        handlers = buffer_on_kernel_stack;
+       buffer_size = ARRAY_SIZE(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)) {
+                               if (count >= buffer_size) {
+                                       int next_size = buffer_size * 2;
+                                       struct fw_address_handler **buffer_on_kernel_heap;
+
+                                       if (handlers == buffer_on_kernel_stack)
+                                               buffer_on_kernel_heap = NULL;
+                                       else
+                                               buffer_on_kernel_heap = handlers;
+
+                                       buffer_on_kernel_heap =
+                                               krealloc_array(buffer_on_kernel_heap, next_size,
+                                                       sizeof(*buffer_on_kernel_heap), GFP_ATOMIC);
+                                       // FCP is used for purposes unrelated to significant system
+                                       // resources (e.g. storage or networking), so allocation
+                                       // failures are not considered so critical.
+                                       if (!buffer_on_kernel_heap)
+                                               break;
+
+                                       if (handlers == buffer_on_kernel_stack) {
+                                               memcpy(buffer_on_kernel_heap, buffer_on_kernel_stack,
+                                                      sizeof(buffer_on_kernel_stack));
+                                       }
+
+                                       handlers = buffer_on_kernel_heap;
+                                       buffer_size = next_size;
+                               }
                                get_address_handler(handler);
-                               handlers[count] = handler;
-                               if (++count >= ARRAY_SIZE(buffer_on_kernel_stack))
-                                       break;
+                               handlers[count++] = handler;
                        }
                }
        }
@@ -1002,6 +1027,9 @@ static void handle_fcp_region_request(struct fw_card *card,
                put_address_handler(handler);
        }
 
+       if (handlers != buffer_on_kernel_stack)
+               kfree(handlers);
+
        fw_send_response(card, request, RCODE_COMPLETE);
 }