Skip to content

userspace: dp: Add option for executing userspace module IPC in DP thread#10579

Open
softwarecki wants to merge 1 commit intothesofproject:mainfrom
softwarecki:userspace-ipc-in-dp
Open

userspace: dp: Add option for executing userspace module IPC in DP thread#10579
softwarecki wants to merge 1 commit intothesofproject:mainfrom
softwarecki:userspace-ipc-in-dp

Conversation

@softwarecki
Copy link
Collaborator

Add the SOF_USERSPACE_MOD_IPC_BY_DP_THREAD Kconfig option and implement support for executing userspace module IPC in its DP thread instead of the shared user worker thread.

…read

Add the SOF_USERSPACE_MOD_IPC_BY_DP_THREAD Kconfig option and implement
support for executing userspace module IPC in its DP thread instead of
the shared user worker thread.

Signed-off-by: Adrian Warecki <adrian.warecki@intel.com>
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 a Zephyr Kconfig option to route userspace module IPC handling through each module’s DP thread (instead of the shared userspace worker), and wires DP-thread event/work-item plumbing through the DP scheduler and userspace proxy layers.

Changes:

  • Add SOF_USERSPACE_MOD_IPC_BY_DP_THREAD Kconfig option.
  • Extend DP scheduler thread/event handling to wake on IPC events and run the userspace proxy IPC handler in the DP thread.
  • Update userspace proxy to optionally signal/await IPC via the DP thread’s k_event, plus adjust system-agent startup timing for this mode.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
zephyr/Kconfig Adds the new configuration option controlling IPC execution context.
src/schedule/zephyr_dp_schedule_thread.c Handles IPC events in DP thread and registers per-module IPC handler/event linkage.
src/schedule/zephyr_dp_schedule.h Adds per-task storage for the IPC work item pointer.
src/include/sof/audio/module_adapter/library/userspace_proxy.h Extends userspace context with DP-thread event pointer and declares IPC handler registration API.
src/audio/module_adapter/module_adapter.c Adjusts DP task/free sequencing around userspace application teardown.
src/audio/module_adapter/module/generic.c Tweaks DP task cancellation behavior under the new IPC-by-DP-thread mode.
src/audio/module_adapter/library/userspace_proxy.c Implements DP-thread IPC signaling path, IPC handler registration, and defers agent startup in this mode.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +69 to +72
user_ctx->dp_event = event;
user_ctx->work_item->event = event;

assert(user_ctx->work_item);
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

userspace_proxy_register_ipc_handler() dereferences user_ctx->work_item before validating it (user_ctx->work_item->event = event; happens before assert(user_ctx->work_item)). This can NULL-deref if work item allocation/init failed. Assert/check user_ctx->work_item before using it and return NULL/error if missing.

Suggested change
user_ctx->dp_event = event;
user_ctx->work_item->event = event;
assert(user_ctx->work_item);
if (!user_ctx->work_item) {
tr_err(&userspace_proxy_tr,
"userspace_proxy_register_ipc_handler(): work_item is NULL for module %p",
(void *)mod);
return NULL;
}
user_ctx->dp_event = event;
user_ctx->work_item->event = event;

Copilot uses AI. Check for mistakes.
Comment on lines +359 to +376
/* Start the system agent, if provided. */
if (start_fn) {
params->ext.agent.params = *agent_params;
params->ext.agent.mod_cfg = *mod_cfg;

*agent_interface = params->ext.agent.out_interface;
return params->status;
/* In case of processing modules ipc in the DP thread, the agent will be started in the
* init function. At this point the DP thread does not exist yet.
*/
#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
int ret = userspace_proxy_invoke(user_ctx, USER_PROXY_MOD_CMD_AGENT_START, true);
if (ret)
return ret;

*agent_interface = params->ext.agent.out_interface;
return params->status;
#endif
}
return 0;
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

With CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD enabled, userspace_proxy_start_agent() no longer invokes start_fn and does not populate *agent_interface. This breaks the LMDK path where native_system_agent_start() is what initializes ops (via agent_iface = (const void **)&ops), and ops is used immediately after userspace_proxy_create() returns. Either keep starting the agent here for cases that must produce ops/agent_interface, or provide an alternative initialization path that guarantees ops is valid before returning from userspace_proxy_create().

