Skip to content

Conversation

@kv2019i
Copy link
Collaborator

@kv2019i kv2019i commented Feb 9, 2026

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.

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>
@kv2019i
Copy link
Collaborator Author

kv2019i commented Feb 9, 2026

Looks like this when running the "userspace_ll" ztest on a Intel PTL system:

Running TESTSUITE userspace_ll
===================================================================
START - ll_task_test
[    0.000095] <dbg> ll_schedule: zephyr_ll_task_init: ll-scheduler task 0x400de000 init
[    0.000095] <inf> sof_boot_test: ll_task_test: task init done
[    0.000095] <inf> ll_schedule: zephyr_ll_task_schedule_common: task add 0xa02422c0 0xa00d38e4U priority 0 flags 0x0
[    0.000095] <dbg> ll_schedule: zephyr_domain_register_user: entry
thread_alloc user, heap 0x400e09dc, sys-heap 0x400e09dc
thread_alloc user, heap 0x400e09dc, sys-heap 0x400e09dc
[    0.000095] <dbg> userspace_helper: user_grant_dai_access_all: Granted DAI access to thread 0x400d6640 for 45 devices
[    0.000095] <dbg> userspace_helper: user_grant_dma_access_all: Granted DMA device access: 0 dma@72c00 to thread 0x400d6640
[    0.000095] <dbg> userspace_helper: user_grant_dma_access_all: Granted DMA device access: 1 dma@72800 to thread 0x400d6640
[    0.000095] <dbg> userspace_helper: user_grant_dma_access_all: Granted DMA device access: 2 dma@79800 to thread 0x400d6640
[    0.000095] <dbg> userspace_helper: user_grant_dma_access_all: Granted DMA device access: 3 dma@79400 to thread 0x400d6640
[    0.000095] <dbg> ll_schedule: zephyr_domain_register_user: granted LL access to thread 0x400d6640 (core 0)

Copy link

Copilot AI left a 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.

Comment on lines +96 to 98
#ifdef CONFIG_SOF_USERSPACE_LL

int user_access_to_mailbox(struct k_mem_domain *domain, k_tid_t thread_id)
Copy link

Copilot AI Feb 9, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines 338 to +340
k_thread_access_grant(thread, dt->sem, domain->lock, zephyr_domain->timer);
user_grant_dai_access_all(thread);
user_grant_dma_access_all(thread);
Copy link

Copilot AI Feb 9, 2026

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.

Suggested change
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

Copilot uses AI. Check for mistakes.
*
* @param thread user-space thread for which DAI access is granted
*/
void user_grant_dai_access_all(struct k_thread *thread);
Copy link

Copilot AI Feb 9, 2026

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.

Copilot uses AI. Check for mistakes.
*
* @param thread user-space thread for which DMA access is granted
*/
void user_grant_dma_access_all(struct k_thread *thread);
Copy link

Copilot AI Feb 9, 2026

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.

Copilot uses AI. Check for mistakes.
* \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);
Copy link

Copilot AI Feb 9, 2026

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.

Suggested change
const struct device **dma_get_device_list(size_t *count);
const struct device * const *dma_get_device_list(size_t *count);

Copilot uses AI. Check for mistakes.
Comment on lines +222 to +225
const struct device **dma_get_device_list(size_t *count)
{
static const struct device *device_list[ARRAY_SIZE(dma)];
int i;
Copy link

Copilot AI Feb 9, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +225 to +232
int i;

if (count)
*count = ARRAY_SIZE(dma);

for (i = 0; i < ARRAY_SIZE(dma); i++)
device_list[i] = dma[i].z_dev;

Copy link

Copilot AI Feb 9, 2026

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.

Suggested change
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);

Copilot uses AI. Check for mistakes.
Comment on lines +160 to +166
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);
}
}

Copy link

Copilot AI Feb 9, 2026

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.

Suggested change
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);
}

Copilot uses AI. Check for mistakes.
Copy link
Member

@lgirdwood lgirdwood left a 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)
Copy link
Member

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 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants