-
Notifications
You must be signed in to change notification settings - Fork 350
user-space ll: grant access to DAIs and DMAs #10533
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?
Conversation
Add helper functions to provide a list of all Zephyr devices and to grant a user thread access to them. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Add helper functions to provide a list of all Zephyr DMA devices and to grant a user thread access to them. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
If the low-latency domain tasks are run in user-space, grant the LL threads access to all DAI and DMA devices. In future, there may be need to filter access permissions more, and/or have variation between different build targets, but for now the user LL thread will have access to the same devices as it does in kernel space. Note that access to memory is separately controlled and in addition to having access to a DMA device, user-space thread also needs access to the memory before it can do DMA transactions. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Now that we have CONFIG_SOF_USERSPACE_LL, use it to conditionally drop some of the thread access helpers. Access to mailbox, DAIs and DMAs is currently only needed if LL thread is run in user-space. Leave these out if SOF is built for LL thread scheduling in kernel space. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
|
Looks like this when running the "userspace_ll" ztest on a Intel PTL system: |
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.
Pull request overview
Adds user-space support for LL scheduler threads by granting them access to all platform DAIs and DMAs, using existing SOF device enumeration helpers.
Changes:
- Add helpers to grant a user thread access to all DAI/DMA Zephyr devices
- Expose DMA/DAI device list APIs for platform-agnostic enumeration
- Integrate the new access grants into Zephyr LL domain thread registration
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| zephyr/lib/userspace_helper.c | Adds userspace helpers to grant access to all DAIs/DMAs and introduces logging |
| zephyr/lib/dma.c | Adds API to enumerate DMA Zephyr devices |
| zephyr/include/sof/lib/dma.h | Declares the new DMA device list API |
| zephyr/include/rtos/userspace_helper.h | Declares new userspace grant helper APIs |
| src/schedule/zephyr_domain.c | Grants DAI/DMA access to LL user threads during registration |
| src/lib/dai.c | Adds API to enumerate DAI Zephyr devices |
| src/include/sof/lib/dai-zephyr.h | Declares the new DAI device list API |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #ifdef CONFIG_SOF_USERSPACE_LL | ||
|
|
||
| int user_access_to_mailbox(struct k_mem_domain *domain, k_tid_t thread_id) |
Copilot
AI
Feb 9, 2026
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.
user_access_to_mailbox() is now compiled only when CONFIG_SOF_USERSPACE_LL is enabled, but it appears to be a general userspace helper (and is still declared under #if CONFIG_USERSPACE in the header). This creates a concrete risk of link failures or missing functionality in CONFIG_USERSPACE=y builds that don’t enable CONFIG_SOF_USERSPACE_LL. Consider moving the #ifdef CONFIG_SOF_USERSPACE_LL guard so it only wraps the new DAI/DMA grant helpers (not user_access_to_mailbox()), or alternatively provide stubs / adjust header declarations so the build remains consistent across configurations.
| k_thread_access_grant(thread, dt->sem, domain->lock, zephyr_domain->timer); | ||
| user_grant_dai_access_all(thread); | ||
| user_grant_dma_access_all(thread); |
Copilot
AI
Feb 9, 2026
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.
These calls are unconditional, but the implementations in userspace_helper.c are built only under CONFIG_USERSPACE && CONFIG_SOF_USERSPACE_LL (and currently the header declarations are not similarly guarded). This can lead to unresolved symbols in configurations where this file is compiled but CONFIG_SOF_USERSPACE_LL (or even CONFIG_USERSPACE) is off. Guard these calls with the same Kconfig option(s), and/or provide static inline no-op stubs in the header for non-enabled configurations.
| k_thread_access_grant(thread, dt->sem, domain->lock, zephyr_domain->timer); | |
| user_grant_dai_access_all(thread); | |
| user_grant_dma_access_all(thread); | |
| k_thread_access_grant(thread, dt->sem, domain->lock, zephyr_domain->timer); | |
| #if CONFIG_USERSPACE && CONFIG_SOF_USERSPACE_LL | |
| user_grant_dai_access_all(thread); | |
| user_grant_dma_access_all(thread); | |
| #endif |
| * | ||
| * @param thread user-space thread for which DAI access is granted | ||
| */ | ||
| void user_grant_dai_access_all(struct k_thread *thread); |
Copilot
AI
Feb 9, 2026
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.
These APIs are declared for all CONFIG_USERSPACE builds, but the implementations are under CONFIG_SOF_USERSPACE_LL in the .c file. To keep the public API consistent and avoid configuration-dependent link issues, either (1) wrap these declarations with #ifdef CONFIG_SOF_USERSPACE_LL to match the implementation, or (2) provide static inline no-op stubs when CONFIG_SOF_USERSPACE_LL is disabled.
| * | ||
| * @param thread user-space thread for which DMA access is granted | ||
| */ | ||
| void user_grant_dma_access_all(struct k_thread *thread); |
Copilot
AI
Feb 9, 2026
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.
These APIs are declared for all CONFIG_USERSPACE builds, but the implementations are under CONFIG_SOF_USERSPACE_LL in the .c file. To keep the public API consistent and avoid configuration-dependent link issues, either (1) wrap these declarations with #ifdef CONFIG_SOF_USERSPACE_LL to match the implementation, or (2) provide static inline no-op stubs when CONFIG_SOF_USERSPACE_LL is disabled.
| * \param count Pointer to store the number of devices in the list. | ||
| * \return Pointer to the array of device pointers. | ||
| */ | ||
| const struct device **dma_get_device_list(size_t *count); |
Copilot
AI
Feb 9, 2026
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.
Returning const struct device ** allows callers to modify the returned array contents (even if the devices themselves are const). Since this API is exposing a shared internal list, it should return an immutable list type: const struct device * const *. Same applies to dai_get_device_list() for consistency/safety.
| const struct device **dma_get_device_list(size_t *count); | |
| const struct device * const *dma_get_device_list(size_t *count); |
| const struct device **dma_get_device_list(size_t *count) | ||
| { | ||
| static const struct device *device_list[ARRAY_SIZE(dma)]; | ||
| int i; |
Copilot
AI
Feb 9, 2026
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.
dma_get_device_list() writes to a function-static array on every call. If multiple threads can call this concurrently, this introduces a data race on device_list[]. To avoid racy writes, populate the list once during a single-threaded init phase (e.g., in dmac_init()), or implement a one-time initialization guard that does not perform concurrent writes.
| int i; | ||
|
|
||
| if (count) | ||
| *count = ARRAY_SIZE(dma); | ||
|
|
||
| for (i = 0; i < ARRAY_SIZE(dma); i++) | ||
| device_list[i] = dma[i].z_dev; | ||
|
|
Copilot
AI
Feb 9, 2026
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.
dma_get_device_list() writes to a function-static array on every call. If multiple threads can call this concurrently, this introduces a data race on device_list[]. To avoid racy writes, populate the list once during a single-threaded init phase (e.g., in dmac_init()), or implement a one-time initialization guard that does not perform concurrent writes.
| int i; | |
| if (count) | |
| *count = ARRAY_SIZE(dma); | |
| for (i = 0; i < ARRAY_SIZE(dma); i++) | |
| device_list[i] = dma[i].z_dev; | |
| static struct k_spinlock device_list_lock; | |
| static int device_list_initialized; | |
| int i; | |
| k_spinlock_key_t key; | |
| if (count) | |
| *count = ARRAY_SIZE(dma); | |
| key = k_spin_lock(&device_list_lock); | |
| if (!device_list_initialized) { | |
| for (i = 0; i < ARRAY_SIZE(dma); i++) | |
| device_list[i] = dma[i].z_dev; | |
| device_list_initialized = 1; | |
| } | |
| k_spin_unlock(&device_list_lock, key); |
| for (i = 0; i < count; i++) { | ||
| k_thread_access_grant(thread, devices[i]); | ||
| LOG_DBG("Granted DMA device access: %d %s to thread %p", | ||
| i, devices[i]->name, thread); | ||
| } | ||
| } | ||
|
|
Copilot
AI
Feb 9, 2026
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.
Logging once per DMA device can become noisy and adds overhead (even at LOG_DBG, it still does formatting work depending on Zephyr logging configuration). Consider logging a single summary line (as done for DAI) or gating per-device debug logs behind a more verbose config.
| for (i = 0; i < count; i++) { | |
| k_thread_access_grant(thread, devices[i]); | |
| LOG_DBG("Granted DMA device access: %d %s to thread %p", | |
| i, devices[i]->name, thread); | |
| } | |
| } | |
| for (i = 0; i < count; i++) | |
| k_thread_access_grant(thread, devices[i]); | |
| LOG_DBG("Granted DMA access to thread %p for %zu devices", thread, count); | |
| } |
lgirdwood
left a comment
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.
Nothing major from me, just one question.
|
|
||
| const struct device **dai_get_device_list(size_t *count) | ||
| { | ||
| if (count) |
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.
Seems count is mandatory ? assert ?
A small set of patches that allow granting access to all DAIs and DMAs to a user-space thread, and this is case, the LL scheduler threads when run in user-space. This PR uses the existing helper code in SOF that allow to enumerate the devices in a platform-agnostic manner, so this PR works for all SOF build targerts.