Copilot uses AI. Check for mistakes.
bool ipc_payload_access)
{
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
struct k_event * const event = user_ctx->dp_event;
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

In the DP-thread IPC mode, userspace_proxy_invoke() uses user_ctx->dp_event. dp_event is set later via userspace_proxy_register_ipc_handler(), but the userspace_context is allocated with k_heap_alloc() (not zeroed), so dp_event can contain garbage until explicitly set. Initialize dp_event to NULL when creating the context and fail fast (return an error) if dp_event is still NULL when invoking IPC.

Suggested change
struct k_event * const event = user_ctx->dp_event;
struct k_event * const event = user_ctx->dp_event;
if (!event) {
tr_err(&userspace_proxy_tr,
"userspace_proxy_invoke(): DP event not registered");
return -EINVAL;
}

Copilot uses AI. Check for mistakes.
Comment on lines 1446 to +1452
ret = module_free(mod);
if (ret)
comp_err(dev, "failed with error: %d", ret);

if (dev->task)
schedule_task_free(dev->task);

Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

The ordering here contradicts the comment and can keep the DP thread alive while module_free(mod) runs. schedule_task_free() joins/aborts the DP thread; moving it after module_free() risks use-after-free if the thread is still runnable or if any late events occur. Restore the safe order: run the thread-context .free IPC, then schedule_task_free(dev->task), then module_free(mod) / remaining cleanup.

Suggested change
ret = module_free(mod);
if (ret)
comp_err(dev, "failed with error: %d", ret);
if (dev->task)
schedule_task_free(dev->task);
if (dev->task)
schedule_task_free(dev->task);
ret = module_free(mod);
if (ret)
comp_err(dev, "failed with error: %d", ret);

Copilot uses AI. Check for mistakes.
Comment on lines +126 to +132
config SOF_USERSPACE_MOD_IPC_BY_DP_THREAD
bool "Handle modules IPC in DP thread"
depends on USERSPACE
help
When enabled, IPC requests targeted to a userspace module are executed
in its DP thread instead of the shared user worker thread.

Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

SOF_USERSPACE_MOD_IPC_BY_DP_THREAD can be enabled when SOF_USERSPACE_PROXY is disabled, but the implementation relies on userspace proxy headers/symbols (e.g., userspace_proxy_register_ipc_handler() / userspace_proxy_worker_handler()). Add a dependency on SOF_USERSPACE_PROXY (and any other required options) to avoid invalid configurations / build failures.

Copilot uses AI. Check for mistakes.
Comment on lines 119 to 121
bool task_stop;
uint32_t event;

Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

task_stop is not initialized on each loop iteration. If the DP thread wakes up only for DP_TASK_EVENT_IPC (no PROCESS/CANCEL), task_stop keeps an indeterminate/stale value but is still used in while (!task_stop), which can lead to premature exit or an infinite loop. Initialize task_stop (e.g., to false) before the loop or at the top of each iteration, and make sure it’s set on all event paths.

Copilot uses AI. Check for mistakes.
k_thread_start(pdata->thread_id);

#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
pdata->ipc_work_item = userspace_proxy_register_ipc_handler(mod, pdata->event);
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

The return value of userspace_proxy_register_ipc_handler() is not checked. If it returns NULL (e.g., missing/failed work item setup), the thread will later hit assert(task_pdata->ipc_work_item) on an IPC event and crash. Handle the NULL return here (propagate an error, disable IPC-by-DP-thread for this task, or avoid asserting later).

Suggested change
pdata->ipc_work_item = userspace_proxy_register_ipc_handler(mod, pdata->event);
pdata->ipc_work_item = userspace_proxy_register_ipc_handler(mod, pdata->event);
if (!pdata->ipc_work_item) {
tr_err(&dp_tr, "userspace_proxy_register_ipc_handler() failed");
ret = -ENOMEM;
goto e_thread;
}

Copilot uses AI. Check for mistakes.
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