Skip to content

Commit 7c527c1

Browse files
committed
firewire: core: use reference counting to invoke address handlers safely
The lifetime of address handler has been managed by linked list and RCU. This approach was introduced in commit 35202f7 ("firewire: remove global lock around address handlers, convert to RCU"). The invocations of address handler are performed within RCU read-side critical sections. In commit 57e6d9f ("firewire: ohci: use workqueue to handle events of AR request/response contexts"), the invocations are in a workqueue context. The approach still imposes limitation that sleeping is not allowed within RCU read-side critical sections. However, since sleeping is not permitted within RCU read-side critical sections, this approach still has a limitation. This commit adds reference counting to decouple handler invocation from handler discovery. The linked list and RCU is used to discover the handlers, while the reference counting is used to invoke them safely. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Sakamoto <[email protected]>
1 parent 8f5ae30 commit 7c527c1

File tree

2 files changed

+34
-2
lines changed

2 files changed

+34
-2
lines changed

drivers/firewire/core-transaction.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,23 @@ const struct fw_address_region fw_unit_space_region =
550550
{ .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, };
551551
#endif /* 0 */
552552

553+
static void complete_address_handler(struct kref *kref)
554+
{
555+
struct fw_address_handler *handler = container_of(kref, struct fw_address_handler, kref);
556+
557+
complete(&handler->done);
558+
}
559+
560+
static void get_address_handler(struct fw_address_handler *handler)
561+
{
562+
kref_get(&handler->kref);
563+
}
564+
565+
static int put_address_handler(struct fw_address_handler *handler)
566+
{
567+
return kref_put(&handler->kref, complete_address_handler);
568+
}
569+
553570
/**
554571
* fw_core_add_address_handler() - register for incoming requests
555572
* @handler: callback
@@ -596,6 +613,8 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
596613
if (other != NULL) {
597614
handler->offset += other->length;
598615
} else {
616+
init_completion(&handler->done);
617+
kref_init(&handler->kref);
599618
list_add_tail_rcu(&handler->link, &address_handler_list);
600619
ret = 0;
601620
break;
@@ -621,6 +640,9 @@ void fw_core_remove_address_handler(struct fw_address_handler *handler)
621640
list_del_rcu(&handler->link);
622641

623642
synchronize_rcu();
643+
644+
if (!put_address_handler(handler))
645+
wait_for_completion(&handler->done);
624646
}
625647
EXPORT_SYMBOL(fw_core_remove_address_handler);
626648

@@ -913,10 +935,13 @@ static void handle_exclusive_region_request(struct fw_card *card,
913935
scoped_guard(rcu) {
914936
handler = lookup_enclosing_address_handler(&address_handler_list, offset,
915937
request->length);
916-
if (handler)
938+
if (handler) {
939+
get_address_handler(handler);
917940
handler->address_callback(card, request, tcode, destination, source,
918941
p->generation, offset, request->data,
919942
request->length, handler->callback_data);
943+
put_address_handler(handler);
944+
}
920945
}
921946

922947
if (!handler)
@@ -952,10 +977,13 @@ static void handle_fcp_region_request(struct fw_card *card,
952977

953978
scoped_guard(rcu) {
954979
list_for_each_entry_rcu(handler, &address_handler_list, link) {
955-
if (is_enclosing_handler(handler, offset, request->length))
980+
if (is_enclosing_handler(handler, offset, request->length)) {
981+
get_address_handler(handler);
956982
handler->address_callback(card, request, tcode, destination, source,
957983
p->generation, offset, request->data,
958984
request->length, handler->callback_data);
985+
put_address_handler(handler);
986+
}
959987
}
960988
}
961989

include/linux/firewire.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,11 @@ struct fw_address_handler {
341341
u64 length;
342342
fw_address_callback_t address_callback;
343343
void *callback_data;
344+
345+
// Only for core functions.
344346
struct list_head link;
347+
struct kref kref;
348+
struct completion done;
345349
};
346350

347351
struct fw_address_region {

0 commit comments

Comments
 (0)