Skip to content

Commit 85dae31

Browse files
committed
userspace: dp: Add option for executing userspace module IPC in DP thread
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>
1 parent d45fa59 commit 85dae31

7 files changed

Lines changed: 169 additions & 61 deletions

File tree

src/audio/module_adapter/library/userspace_proxy.c

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,33 @@ DECLARE_TR_CTX(userspace_proxy_tr, SOF_UUID(userspace_proxy_uuid), LOG_LEVEL_INF
4949

5050
static const struct module_interface userspace_proxy_interface;
5151

52+
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
53+
#include <sof/audio/module_adapter/iadk/system_agent.h>
54+
#include <sof/schedule/dp_schedule.h>
55+
56+
static inline int user_worker_get(void)
57+
{
58+
return 0;
59+
}
60+
static inline void user_worker_put(void) { }
61+
62+
struct k_work_user *userspace_proxy_register_ipc_handler(struct processing_module *mod,
63+
struct k_event *event)
64+
{
65+
struct userspace_context * const user_ctx = mod->user_ctx;
66+
if (user_ctx) {
67+
tr_dbg(&userspace_proxy_tr, "Set DP event %p for module %p",
68+
(void *)event, (void *)mod);
69+
user_ctx->dp_event = event;
70+
user_ctx->work_item->event = event;
71+
72+
assert(user_ctx->work_item);
73+
return &user_ctx->work_item->work_item;
74+
}
75+
76+
return NULL;
77+
}
78+
#else
5279
/* IPC requests targeting userspace modules are handled through a user work queue.
5380
* Each userspace module provides its own work item that carries the IPC request parameters.
5481
* The worker thread is switched into the module's memory domain and receives the work item.
@@ -106,6 +133,7 @@ static void user_worker_put(void)
106133
user_stack_free(worker.stack_ptr);
107134
}
108135
}
136+
#endif
109137

110138
static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap *user_heap)
111139
{
@@ -128,7 +156,9 @@ static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap
128156

129157
k_work_user_init(&work_item->work_item, userspace_proxy_worker_handler);
130158

159+
#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
131160
work_item->event = &worker.event;
161+
#endif
132162
work_item->params.context = user_ctx;
133163
user_ctx->work_item = work_item;
134164

@@ -155,14 +185,19 @@ BUILD_ASSERT(IS_ALIGNED(MAILBOX_HOSTBOX_SIZE, CONFIG_MMU_PAGE_SIZE),
155185
static int userspace_proxy_invoke(struct userspace_context *user_ctx, uint32_t cmd,
156186
bool ipc_payload_access)
157187
{
188+
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
189+
struct k_event * const event = user_ctx->dp_event;
190+
#else
191+
struct k_event * const event = &worker.event;
192+
#endif
158193
struct module_params *params = user_work_get_params(user_ctx);
159194
const uintptr_t ipc_req_buf = (uintptr_t)MAILBOX_HOSTBOX_BASE;
160195
struct k_mem_partition ipc_part = {
161196
.start = ipc_req_buf,
162197
.size = MAILBOX_HOSTBOX_SIZE,
163198
.attr = user_get_partition_attr(ipc_req_buf) | K_MEM_PARTITION_P_RO_U_RO,
164199
};
165-
int ret, ret2;
200+
int ret = 0, ret2;
166201

167202
params->cmd = cmd;
168203

@@ -174,6 +209,7 @@ static int userspace_proxy_invoke(struct userspace_context *user_ctx, uint32_t c
174209
}
175210
}
176211

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

197237
/* Timeout value is aligned with the ipc_wait_for_compound_msg function */
198-
if (!k_event_wait_safe(&worker.event, DP_TASK_EVENT_IPC_DONE, false,
238+
if (!k_event_wait_safe(event, DP_TASK_EVENT_IPC_DONE, false,
199239
Z_TIMEOUT_US(250 * 20))) {
200240
tr_err(&userspace_proxy_tr, "IPC processing timedout.");
201241
ret = -ETIMEDOUT;
@@ -313,18 +353,27 @@ static int userspace_proxy_start_agent(struct userspace_context *user_ctx,
313353
{
314354
const byte_array_t * const mod_cfg = (byte_array_t *)agent_params->mod_cfg;
315355
struct module_params *params = user_work_get_params(user_ctx);
316-
int ret;
317356

318357
params->ext.agent.start_fn = start_fn;
319-
params->ext.agent.params = *agent_params;
320-
params->ext.agent.mod_cfg = *mod_cfg;
321358

322-
ret = userspace_proxy_invoke(user_ctx, USER_PROXY_MOD_CMD_AGENT_START, true);
323-
if (ret)
324-
return ret;
359+
/* Start the system agent, if provided. */
360+
if (start_fn) {
361+
params->ext.agent.params = *agent_params;
362+
params->ext.agent.mod_cfg = *mod_cfg;
325363

326-
*agent_interface = params->ext.agent.out_interface;
327-
return params->status;
364+
/* In case of processing modules ipc in the DP thread, the agent will be started in the
365+
* init function. At this point the DP thread does not exist yet.
366+
*/
367+
#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
368+
int ret = userspace_proxy_invoke(user_ctx, USER_PROXY_MOD_CMD_AGENT_START, true);
369+
if (ret)
370+
return ret;
371+
372+
*agent_interface = params->ext.agent.out_interface;
373+
return params->status;
374+
#endif
375+
}
376+
return 0;
328377
}
329378

330379
int userspace_proxy_create(struct userspace_context **user_ctx, const struct comp_driver *drv,
@@ -362,14 +411,10 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com
362411
if (ret)
363412
goto error_dom;
364413

365-
/* Start the system agent, if provided. */
366-
367-
if (start_fn) {
368-
ret = userspace_proxy_start_agent(context, start_fn, agent_params, agent_interface);
369-
if (ret) {
370-
tr_err(&userspace_proxy_tr, "System agent failed with error %d.", ret);
371-
goto error_work_item;
372-
}
414+
ret = userspace_proxy_start_agent(context, start_fn, agent_params, agent_interface);
415+
if (ret) {
416+
tr_err(&userspace_proxy_tr, "System agent failed with error %d.", ret);
417+
goto error_work_item;
373418
}
374419

375420
*user_ctx = context;
@@ -420,6 +465,22 @@ static int userspace_proxy_init(struct processing_module *mod)
420465

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

468+
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
469+
/* Start the system agent, if provided. Params is already filled by
470+
* the userspace_proxy_start_agent function.
471+
*/
472+
if (params->ext.agent.start_fn) {
473+
ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_AGENT_START, true);
474+
if (ret)
475+
return ret;
476+
477+
if (params->ext.agent.start_fn == system_agent_start)
478+
module_set_private_data(mod, (void*)params->ext.agent.out_interface);
479+
else
480+
mod->user_ctx->interface = params->ext.agent.out_interface;
481+
}
482+
#endif
483+
423484
params->mod = mod;
424485
ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_INIT, true);
425486
if (ret)

src/audio/module_adapter/module/generic.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,8 @@ int module_reset(struct processing_module *mod)
622622

623623
/* cancel task if DP task*/
624624
if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP && mod->dev->task &&
625-
!IS_ENABLED(CONFIG_SOF_USERSPACE_APPLICATION))
625+
!IS_ENABLED(CONFIG_SOF_USERSPACE_APPLICATION) &&
626+
!IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD))
626627
schedule_task_cancel(mod->dev->task);
627628

628629
if (ops->reset) {

src/audio/module_adapter/module_adapter.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,23 +1432,24 @@ void module_adapter_free(struct comp_dev *dev)
14321432

14331433
comp_dbg(dev, "start");
14341434

1435-
if (dev->task) {
1435+
#if CONFIG_SOF_USERSPACE_APPLICATION
1436+
if (dev->task)
14361437
/*
14371438
* Run DP module's .free() method in its thread context.
14381439
* Unlike with other IPCs we first run module's .free() in
14391440
* thread context, then cancel the thread, and then execute
14401441
* final clean up
14411442
*/
1442-
#if CONFIG_SOF_USERSPACE_APPLICATION
14431443
scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_DELETE_INSTANCE, NULL);
14441444
#endif
1445-
schedule_task_free(dev->task);
1446-
}
14471445

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

1450+
if (dev->task)
1451+
schedule_task_free(dev->task);
1452+
14521453
list_for_item_safe(blist, _blist, &mod->raw_data_buffers_list) {
14531454
struct comp_buffer *buffer = container_of(blist, struct comp_buffer,
14541455
buffers_list);

src/include/sof/audio/module_adapter/library/userspace_proxy.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct userspace_context {
3131
struct k_mem_domain *comp_dom; /* Module specific memory domain */
3232
const struct module_interface *interface; /* Userspace module interface */
3333
struct user_work_item *work_item; /* work item for user worker thread */
34+
struct k_event *dp_event; /* DP thread event */
3435
};
3536
#endif /* CONFIG_USERSPACE */
3637

@@ -63,6 +64,20 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com
6364
*/
6465
void userspace_proxy_destroy(const struct comp_driver *drv, struct userspace_context *user_ctx);
6566

67+
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
68+
/**
69+
* Register a k_event object used to notify the DP thread about a pending IPC
70+
* request to process.
71+
*
72+
* @param mod Pointer to the processing module.
73+
* @param event Pointer to the event to signal incoming IPC.
74+
*
75+
* @return Pointer to a k_work_user work item on success, or NULL on failure.
76+
*/
77+
struct k_work_user *userspace_proxy_register_ipc_handler(struct processing_module *mod,
78+
struct k_event *event);
79+
#endif
80+
6681
#endif /* CONFIG_SOF_USERSPACE_PROXY */
6782

6883
#endif /* __SOF_AUDIO_USERSPACE_PROXY_H__ */

src/schedule/zephyr_dp_schedule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ enum sof_dp_part_type {
3232
};
3333

3434
struct ipc4_flat;
35+
3536
struct task_dp_pdata {
3637
k_tid_t thread_id; /* zephyr thread ID */
3738
struct k_thread *thread; /* pointer to the kernels' thread object */
@@ -46,6 +47,9 @@ struct task_dp_pdata {
4647
#endif
4748
struct k_event *event; /* pointer to event for task scheduling */
4849
struct k_event event_struct; /* event for task scheduling for kernel threads */
50+
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
51+
struct k_work_user *ipc_work_item; /* work item for IPC handling */
52+
#endif
4953
};
5054

5155
void scheduler_dp_recalculate(struct scheduler_dp_data *dp_sch, bool is_ll_post_run);

src/schedule/zephyr_dp_schedule_thread.c

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
* Copyright(c) 2025 Intel Corporation. All rights reserved.
44
*
55
* Author: Marcin Szkudlinski
6+
* Adrian Warecki
67
*/
78

89
#include <rtos/task.h>
910

1011
#include <sof/audio/module_adapter/module/generic.h>
1112
#include <sof/audio/module_adapter/library/userspace_proxy.h>
13+
#include <sof/audio/module_adapter/library/userspace_proxy_user.h>
1214
#include <sof/common.h>
1315
#include <sof/list.h>
1416
#include <sof/schedule/ll_schedule_domain.h>
@@ -115,6 +117,7 @@ void dp_thread_fn(void *p1, void *p2, void *p3)
115117
unsigned int lock_key;
116118
enum task_state state;
117119
bool task_stop;
120+
uint32_t event;
118121

119122
if (!(task->flags & K_USER))
120123
dp_sch = scheduler_get_data(SOF_SCHEDULE_DP);
@@ -124,50 +127,62 @@ void dp_thread_fn(void *p1, void *p2, void *p3)
124127
* the thread is started immediately after creation, it will stop on event.
125128
* Event will be signalled once the task is ready to process.
126129
*/
127-
k_event_wait_safe(task_pdata->event, DP_TASK_EVENT_PROCESS | DP_TASK_EVENT_CANCEL,
128-
false, K_FOREVER);
130+
event = k_event_wait_safe(task_pdata->event, DP_TASK_EVENT_PROCESS |
131+
DP_TASK_EVENT_CANCEL | DP_TASK_EVENT_IPC, false,
132+
K_FOREVER);
133+
134+
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
135+
if (event & DP_TASK_EVENT_IPC) {
136+
assert(task_pdata->ipc_work_item);
137+
userspace_proxy_worker_handler(task_pdata->ipc_work_item);
138+
}
139+
#endif
129140

130-
if (task->state == SOF_TASK_STATE_RUNNING)
131-
state = task_run(task);
132-
else
141+
if (event & DP_TASK_EVENT_PROCESS) {
133142
state = task->state; /* to avoid undefined variable warning */
143+
if (task->state == SOF_TASK_STATE_RUNNING && event & DP_TASK_EVENT_PROCESS)
144+
state = task_run(task);
134145

135-
lock_key = scheduler_dp_lock(task->core);
136-
/*
137-
* check if task is still running, may have been canceled by external call
138-
* if not, set the state returned by run procedure
139-
*/
140-
if (task->state == SOF_TASK_STATE_RUNNING) {
141-
task->state = state;
142-
switch (state) {
143-
case SOF_TASK_STATE_RESCHEDULE:
144-
/* mark to reschedule, schedule time is already calculated */
145-
task->state = SOF_TASK_STATE_QUEUED;
146-
break;
147-
148-
case SOF_TASK_STATE_CANCEL:
149-
case SOF_TASK_STATE_COMPLETED:
150-
/* remove from scheduling */
151-
list_item_del(&task->list);
152-
break;
153-
154-
default:
155-
/* illegal state, serious defect, won't happen */
156-
k_panic();
146+
lock_key = scheduler_dp_lock(task->core);
147+
/*
148+
* check if task is still running, may have been canceled by external call
149+
* if not, set the state returned by run procedure
150+
*/
151+
if (task->state == SOF_TASK_STATE_RUNNING) {
152+
task->state = state;
153+
switch (state) {
154+
case SOF_TASK_STATE_RESCHEDULE:
155+
/* mark to reschedule, schedule time is already calculated */
156+
task->state = SOF_TASK_STATE_QUEUED;
157+
break;
158+
159+
case SOF_TASK_STATE_CANCEL:
160+
case SOF_TASK_STATE_COMPLETED:
161+
/* remove from scheduling */
162+
list_item_del(&task->list);
163+
break;
164+
165+
default:
166+
/* illegal state, serious defect, won't happen */
167+
k_panic();
168+
}
157169
}
158-
}
159170

160-
/* if true exit the while loop, terminate the thread */
161-
task_stop = task->state == SOF_TASK_STATE_COMPLETED ||
162-
task->state == SOF_TASK_STATE_CANCEL;
163-
/* recalculate all DP tasks readiness and deadlines
164-
* TODO: it should be for all tasks, for all cores
165-
* currently its limited to current core only
166-
*/
167-
if (dp_sch)
168-
scheduler_dp_recalculate(dp_sch, false);
171+
/* if true exit the while loop, terminate the thread */
172+
task_stop = task->state == SOF_TASK_STATE_COMPLETED ||
173+
task->state == SOF_TASK_STATE_CANCEL;
174+
/* recalculate all DP tasks readiness and deadlines
175+
* TODO: it should be for all tasks, for all cores
176+
* currently its limited to current core only
177+
*/
178+
if (dp_sch)
179+
scheduler_dp_recalculate(dp_sch, false);
180+
181+
scheduler_dp_unlock(lock_key);
182+
}
169183

170-
scheduler_dp_unlock(lock_key);
184+
if (event & DP_TASK_EVENT_CANCEL)
185+
task_stop = true;
171186
} while (!task_stop);
172187

173188
/* call task_complete */
@@ -294,6 +309,10 @@ int scheduler_dp_task_init(struct task **task,
294309
k_event_init(pdata->event);
295310
k_thread_start(pdata->thread_id);
296311

312+
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
313+
pdata->ipc_work_item = userspace_proxy_register_ipc_handler(mod, pdata->event);
314+
#endif
315+
297316
/* success, fill output parameter */
298317
*task = &task_memory->task;
299318
return 0;

0 commit comments

Comments
 (0)