Skip to content

Commit 0cddfe3

Browse files
userspace: proxy: use a separate worker per core
When CONFIG_SCHED_CPU_MASK_PIN_ONLY=y, re-pinning a thread to a new core works only if the thread has never been active on another core. To avoid re-pinning, rework the userspace proxy to use a separate worker per core. Each worker is pinned to its core once before it starts. Signed-off-by: Serhiy Katsyuba <serhiy.katsyuba@intel.com>
1 parent 8ea3893 commit 0cddfe3

1 file changed

Lines changed: 64 additions & 43 deletions

File tree

src/audio/module_adapter/library/userspace_proxy.c

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <sof/audio/component.h>
3131
#include <sof/schedule/dp_schedule.h>
3232
#include <rtos/userspace_helper.h>
33+
#include <rtos/user_work.h>
3334
#include <utilities/array.h>
3435
#include <zephyr/sys/sem.h>
3536
#include <sof/audio/module_adapter/module/generic.h>
@@ -54,12 +55,16 @@ static const struct module_interface userspace_proxy_interface;
5455
#include <sof/audio/module_adapter/iadk/system_agent.h>
5556
#include <sof/schedule/dp_schedule.h>
5657

57-
static inline int user_worker_get(void)
58+
static inline int user_worker_get(int core)
5859
{
60+
ARG_UNUSED(core);
5961
return 0;
6062
}
6163

62-
static inline void user_worker_put(void) { }
64+
static inline void user_worker_put(int core)
65+
{
66+
ARG_UNUSED(core);
67+
}
6368

