Skip to content

LL userspace: enable cross-core DP pipelines - #11397

Open
kv2019i wants to merge 7 commits into
thesofproject:mainfrom
kv2019i:202611-lluser-cross-core-ipc-idc
Open

kv2019i wants to merge 7 commits into
thesofproject:mainfrom
kv2019i:202611-lluser-cross-core-ipc-idc

Conversation

@kv2019i

@kv2019i kv2019i commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Initial implementation of cross-core pipeline support to full userspace builds. This covers the common use-case of running compressed codec module code in a different core (as DP module).

Pass the message payload to the component IDC message handlers as an
argument, instead of fetching it from the per-core IDC payload area,
which is kernel memory.

Add idc_cmd_user() to let user-space threads (with CONFIG_SOF_USERSPACE_LL)
process component messages, sent from another core, with a payload
in user-accessible memory. Only the messages, which can originate from
the user-space IPC and LL threads, are supported.

LL modules on a core different from their pipeline core aren't
supported with user-space LL, so fail the prepare instead of trying
to set up an LL task for them.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
With CONFIG_SOF_USERSPACE_LL, component operations on a module on
another core (cross-core bind, remote get_attribute, free, params,
prepare, trigger, reset) are called from user-space IPC and LL threads.
They use idc_send_msg(), which relies on kernel objects and data and
faults in user mode.

Add a cross-core request slot per core to struct ipc_user. When called
in user context, idc_send_msg() now copies the message and its payload
to the slot of the target core and wakes the user-space IPC thread of
that core, which processes it with idc_cmd_user() and signals
completion. The IPC4 bind / unbind message, referenced by the bind
payload, usually is on the sender's stack, so it's copied as well.

Grant all user-space IPC and LL threads access to the request slots
and LL scheduler locks of all initialized cores. The grants are done
from kernel-only copies of the thread pointers, so keep one for the LL
audio threads as well.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
comp_ipc4_get_attribute_remote() allocates the buffer for the remote
core to fill with rzalloc(), which takes a spinlock and fails in user
mode. With CONFIG_SOF_USERSPACE_LL it's called from user-space threads
and the buffer is filled by the user-space IPC thread of the remote
core, so allocate it from the coherent LL user heap instead.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
With CONFIG_SOF_USERSPACE_LL, ipc_pipeline_module_free() takes the LL
scheduler lock of the core of the first module in the pipeline. That
is supposed to be the pipeline core, but a module can be on another
core (e.g. an offloaded DP module). Freeing such a remote module is
done on its core, where ipc_comp_free() takes the LL lock of that core,
which would deadlock, if it's the lock taken here.

Pass the pipeline core to ipc_pipeline_module_free() and lock that.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Cross-core binding was rejected at build time with
CONFIG_SOF_USERSPACE_LL. Implement the LL blocking for it:

The user-space LL thread holds its scheduler lock for the whole LL
cycle, so taking the LL locks of both cores both blocks their next
cycles and waits for the current ones to finish. ll_block() now takes
both locks, in a fixed order to avoid deadlocks, and
ll_wait_finished_on_core() doesn't need to send an IDC anymore.

The remote parts of the bind (get_attribute, bind, unbind) go through
idc_send_msg(), which forwards them to the user-space IPC thread of
the remote core.

The domain block / unblock in zephyr_domain.c uses a mutex and a
condition variable, embedded in user memory, and isn't used with
user-space LL, so don't build it then.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
With CONFIG_CROSS_CORE_STREAM and CONFIG_SOF_USERSPACE_LL, a DP module
on a different core than its pipeline can now be bound and operated
across cores. With user-space LL, keep rejecting only LL modules on
another core than their pipeline. Such an LL module would need its own
LL task on that core, which is only set up by the kernel-mode IDC
handlers.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Cross-core streams are now supported with user-space LL. Stop
disabling CONFIG_CROSS_CORE_STREAM in the LL user-space overlay.

This allows e.g. the compressed playback topology, which runs the
decoder (DP) on core 1, bound to the host copier on core 0.

The overlay is used by all ACE3.x and newer targets (e.g. PTL, WCL,
NVL) built with os_linux_overlay.conf. Tested on PTL.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
@kv2019i

kv2019i commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator Author

FYI @ujfalusi @lgirdwood with this, compressed playback can be supported also with full userspace (both LL pipes and the DP codecs running in user-space).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical multicore userspace defects in state handling, locking, access grants, event signaling, and component dispatch can cause failed transitions, panics, unauthorized access, and deadlocks.

5 open issues
What changed in this PR

Adds initial cross-core pipeline support to full userspace builds, allowing DP modules such as compressed codecs to run on a different core from their pipeline.

Changes:

  • Routes component IDC requests through per-core userspace IPC threads.
  • Adds shared request storage, access grants, and userspace scheduler locking.
  • Enables cross-core streams while keeping LL modules on their pipeline’s core.
File Description
zephyr/​include/​rtos/​idc.h Declares userspace cross-core messaging interfaces.
src/​schedule/​zephyr_domain.c Excludes kernel-style scheduler blocking from userspace LL builds.
src/​ipc/​ipc4/​helper.c Allows remote DP modules and adjusts binding and teardown locks.
src/​ipc/​ipc-common.c Implements cross-core request transport, dispatch, and access grants.
src/​include/​sof/​ipc/​common.h Defines per-core cross-core request slots.
src/​include/​sof/​audio/​component_ext.h Allocates remote attribute results in user-accessible memory.
src/​idc/​zephyr_idc.c Routes userspace IDC sends through userspace IPC threads.
src/​idc/​idc.c Adds userspace component dispatch and explicit payload handling.
app/​ll_userspace_overlay.conf Removes the forced disabling of cross-core streams.

Review effort: Balanced

Comment thread src/idc/idc.c
Comment on lines +443 to +445
default:
tr_err(&idc_tr, "unsupported user msg->header = %#x", msg->header);
return -EINVAL;
Comment thread src/ipc/ipc-common.c
}
#endif

k_event_set(ipc_user->event[msg->core], IPC_USER_EVENT_XCORE);
Comment thread src/ipc/ipc-common.c
if (!new_threads[i])
continue;
k_thread_access_grant(new_threads[i], ipc_user_kevent[c]);
user_ll_grant_access(new_threads[i], c);
Comment thread src/ipc/ipc4/helper.c
Comment on lines +175 to +178
if (ppl->core != ipc_config->core &&
(!IS_ENABLED(CONFIG_CROSS_CORE_STREAM) ||
(IS_ENABLED(CONFIG_SOF_USERSPACE_LL) &&
ipc_config->proc_domain != COMP_PROCESSING_DOMAIN_DP))) {
Comment thread src/ipc/ipc4/helper.c
Comment on lines +752 to +754
user_ll_lock_sched(MIN(src_core, dst_core)); \
if (src_core != dst_core) \
user_ll_lock_sched(MAX(src_core, dst_core)); \
@intel-sofci

Copy link
Copy Markdown

PR 11397: test results

Run date: 2026-11-03 18:35 UTC

Tested commit: 72b5b6736f316be6a2d6643bd8417dc4054c15a0

mtl pass rate lnl pass rate ptl pass rate wcl pass rate nvl pass rate

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.

3 participants