Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'firewire-fixes-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394

Pull firewire fixes from Takashi Sakamoto:
"This fixes a potential call to schedule() within an RCU read-side
critical section. The solution applies reference counting to ensure
that handlers which may call schedule() are invoked safely outside of
the critical section"

* tag 'firewire-fixes-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394:
firewire: core: reallocate buffer for FCP address handlers when more than 4 are registered
firewire: core: call FCP address handlers outside RCU read-side critical section
firewire: core: call handler for exclusive regions outside RCU read-side critical section
firewire: core: use reference counting to invoke address handlers safely

+85 -10
+81 -10
drivers/firewire/core-transaction.c
··· 550 550 { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, }; 551 551 #endif /* 0 */ 552 552 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 + 553 570 /** 554 571 * fw_core_add_address_handler() - register for incoming requests 555 572 * @handler: callback ··· 613 596 if (other != NULL) { 614 597 handler->offset += other->length; 615 598 } else { 599 + init_completion(&handler->done); 600 + kref_init(&handler->kref); 616 601 list_add_tail_rcu(&handler->link, &address_handler_list); 617 602 ret = 0; 618 603 break; ··· 640 621 list_del_rcu(&handler->link); 641 622 642 623 synchronize_rcu(); 624 + 625 + if (!put_address_handler(handler)) 626 + wait_for_completion(&handler->done); 643 627 } 644 628 EXPORT_SYMBOL(fw_core_remove_address_handler); 645 629 ··· 936 914 handler = lookup_enclosing_address_handler(&address_handler_list, offset, 937 915 request->length); 938 916 if (handler) 939 - handler->address_callback(card, request, tcode, destination, source, 940 - p->generation, offset, request->data, 941 - request->length, handler->callback_data); 917 + get_address_handler(handler); 942 918 } 943 919 944 - if (!handler) 920 + if (!handler) { 945 921 fw_send_response(card, request, RCODE_ADDRESS_ERROR); 922 + return; 923 + } 924 + 925 + // Outside the RCU read-side critical section. Without spinlock. With reference count. 926 + handler->address_callback(card, request, tcode, destination, source, p->generation, offset, 927 + request->data, request->length, handler->callback_data); 928 + put_address_handler(handler); 946 929 } 930 + 931 + // To use kmalloc allocator efficiently, this should be power of two. 932 + #define BUFFER_ON_KERNEL_STACK_SIZE 4 947 933 948 934 static void handle_fcp_region_request(struct fw_card *card, 949 935 struct fw_packet *p, 950 936 struct fw_request *request, 951 937 unsigned long long offset) 952 938 { 953 - struct fw_address_handler *handler; 954 - int tcode, destination, source; 939 + struct fw_address_handler *buffer_on_kernel_stack[BUFFER_ON_KERNEL_STACK_SIZE]; 940 + struct fw_address_handler *handler, **handlers; 941 + int tcode, destination, source, i, count, buffer_size; 955 942 956 943 if ((offset != (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && 957 944 offset != (CSR_REGISTER_BASE | CSR_FCP_RESPONSE)) || ··· 981 950 return; 982 951 } 983 952 953 + count = 0; 954 + handlers = buffer_on_kernel_stack; 955 + buffer_size = ARRAY_SIZE(buffer_on_kernel_stack); 984 956 scoped_guard(rcu) { 985 957 list_for_each_entry_rcu(handler, &address_handler_list, link) { 986 - if (is_enclosing_handler(handler, offset, request->length)) 987 - handler->address_callback(card, request, tcode, destination, source, 988 - p->generation, offset, request->data, 989 - request->length, handler->callback_data); 958 + if (is_enclosing_handler(handler, offset, request->length)) { 959 + if (count >= buffer_size) { 960 + int next_size = buffer_size * 2; 961 + struct fw_address_handler **buffer_on_kernel_heap; 962 + 963 + if (handlers == buffer_on_kernel_stack) 964 + buffer_on_kernel_heap = NULL; 965 + else 966 + buffer_on_kernel_heap = handlers; 967 + 968 + buffer_on_kernel_heap = 969 + krealloc_array(buffer_on_kernel_heap, next_size, 970 + sizeof(*buffer_on_kernel_heap), GFP_ATOMIC); 971 + // FCP is used for purposes unrelated to significant system 972 + // resources (e.g. storage or networking), so allocation 973 + // failures are not considered so critical. 974 + if (!buffer_on_kernel_heap) 975 + break; 976 + 977 + if (handlers == buffer_on_kernel_stack) { 978 + memcpy(buffer_on_kernel_heap, buffer_on_kernel_stack, 979 + sizeof(buffer_on_kernel_stack)); 980 + } 981 + 982 + handlers = buffer_on_kernel_heap; 983 + buffer_size = next_size; 984 + } 985 + get_address_handler(handler); 986 + handlers[count++] = handler; 987 + } 990 988 } 991 989 } 990 + 991 + for (i = 0; i < count; ++i) { 992 + handler = handlers[i]; 993 + handler->address_callback(card, request, tcode, destination, source, 994 + p->generation, offset, request->data, 995 + request->length, handler->callback_data); 996 + put_address_handler(handler); 997 + } 998 + 999 + if (handlers != buffer_on_kernel_stack) 1000 + kfree(handlers); 992 1001 993 1002 fw_send_response(card, request, RCODE_COMPLETE); 994 1003 }
+4
include/linux/firewire.h
··· 341 341 u64 length; 342 342 fw_address_callback_t address_callback; 343 343 void *callback_data; 344 + 345 + // Only for core functions. 344 346 struct list_head link; 347 + struct kref kref; 348 + struct completion done; 345 349 }; 346 350 347 351 struct fw_address_region {