Commit e884a8a0 authored by Takashi Sakamoto's avatar Takashi Sakamoto
Browse files

firewire: core: call FCP address handlers outside RCU read-side critical section

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: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
parent e8cf6875
Loading
Loading
Loading
Loading
+19 −6
Original line number Diff line number Diff line
@@ -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,17 +981,26 @@ 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);
				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);
}