6469
struct k_work_user *userspace_proxy_register_ipc_handler(struct processing_module *mod,
6570
struct k_event *event)
@@ -86,9 +91,9 @@ struct k_work_user *userspace_proxy_register_ipc_handler(struct processing_modul
8691
* It invokes the appropriate module function in userspace context and writes the operation
8792
* result back into the work item.
8893
*
89-
* There is only a single work queue, which is shared by all userspace modules. It is created
90-
* dynamically when needed. Because SOF uses a single dedicated thread for handling IPC, there
91-
* is no need to perform any additional serialization when accessing the worker.
94+
* Each core owns one work queue, shared by the userspace modules assigned to that core.
95+
* Each work queue has its own worker thread, which is pinned to its core before it is first
96+
* started and never migrates.
9297
*/
9398
struct user_worker {
9499
k_tid_t thread_id; /* ipc worker thread ID */
@@ -98,70 +103,94 @@ struct user_worker {
98103
struct k_event event;
99104
};
100105

101-
static struct user_worker worker;
106+
static struct user_worker workers[CONFIG_CORE_COUNT];
102107

103-
static int user_worker_get(void)
108+
static int user_worker_get(int core)
104109
{
105-
if (worker.reference_count) {
106-
worker.reference_count++;
110+
struct user_worker *worker;
111+
int ret;
112+
113+
if (core < 0 || core >= CONFIG_CORE_COUNT)
114+
return -EINVAL;
115+
116+
worker = &workers[core];
117+
if (worker->reference_count) {
118+
worker->reference_count++;
107119
return 0;
108120
}
109121

110-
worker.stack_ptr = user_stack_allocate(CONFIG_SOF_USERSPACE_PROXY_WORKER_STACK_SIZE,
111-
K_USER);
112-
if (!worker.stack_ptr) {
122+
worker->stack_ptr = user_stack_allocate(CONFIG_SOF_USERSPACE_PROXY_WORKER_STACK_SIZE,
123+
K_USER);
124+
if (!worker->stack_ptr) {
113125
tr_err(&userspace_proxy_tr, "Userspace worker stack allocation failed.");
114126
return -ENOMEM;
115127
}
116128

117-
k_event_init(&worker.event);
118-
k_work_user_queue_start(&worker.work_queue, worker.stack_ptr,
119-
CONFIG_SOF_USERSPACE_PROXY_WORKER_STACK_SIZE, 0, NULL);
129+
k_event_init(&worker->event);
130+
sof_work_user_queue_create(&worker->work_queue, worker->stack_ptr,
131+
CONFIG_SOF_USERSPACE_PROXY_WORKER_STACK_SIZE, 0, NULL);
132+
worker->thread_id = k_work_user_queue_thread_get(&worker->work_queue);
133+
k_thread_access_grant(worker->thread_id, &worker->event);
120134

121-
worker.thread_id = k_work_user_queue_thread_get(&worker.work_queue);
135+
#ifdef CONFIG_SCHED_CPU_MASK
136+
ret = k_thread_cpu_pin(worker->thread_id, core);
137+
if (ret < 0) {
138+
tr_err(&userspace_proxy_tr, "Failed to pin userspace worker to core %d: %d",
139+
core, ret);
140+
k_thread_abort(worker->thread_id);
141+
user_stack_free(worker->stack_ptr);
142+
worker->stack_ptr = NULL;
143+
worker->thread_id = NULL;
144+
return ret;
145+
}
146+
#elif CONFIG_CORE_COUNT > 1
147+
#error "CONFIG_SCHED_CPU_MASK is not enabled"
148+
#endif
122149

123-
k_thread_access_grant(worker.thread_id, &worker.event);
150+
k_thread_start(worker->thread_id);
124151

125-
worker.reference_count++;
152+
worker->reference_count++;
126153
return 0;
127154
}
128155

129-
static void user_worker_put(void)
156+
static void user_worker_put(int core)
130157
{
158+
struct user_worker *worker = &workers[core];
159+
131160
/* Module removed so decrement counter */
132-
worker.reference_count--;
161+
worker->reference_count--;
133162

134163
/* Free worker resources if no more active user space modules */
135-
if (worker.reference_count == 0) {
136-
k_thread_abort(worker.thread_id);
137-
user_stack_free(worker.stack_ptr);
164+
if (worker->reference_count == 0) {
165+
k_thread_abort(worker->thread_id);
166+
user_stack_free(worker->stack_ptr);
167+
worker->stack_ptr = NULL;
168+
worker->thread_id = NULL;
138169
}
139170
}
140171
#endif
141172

142173
static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap *user_heap)
143174
{
144175
struct user_work_item *work_item = NULL;
176+
const int core = cpu_get_id();
145177
int ret;
146178

147-
ret = user_worker_get();
179+
ret = user_worker_get(core);
148180
if (ret)
149181
return ret;
150182

151-
/* We have only a single userspace IPC worker. It handles requests for all userspace
152-
* modules, which may run on different cores. Because the worker processes work items
153-
* coming from any core, the work item must be allocated in coherent memory.
154-
*/
183+
/* TODO: this can probably be cached now? */
155184
work_item = sof_heap_alloc(user_heap, SOF_MEM_FLAG_COHERENT, sizeof(*work_item), 0);
156185
if (!work_item) {
157-
user_worker_put();
186+
user_worker_put(core);
158187
return -ENOMEM;
159188
}
160189

161190
k_work_user_init(&work_item->work_item, userspace_proxy_worker_handler);
162191

163192
#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
164-
work_item->event = &worker.event;
193+
work_item->event = &workers[core].event;
165194
#endif
166195
work_item->params.context = user_ctx;
167196
work_item->params.mod = NULL;
@@ -173,7 +202,7 @@ static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap
173202
static void user_work_item_free(struct userspace_context *user_ctx, struct k_heap *user_heap)
174203
{
175204
sof_heap_free(user_heap, user_ctx->work_item);
176-
user_worker_put();
205+
user_worker_put(cpu_get_id());
177206
}
178207

179208
static inline struct module_params *user_work_get_params(struct userspace_context *user_ctx)
@@ -193,7 +222,8 @@ static int userspace_proxy_invoke(struct userspace_context *user_ctx, uint32_t c
193222
#if IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
194223
struct k_event * const event = user_ctx->dp_event;
195224
#else
196-
struct k_event * const event = &worker.event;
225+
struct user_worker *worker = &workers[cpu_get_id()];
226+
struct k_event * const event = &worker->event;
197227
#endif
198228
struct module_params *params = user_work_get_params(user_ctx);
199229
const uintptr_t ipc_req_buf = (uintptr_t)MAILBOX_HOSTBOX_BASE;
@@ -216,22 +246,13 @@ static int userspace_proxy_invoke(struct userspace_context *user_ctx, uint32_t c
216246

217247
#if !IS_ENABLED(CONFIG_SOF_USERSPACE_MOD_IPC_BY_DP_THREAD)
218248
/* Switch worker thread to module memory domain */
219-
ret = k_mem_domain_add_thread(user_ctx->comp_dom, worker.thread_id);
249+
ret = k_mem_domain_add_thread(user_ctx->comp_dom, worker->thread_id);
220250
if (ret < 0) {
221251
tr_err(&userspace_proxy_tr, "Failed to switch memory domain, error: %d", ret);
222252
goto done;
223253
}
224254

225-
#ifdef CONFIG_SCHED_CPU_MASK
226-
/* Pin worker thread to the same core as the module */
227-
ret = k_thread_cpu_pin(worker.thread_id, cpu_get_id());
228-
if (ret < 0) {
229-
tr_err(&userspace_proxy_tr, "Failed to pin cpu, error: %d", ret);
230-
goto done;
231-
}
232-
#endif
233-
234-
ret = k_work_user_submit_to_queue(&worker.work_queue, &user_ctx->work_item->work_item);
255+
ret = k_work_user_submit_to_queue(&worker->work_queue, &user_ctx->work_item->work_item);
235256
if (ret < 0) {
236257
tr_err(&userspace_proxy_tr, "Submit to queue error: %d", ret);
237258
goto done;

0 commit comments

Comments
 (0)