Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/overlays/ptl/userspace_overlay.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ CONFIG_DYNAMIC_THREAD_ALLOC=n
# IADK module adapter store some buffers on stack, so we need to increase stack size
CONFIG_DYNAMIC_THREAD_STACK_SIZE=8192
CONFIG_SOF_STACK_SIZE=8192
CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD=y
104 changes: 86 additions & 18 deletions src/audio/module_adapter/library/userspace_proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,36 @@ DECLARE_TR_CTX(userspace_proxy_tr, SOF_UUID(userspace_proxy_uuid), LOG_LEVEL_INF

static const struct module_interface userspace_proxy_interface;

#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
#include <sof/audio/module_adapter/iadk/system_agent.h>
#include <sof/schedule/dp_schedule.h>

static inline int user_worker_get(void)
{
return 0;
}

static inline void user_worker_put(void) { }

struct k_work_user *userspace_proxy_register_ipc_handler(struct processing_module *mod,
struct k_event *event)
{
struct userspace_context * const user_ctx = mod->user_ctx;

if (user_ctx) {
tr_dbg(&userspace_proxy_tr, "Set DP event %p for module %p",
(void *)event, (void *)mod);
assert(user_ctx->work_item);

user_ctx->dp_event = event;
user_ctx->work_item->event = event;

return &user_ctx->work_item->work_item;
}

return NULL;
}
#else
/* IPC requests targeting userspace modules are handled through a user work queue.
* Each userspace module provides its own work item that carries the IPC request parameters.
* The worker thread is switched into the module's memory domain and receives the work item.
Expand Down Expand Up @@ -106,6 +136,7 @@ static void user_worker_put(void)
user_stack_free(worker.stack_ptr);
}
}
#endif

static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap *user_heap)
{
Expand All @@ -128,7 +159,9 @@ static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap

k_work_user_init(&work_item->work_item, userspace_proxy_worker_handler);

#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
work_item->event = &worker.event;
#endif
work_item->params.context = user_ctx;
user_ctx->work_item = work_item;

Expand All @@ -155,14 +188,19 @@ BUILD_ASSERT(IS_ALIGNED(MAILBOX_HOSTBOX_SIZE, CONFIG_MMU_PAGE_SIZE),
static int userspace_proxy_invoke(struct userspace_context *user_ctx, uint32_t cmd,
bool ipc_payload_access)
{
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
struct k_event * const event = user_ctx->dp_event;
#else
struct k_event * const event = &worker.event;
#endif
struct module_params *params = user_work_get_params(user_ctx);
const uintptr_t ipc_req_buf = (uintptr_t)MAILBOX_HOSTBOX_BASE;
struct k_mem_partition ipc_part = {
.start = ipc_req_buf,
.size = MAILBOX_HOSTBOX_SIZE,
.attr = user_get_partition_attr(ipc_req_buf) | K_MEM_PARTITION_P_RO_U_RO,
};
int ret, ret2;
int ret = 0, ret2;

params->cmd = cmd;

Expand All @@ -174,6 +212,7 @@ static int userspace_proxy_invoke(struct userspace_context *user_ctx, uint32_t c
}
}

#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
/* Switch worker thread to module memory domain */
ret = k_mem_domain_add_thread(user_ctx->comp_dom, worker.thread_id);
if (ret < 0) {
Expand All @@ -193,9 +232,13 @@ static int userspace_proxy_invoke(struct userspace_context *user_ctx, uint32_t c
tr_err(&userspace_proxy_tr, "Submit to queue error: %d", ret);
goto done;
}
#else
assert(event);
k_event_post(event, DP_TASK_EVENT_IPC);
#endif

/* Timeout value is aligned with the ipc_wait_for_compound_msg function */
if (!k_event_wait_safe(&worker.event, DP_TASK_EVENT_IPC_DONE, false,
if (!k_event_wait_safe(event, DP_TASK_EVENT_IPC_DONE, false,
Z_TIMEOUT_US(250 * 20))) {
tr_err(&userspace_proxy_tr, "IPC processing timedout.");
ret = -ETIMEDOUT;
Expand Down Expand Up @@ -313,18 +356,29 @@ static int userspace_proxy_start_agent(struct userspace_context *user_ctx,
{
const byte_array_t * const mod_cfg = (byte_array_t *)agent_params->mod_cfg;
struct module_params *params = user_work_get_params(user_ctx);
int ret;

params->ext.agent.start_fn = start_fn;
params->ext.agent.params = *agent_params;
params->ext.agent.mod_cfg = *mod_cfg;

ret = userspace_proxy_invoke(user_ctx, USER_PROXY_MOD_CMD_AGENT_START, true);
if (ret)
return ret;
/* Start the system agent, if provided. */
if (start_fn) {
params->ext.agent.params = *agent_params;
params->ext.agent.params.mod_cfg = &params->ext.agent.mod_cfg;
params->ext.agent.mod_cfg = *mod_cfg;
Copy link
Contributor

Choose a reason for hiding this comment

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

How about adding?
params->ext.agent.params.mod_cfg = &params->ext.agent.mod_cfg;
OR:
params->ext.agent.params.mod_cfg =NULL;

Copy link
Collaborator Author

@softwarecki softwarecki Feb 27, 2026

Choose a reason for hiding this comment

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

mod_cfg points to a structure allocated on the kernel thread stack, so we must copy the value of the structure here instead of storing a pointer to it.

The line you suggest to add is already present in userspace_proxy_handle_request(). However, you are right that this location is better, so I moved it there.


*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;
}

int userspace_proxy_create(struct userspace_context **user_ctx, const struct comp_driver *drv,
Expand All @@ -342,6 +396,8 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com
if (!context)
return -ENOMEM;

context->dp_event = NULL;

/* Allocate memory domain struct */
domain = rzalloc(SOF_MEM_FLAG_KERNEL, sizeof(*domain));
if (!domain) {
Expand All @@ -362,14 +418,10 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com
if (ret)
goto error_dom;

/* Start the system agent, if provided. */

if (start_fn) {
ret = userspace_proxy_start_agent(context, start_fn, agent_params, agent_interface);
if (ret) {
tr_err(&userspace_proxy_tr, "System agent failed with error %d.", ret);
goto error_work_item;
}
ret = userspace_proxy_start_agent(context, start_fn, agent_params, agent_interface);
if (ret) {
tr_err(&userspace_proxy_tr, "System agent failed with error %d.", ret);
goto error_work_item;
}

*user_ctx = context;
Expand Down Expand Up @@ -420,6 +472,22 @@ static int userspace_proxy_init(struct processing_module *mod)

comp_dbg(mod->dev, "start");

#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
/* Start the system agent, if provided. Params is already filled by
* the userspace_proxy_start_agent function.
*/
if (params->ext.agent.start_fn) {
ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_AGENT_START, true);
if (ret)
return ret;

if (params->ext.agent.start_fn == system_agent_start)
module_set_private_data(mod, (void *)params->ext.agent.out_interface);
else
mod->user_ctx->interface = params->ext.agent.out_interface;
}
#endif

params->mod = mod;
ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_INIT, true);
if (ret)
Expand Down
3 changes: 0 additions & 3 deletions src/audio/module_adapter/library/userspace_proxy_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ void userspace_proxy_handle_request(struct processing_module *mod, struct module

switch (params->cmd) {
case USER_PROXY_MOD_CMD_AGENT_START:
/* Set pointer to user accessible mod_cfg structure. */
params->ext.agent.params.mod_cfg = &params->ext.agent.mod_cfg;

params->status = params->ext.agent.start_fn(&params->ext.agent.params,
&params->ext.agent.out_interface);
break;
Expand Down
3 changes: 2 additions & 1 deletion src/audio/module_adapter/module/generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,8 @@ int module_reset(struct processing_module *mod)

/* cancel task if DP task*/
if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP && mod->dev->task &&
!IS_ENABLED(CONFIG_SOF_USERSPACE_APPLICATION))
!IS_ENABLED(CONFIG_SOF_USERSPACE_APPLICATION) &&
!IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD))
schedule_task_cancel(mod->dev->task);

if (ops->reset) {
Expand Down
9 changes: 5 additions & 4 deletions src/audio/module_adapter/module_adapter.c
Original file line number Diff line number Diff line change
Expand Up @@ -1432,23 +1432,24 @@ void module_adapter_free(struct comp_dev *dev)

comp_dbg(dev, "start");

if (dev->task) {
#if CONFIG_SOF_USERSPACE_APPLICATION
if (dev->task)
/*
* Run DP module's .free() method in its thread context.
* Unlike with other IPCs we first run module's .free() in
* thread context, then cancel the thread, and then execute
* final clean up
*/
#if CONFIG_SOF_USERSPACE_APPLICATION
scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_DELETE_INSTANCE, NULL);
#endif
schedule_task_free(dev->task);
}

ret = module_free(mod);
if (ret)
comp_err(dev, "failed with error: %d", ret);

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

list_for_item_safe(blist, _blist, &mod->raw_data_buffers_list) {
struct comp_buffer *buffer = container_of(blist, struct comp_buffer,
buffers_list);
Expand Down
16 changes: 16 additions & 0 deletions src/include/sof/audio/module_adapter/library/userspace_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct userspace_context {
struct k_mem_domain *comp_dom; /* Module specific memory domain */
const struct module_interface *interface; /* Userspace module interface */
struct user_work_item *work_item; /* work item for user worker thread */
struct k_event *dp_event; /* DP thread event */
};
#endif /* CONFIG_USERSPACE */

Expand Down Expand Up @@ -63,6 +64,21 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com
*/
void userspace_proxy_destroy(const struct comp_driver *drv, struct userspace_context *user_ctx);

#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
/**
* Register a k_event object used to notify the DP thread about a pending userspace module IPC
* request to process.
*
* @param mod Pointer to the processing module.
* @param event Pointer to the event to signal incoming IPC.
*
* @return Pointer to a k_work_user work item for userspace modules, or NULL for non-userspace
* modules.
*/
struct k_work_user *userspace_proxy_register_ipc_handler(struct processing_module *mod,
struct k_event *event);
#endif

#endif /* CONFIG_SOF_USERSPACE_PROXY */

#endif /* __SOF_AUDIO_USERSPACE_PROXY_H__ */
4 changes: 4 additions & 0 deletions src/schedule/zephyr_dp_schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum sof_dp_part_type {
};

struct ipc4_flat;

struct task_dp_pdata {
k_tid_t thread_id; /* zephyr thread ID */
struct k_thread *thread; /* pointer to the kernels' thread object */
Expand All @@ -46,6 +47,9 @@ struct task_dp_pdata {
#endif
struct k_event *event; /* pointer to event for task scheduling */
struct k_event event_struct; /* event for task scheduling for kernel threads */
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
struct k_work_user *ipc_work_item; /* work item for IPC handling */
#endif
};

void scheduler_dp_recalculate(struct scheduler_dp_data *dp_sch, bool is_ll_post_run);
Expand Down
Loading
Loading