-
Notifications
You must be signed in to change notification settings - Fork 349
userspace: proxy: Add support for llext modules #10643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c51277d
2da0845
7fa81f8
761633a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -26,6 +26,7 @@ | |||||
| #include <stdint.h> | ||||||
|
|
||||||
| #include <sof/lib_manager.h> | ||||||
| #include <sof/llext_manager.h> | ||||||
| #include <sof/audio/component.h> | ||||||
| #include <sof/schedule/dp_schedule.h> | ||||||
| #include <rtos/userspace_helper.h> | ||||||
|
|
@@ -163,6 +164,7 @@ static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap | |||||
| work_item->event = &worker.event; | ||||||
| #endif | ||||||
| work_item->params.context = user_ctx; | ||||||
| work_item->params.mod = NULL; | ||||||
| user_ctx->work_item = work_item; | ||||||
|
|
||||||
| return 0; | ||||||
|
|
@@ -274,8 +276,24 @@ static int userspace_proxy_memory_init(struct userspace_context *user_ctx, | |||||
| tr_dbg(&userspace_proxy_tr, "Heap partition %#lx + %zx, attr = %u", | ||||||
| heap_part.start, heap_part.size, heap_part.attr); | ||||||
|
|
||||||
| #if !defined(CONFIG_XTENSA_MMU_DOUBLE_MAP) && defined(CONFIG_SOF_ZEPHYR_HEAP_CACHED) | ||||||
| #define HEAP_PART_CACHED | ||||||
| /* When a new memory domain is created, only the "factory" entries from the L2 page | ||||||
| * tables are copied. Memory that was dynamically mapped during firmware execution | ||||||
| * will not be accessible from the new domain. The k_heap structure (drv->user_heap) | ||||||
| * resides in such dynamically mapped memory, so we must explicitly add a partition | ||||||
| * for it to ensure that syscalls can access this structure from the userspace domain. | ||||||
| */ | ||||||
| struct k_mem_partition heap_struct_part; | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this creates an uninitialised object on stack. Below all its fields are set individually, but if the structure changes and fields are added to it, they will end up uninitialised here. Doing something like would be safer. |
||||||
|
|
||||||
| k_mem_region_align(&heap_struct_part.start, &heap_struct_part.size, | ||||||
| POINTER_TO_UINT(drv->user_heap), | ||||||
| sizeof(*drv->user_heap), CONFIG_MM_DRV_PAGE_SIZE); | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this mean that
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its grants access for kernel mode only. |
||||||
| heap_struct_part.attr = K_MEM_PARTITION_P_RW_U_NA | | ||||||
softwarecki marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| user_get_partition_attr(heap_struct_part.start); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about: user_get_partition_attr -> user_get_cache_attr OR user_get_cache_part_attr? |
||||||
|
|
||||||
| tr_err(&userspace_proxy_tr, "Heap struct partition %#lx + %zx, attr = %u", | ||||||
|
||||||
| tr_err(&userspace_proxy_tr, "Heap struct partition %#lx + %zx, attr = %u", | |
| tr_dbg(&userspace_proxy_tr, "Heap struct partition %#lx + %zx, attr = %u", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated to this PR, but you might want to revisit your memory domain life cycle. Currently memory domain destruction isn't really supported by the Zephyr version, that SOF is linking to. Recently 3032b58f52d776296a6e7e9c1a9c44b87f3cf019 has been merged into Zephyr, which makes it possible, but even with that domains have to be freed explicitly
softwarecki marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does llext_manager_add_domain really add a domain to something (thread i guess?)? If not, I encourage you to change the function name to *prep_domain or *init_domain. The previous use case for llext_manager_add_domain in scheduler_dp_task_init is straightforward whereas this use case is much harder to understand. A better name would greatly improve readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The llext_manager_add_domain() adds a llext module memory partitions to a given domain (second argument).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it doesn't add a domain itself, i.e. a domain remains unassigned. This caused my confusion and I started to wonder why the domain isn't added for the non-llext case. I suspected a bug, but then I figured out that this function only adds partitions, but the domain itself is assigned to the thread in another place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, the memory domain is a separate entity. Memory partitions and threads are added to a domain. Multiple threads can be assigned to the same memory domain using the k_mem_domain_add_thread() and share its mappings.
softwarecki marked this conversation as resolved.
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -150,7 +150,11 @@ const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) | |||||||||
| alloc_align = PLATFORM_DCACHE_ALIGN; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (size > FAST_GET_MAX_COPY_SIZE || !IS_ENABLED(CONFIG_USERSPACE)) | ||||||||||
| if (size > FAST_GET_MAX_COPY_SIZE || !IS_ENABLED(CONFIG_USERSPACE) || | ||||||||||
| /* The module driver heap is shared by all instances of a given module. | ||||||||||
| * Instances can share the allocated buffer. | ||||||||||
| */ | ||||||||||
| IS_ENABLED(CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP)) | ||||||||||
| alloc_ptr = dram_ptr; | ||||||||||
| else | ||||||||||
| /* When userspace is enabled only share large buffers */ | ||||||||||
|
|
@@ -183,8 +187,8 @@ const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) | |||||||||
| ret = entry->sram_ptr; | ||||||||||
|
|
||||||||||
| #if CONFIG_USERSPACE | ||||||||||
| /* We only get there for large buffers */ | ||||||||||
| if (k_current_get()->mem_domain_info.mem_domain->num_partitions > 1) { | ||||||||||
| /* We only get there for large buffers or module driver heap is in use */ | ||||||||||
| if (k_current_get()->base.user_options & K_USER && size > FAST_GET_MAX_COPY_SIZE) { | ||||||||||
|
||||||||||
| if (k_current_get()->base.user_options & K_USER && size > FAST_GET_MAX_COPY_SIZE) { | |
| if ((k_current_get()->base.user_options & K_USER) && | |
| (size > FAST_GET_MAX_COPY_SIZE || | |
| IS_ENABLED(CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure I understand the comment correctly. "syscalls can access" - syscalls execute in kernel mode, so they can access everything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, syscalls executes in kernel mode but it still use userspace module memory domain
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@softwarecki do you mean that when a syscall is executed, it inherits page tables from the userspace context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lyakh Exactly, yes. That is why, when a new memory domain is created, the L2 page tables are copied. This provides access for kernel to selected memory regions.
@dcpleung Can you confirm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A new memory domain is created by copying the kernel pagetables. So during syscall, the memory can be accessed if it is accessible via kernel threads. If you have already mapped the heap area at boot as readable by kernel threads, syscall can use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, if the memory was mapped dynamically at runtime, it will not be present in the new domain and must be explicitly mapped there as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, if you allocate memory, you will need to explicitly add it via memory partition to a memory domain before domain associated user threads can access them. Kernel threads and syscalls depend on whether that memory block has been mapped at boot or via
k_mem_map(). If not, you definitely need to add it to the domain or else it cannot be accessed in kernel mode.