From d8a7926cd478388b3a07568a189d01e342193a7f Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 14 Sep 2026 14:07:12 +0200 Subject: [PATCH 1/7] ipc: (cosmetic) make a function static Make ipc4_process_large_config_get() static, it's only called once in the same file where it's defined. Signed-off-by: Guennadi Liakhovetski --- src/include/ipc4/handler.h | 13 ------------- src/ipc/ipc4/handler-user.c | 10 ++++++---- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/include/ipc4/handler.h b/src/include/ipc4/handler.h index cb54ecc08db0..8f624a3c5c70 100644 --- a/src/include/ipc4/handler.h +++ b/src/include/ipc4/handler.h @@ -34,19 +34,6 @@ int ipc4_user_module_load(const struct comp_driver *drv, int ipc4_process_module_config(struct ipc4_message_request *ipc4, bool set, uint32_t *reply_ext); -/** - * @brief Process MOD_LARGE_CONFIG_GET in any execution context. - * @param[in] ipc4 IPC4 message request. - * @param[out] reply_ext Receives extension value for reply. - * @param[out] reply_tx_size Receives TX data size for reply. - * @param[out] reply_tx_data Receives TX data pointer for reply. - * @return IPC4 status code (0 on success). - */ -int ipc4_process_large_config_get(struct ipc4_message_request *ipc4, - uint32_t *reply_ext, - uint32_t *reply_tx_size, - void **reply_tx_data); - /** * @brief Process MOD_LARGE_CONFIG_SET in any execution context. * @param[in] ipc4 IPC4 message request. diff --git a/src/ipc/ipc4/handler-user.c b/src/ipc/ipc4/handler-user.c index 5cea1bf7dbe4..72b00bc32496 100644 --- a/src/ipc/ipc4/handler-user.c +++ b/src/ipc/ipc4/handler-user.c @@ -1091,10 +1091,11 @@ __cold static int ipc4_get_vendor_config_module_instance(struct comp_dev *dev, return IPC4_SUCCESS; } -__cold int ipc4_process_large_config_get(struct ipc4_message_request *ipc4, - uint32_t *reply_ext, - uint32_t *reply_tx_size, - void **reply_tx_data) +#ifdef CONFIG_SOF_USERSPACE_LL +__cold static int ipc4_process_large_config_get(struct ipc4_message_request *ipc4, + uint32_t *reply_ext, + uint32_t *reply_tx_size, + void **reply_tx_data) { struct ipc4_module_large_config_reply reply; const struct ipc4_module_large_config *config = @@ -1189,6 +1190,7 @@ __cold int ipc4_process_large_config_get(struct ipc4_message_request *ipc4, *reply_tx_data = data; return ret; } +#endif __cold static int ipc4_get_large_config_module_instance(struct ipc4_message_request *ipc4) { From eab9059af9a6b1d14dfac6dfb3ae513493c98e55 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 14 Sep 2026 14:14:42 +0200 Subject: [PATCH 2/7] schedule: dp: remove the "const" qualifiew from a function Remove "const" from the "param" argument of scheduler_dp_thread_ipc() - some of the wrapped methods have to modify it. Signed-off-by: Guennadi Liakhovetski --- src/audio/module_adapter/module/generic.c | 8 ++++---- src/audio/module_adapter/module_adapter.c | 2 +- src/include/sof/schedule/dp_schedule.h | 4 ++-- src/schedule/zephyr_dp_schedule_application.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/audio/module_adapter/module/generic.c b/src/audio/module_adapter/module/generic.c index 639dbc954dbb..889b5fdaec08 100644 --- a/src/audio/module_adapter/module/generic.c +++ b/src/audio/module_adapter/module/generic.c @@ -580,7 +580,7 @@ int module_prepare(struct processing_module *mod, #if CONFIG_SOF_USERSPACE_APPLICATION if (dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { - const union scheduler_dp_thread_ipc_param param = { + union scheduler_dp_thread_ipc_param param = { .pipeline_state = { .trigger_cmd = COMP_TRIGGER_PREPARE, .state = SOF_IPC4_PIPELINE_STATE_RUNNING, @@ -722,7 +722,7 @@ int module_reset(struct processing_module *mod) if (ops->reset) { #if CONFIG_SOF_USERSPACE_APPLICATION if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { - const union scheduler_dp_thread_ipc_param param = { + union scheduler_dp_thread_ipc_param param = { .pipeline_state.trigger_cmd = COMP_TRIGGER_STOP, }; ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_GLB_SET_PIPELINE_STATE, ¶m); @@ -939,7 +939,7 @@ int module_bind(struct processing_module *mod, const struct bind_info *bind_data if (ops->bind) { #if CONFIG_SOF_USERSPACE_APPLICATION if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { - const union scheduler_dp_thread_ipc_param param = { + union scheduler_dp_thread_ipc_param param = { .bind_data = bind_data, }; ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_BIND, ¶m); @@ -972,7 +972,7 @@ int module_unbind(struct processing_module *mod, const struct bind_info *unbind_ if (ops->unbind) { #if CONFIG_SOF_USERSPACE_APPLICATION if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { - const union scheduler_dp_thread_ipc_param param = { + union scheduler_dp_thread_ipc_param param = { .bind_data = unbind_data, }; ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_UNBIND, ¶m); diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c index 98234daa082c..e42b0e0fc66c 100644 --- a/src/audio/module_adapter/module_adapter.c +++ b/src/audio/module_adapter/module_adapter.c @@ -1413,7 +1413,7 @@ int module_adapter_trigger(struct comp_dev *dev, int cmd) #if CONFIG_SOF_USERSPACE_APPLICATION if (dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { /* Process DP module's trigger */ - const union scheduler_dp_thread_ipc_param param = { + union scheduler_dp_thread_ipc_param param = { .pipeline_state.trigger_cmd = cmd, }; return scheduler_dp_thread_ipc(mod, SOF_IPC4_GLB_SET_PIPELINE_STATE, diff --git a/src/include/sof/schedule/dp_schedule.h b/src/include/sof/schedule/dp_schedule.h index 2c72178cd766..182df29301bb 100644 --- a/src/include/sof/schedule/dp_schedule.h +++ b/src/include/sof/schedule/dp_schedule.h @@ -133,11 +133,11 @@ union scheduler_dp_thread_ipc_param { #if CONFIG_ZEPHYR_DP_SCHEDULER int scheduler_dp_thread_ipc(struct processing_module *pmod, unsigned int cmd, - const union scheduler_dp_thread_ipc_param *param); + union scheduler_dp_thread_ipc_param *param); #else static inline int scheduler_dp_thread_ipc(struct processing_module *pmod, unsigned int cmd, - const union scheduler_dp_thread_ipc_param *param) + union scheduler_dp_thread_ipc_param *param) { return 0; } diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c index 0cf8c457b9b8..75356d990b4c 100644 --- a/src/schedule/zephyr_dp_schedule_application.c +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -174,7 +174,7 @@ static void ipc_thread_unflatten_run(struct processing_module *pmod, struct ipc4 /* Signal an IPC and wait for processing completion */ int scheduler_dp_thread_ipc(struct processing_module *pmod, unsigned int cmd, - const union scheduler_dp_thread_ipc_param *param) + union scheduler_dp_thread_ipc_param *param) { if (!pmod) { tr_err(&dp_tr, "no thread module"); From d592bff980af9a4c2c4397242d41756e0c03cb5b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 14 Sep 2026 14:19:31 +0200 Subject: [PATCH 3/7] audio: module-adapter: (cosmetic) consolidate module freeing code No need to split module freeing code between module_free() and module_adapter_free(), merge it back together. Signed-off-by: Guennadi Liakhovetski --- src/audio/module_adapter/module/generic.c | 22 +++++++++++++++++----- src/audio/module_adapter/module_adapter.c | 11 ----------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/audio/module_adapter/module/generic.c b/src/audio/module_adapter/module/generic.c index 889b5fdaec08..48906bada70f 100644 --- a/src/audio/module_adapter/module/generic.c +++ b/src/audio/module_adapter/module/generic.c @@ -781,11 +781,23 @@ int module_free(struct processing_module *mod) struct module_data *md = &mod->priv; int ret = 0; - if (ops->free && (mod->dev->ipc_config.proc_domain != COMP_PROCESSING_DOMAIN_DP || - !IS_ENABLED(CONFIG_SOF_USERSPACE_APPLICATION))) { - ret = ops->free(mod); - if (ret) - comp_warn(mod->dev, "error: %d", ret); + if (ops->free) { + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL || + !IS_ENABLED(CONFIG_SOF_USERSPACE_APPLICATION)) { + ret = ops->free(mod); + if (ret) + comp_warn(mod->dev, "error: %d", ret); +#if CONFIG_SOF_USERSPACE_APPLICATION + } else { + /* + * 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 + */ + scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_DELETE_INSTANCE, NULL); +#endif + } } /* Free all memory shared by module_adapter & module */ diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c index e42b0e0fc66c..2da09b17f5a3 100644 --- a/src/audio/module_adapter/module_adapter.c +++ b/src/audio/module_adapter/module_adapter.c @@ -1483,17 +1483,6 @@ void module_adapter_free(struct comp_dev *dev) comp_dbg(dev, "start"); -#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 - */ - scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_DELETE_INSTANCE, NULL); -#endif - ret = module_free(mod); if (ret) comp_err(dev, "failed with error: %d", ret); From 89df374b131b17af15352276aeac47e78964366c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 14 Sep 2026 14:24:39 +0200 Subject: [PATCH 4/7] schedule: dp: use a macro instead of hard-coded mailbox size Use MAILBOX_HOSTBOX_SIZE to map the mailbox. Signed-off-by: Guennadi Liakhovetski --- src/schedule/zephyr_dp_schedule_application.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c index 75356d990b4c..0c058bc30004 100644 --- a/src/schedule/zephyr_dp_schedule_application.c +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -592,12 +592,12 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, /* Host mailbox partition for additional IPC parameters: read-only */ pdata->mpart[SOF_DP_PART_CFG] = (struct k_mem_partition){ .start = (uintptr_t)sys_cache_uncached_ptr_get((void *)MAILBOX_HOSTBOX_BASE), - .size = 4096, + .size = MAILBOX_HOSTBOX_SIZE, .attr = K_MEM_PARTITION_P_RO_U_RO, }; pdata->mpart[SOF_DP_PART_CFG_CACHE] = (struct k_mem_partition){ .start = (uintptr_t)MAILBOX_HOSTBOX_BASE, - .size = 4096, + .size = MAILBOX_HOSTBOX_SIZE, .attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB, }; From 05288debcf51f224ff2898f2a31282f701d07b18 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 14 Sep 2026 14:43:00 +0200 Subject: [PATCH 5/7] schedule: dp: add support for get/set_configuration With the "application" version of the DP scheduler individual module adapter methods are offloaded to the userspace thread context. Add two more methods to the offloaded set: get_configuration() and set_configuration(). Note, thet get_configuration() also requires copying data back to the coller, which until now wasn't done. Signed-off-by: Guennadi Liakhovetski --- .../module_adapter/module_adapter_ipc4.c | 52 +++++++-- src/include/sof/schedule/dp_schedule.h | 14 +++ src/schedule/zephyr_dp_schedule.h | 1 + src/schedule/zephyr_dp_schedule_application.c | 104 ++++++++++++++---- 4 files changed, 144 insertions(+), 27 deletions(-) diff --git a/src/audio/module_adapter/module_adapter_ipc4.c b/src/audio/module_adapter/module_adapter_ipc4.c index 7fa224a06d6c..a70eb77eff38 100644 --- a/src/audio/module_adapter/module_adapter_ipc4.c +++ b/src/audio/module_adapter/module_adapter_ipc4.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -263,10 +265,27 @@ int module_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_ return -EINVAL; } - if (interface->set_configuration) - return interface->set_configuration(mod, param_id, pos, data_offset_size, - (const uint8_t *)data, fragment_size, - NULL, 0); + if (interface->set_configuration) { +#if CONFIG_SOF_USERSPACE_APPLICATION + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { + union scheduler_dp_thread_ipc_param param = { + .set_config = { + .param_id = param_id, + .position = pos, + .data_offset_size = data_offset_size, + .data = data, + .fragment_size = fragment_size, + }, + }; + + return scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_LARGE_CONFIG_SET, ¶m); + } else +#endif + return interface->set_configuration(mod, param_id, pos, data_offset_size, + (const uint8_t *)data, fragment_size, + NULL, 0); + } + return 0; } @@ -297,9 +316,28 @@ int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_ } } - if (interface->get_configuration) - return interface->get_configuration(mod, param_id, data_offset_size, - (uint8_t *)data, fragment_size); + if (interface->get_configuration) { +#if CONFIG_SOF_USERSPACE_APPLICATION + if (mod->dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) { + union scheduler_dp_thread_ipc_param param = { + .get_config = { + .param_id = param_id, + .data_offset_size = *data_offset_size, + .data = data, + .fragment_size = fragment_size, + }, + }; + + int ret = scheduler_dp_thread_ipc(mod, SOF_IPC4_MOD_LARGE_CONFIG_SET, + ¶m); + if (!ret) + *data_offset_size = param.get_config.data_offset_size; + } else +#endif + return interface->get_configuration(mod, param_id, data_offset_size, + (uint8_t *)data, fragment_size); + } + /* * Return error if getter is not implemented. Otherwise, the host will suppose * the GET_VALUE command is successful, but the received cdata is not filled. diff --git a/src/include/sof/schedule/dp_schedule.h b/src/include/sof/schedule/dp_schedule.h index 182df29301bb..a2260febb2fc 100644 --- a/src/include/sof/schedule/dp_schedule.h +++ b/src/include/sof/schedule/dp_schedule.h @@ -15,6 +15,7 @@ #include #include #include +#include struct processing_module; struct module_ext_init_data; @@ -129,6 +130,19 @@ union scheduler_dp_thread_ipc_param { int n_sinks; struct sof_sink **sinks; } pipeline_state; + struct { + uint32_t param_id; + enum module_cfg_fragment_position position; + uint32_t data_offset_size; + size_t fragment_size; + const char *data; + } set_config; + struct { + uint32_t param_id; + uint32_t data_offset_size; + size_t fragment_size; + char *data; + } get_config; }; #if CONFIG_ZEPHYR_DP_SCHEDULER diff --git a/src/schedule/zephyr_dp_schedule.h b/src/schedule/zephyr_dp_schedule.h index 2119e44c9dfc..d33e6895507c 100644 --- a/src/schedule/zephyr_dp_schedule.h +++ b/src/schedule/zephyr_dp_schedule.h @@ -42,6 +42,7 @@ struct task_dp_pdata { struct processing_module *mod; /* the module to be scheduled */ uint32_t ll_cycles_to_start; /* current number of LL cycles till delayed start */ #if CONFIG_SOF_USERSPACE_APPLICATION + uint8_t *ipc_config_data; struct ipc4_flat *flat; struct k_mem_partition mpart[SOF_DP_PART_TYPE_COUNT]; #endif diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c index 0c058bc30004..02bd00da5bbc 100644 --- a/src/schedule/zephyr_dp_schedule_application.c +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -62,6 +62,19 @@ struct ipc4_flat { * valid and is covered by the DP thread's SOF_DP_PART_CFG partition. */ struct module_ext_init_data init_instance; + struct { + uint32_t param_id; + enum module_cfg_fragment_position position; + uint32_t data_offset_size; + size_t fragment_size; + const char *data; + } set_config; + struct { + uint32_t param_id; + uint32_t data_offset_size; + size_t fragment_size; + char *data; + } get_config; }; }; @@ -108,15 +121,39 @@ static int ipc_thread_flatten(unsigned int cmd, const union scheduler_dp_thread_ flat->pipeline_state.n_sinks * sizeof(flat->pipeline_state.sink[0])); } + break; + case SOF_IPC4_MOD_LARGE_CONFIG_SET: + flat->set_config.param_id = param->set_config.param_id; + flat->set_config.position = param->set_config.position; + flat->set_config.data_offset_size = param->set_config.data_offset_size; + flat->set_config.fragment_size = param->set_config.fragment_size; + flat->set_config.data = param->set_config.data; + break; + case SOF_IPC4_MOD_LARGE_CONFIG_GET: + flat->get_config.param_id = param->get_config.param_id; + flat->get_config.data_offset_size = param->get_config.data_offset_size; + flat->get_config.fragment_size = param->get_config.fragment_size; + flat->get_config.data = NULL/*param->get_config.data*/; } return 0; } +/* memory allocation helper structure */ +struct scheduler_dp_task_memory { + struct task task; + struct task_dp_pdata pdata; + struct comp_driver drv; + struct ipc4_flat flat; +}; + /* Unpack IPC data and execute a callback */ static void ipc_thread_unflatten_run(struct processing_module *pmod, struct ipc4_flat *flat) { const struct module_interface *const ops = pmod->dev->drv->adapter_ops; + struct scheduler_dp_task_memory *task_mem = container_of(flat, + struct scheduler_dp_task_memory, + flat); switch (flat->cmd) { case SOF_IPC4_MOD_BIND: @@ -167,6 +204,21 @@ static void ipc_thread_unflatten_run(struct processing_module *pmod, struct ipc4 flat->pipeline_state.sink, flat->pipeline_state.n_sinks); } + break; + case SOF_IPC4_MOD_LARGE_CONFIG_SET: + flat->ret = ops->set_configuration(pmod, flat->set_config.param_id, + flat->set_config.position, + flat->set_config.data_offset_size, + flat->set_config.data, + flat->set_config.fragment_size, NULL, 0); + break; + case SOF_IPC4_MOD_LARGE_CONFIG_GET: + flat->ret = ops->get_configuration(pmod, flat->get_config.param_id, + &flat->get_config.data_offset_size, + task_mem->pdata.ipc_config_data, + flat->get_config.fragment_size); + if (!flat->ret) + flat->get_config.data = task_mem->pdata.ipc_config_data; } } @@ -205,16 +257,24 @@ int scheduler_dp_thread_ipc(struct processing_module *pmod, unsigned int cmd, scheduler_dp_unlock(lock_key); - if (!ret) { - /* Wait for completion */ - ret = k_sem_take(&dp_sync[core], DP_THREAD_IPC_TIMEOUT); - if (ret < 0) - tr_err(&dp_tr, "Failed waiting for DP thread: %d", ret); - else - ret = pdata->flat->ret; + if (ret < 0) + return ret; + + /* Wait for completion */ + ret = k_sem_take(&dp_sync[core], DP_THREAD_IPC_TIMEOUT); + if (ret < 0) { + tr_err(&dp_tr, "Failed waiting for DP thread: %d", ret); + return ret; } - return ret; + if (!pdata->flat->ret && cmd == SOF_IPC4_MOD_LARGE_CONFIG_GET) { + /* A single case of returning data */ + param->get_config.data_offset_size = pdata->flat->get_config.data_offset_size; + return memcpy_s(param->get_config.data, SOF_IPC_MSG_MAX_SIZE, + pdata->flat->get_config.data, param->get_config.fragment_size); + } + + return pdata->flat->ret; } /* Go through all DP tasks and recalculate their readiness and deadlines @@ -415,14 +475,6 @@ static void scheduler_dp_domain_free(struct task_dp_pdata *pdata) objpool_free(&dp_mdom_head, mdom); } -/* memory allocation helper structure */ -struct scheduler_dp_task_memory { - struct task task; - struct task_dp_pdata pdata; - struct comp_driver drv; - struct ipc4_flat flat; -}; - void z_impl_scheduler_dp_internal_free(struct task *task) { struct task_dp_pdata *pdata = task->priv_data; @@ -500,20 +552,31 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, struct task_dp_pdata *pdata = &task_memory->pdata; + if (mod->dev->drv->adapter_ops->get_configuration) { + pdata->ipc_config_data = mod_alloc_ext(mod, SOF_MEM_FLAG_USER, SOF_IPC_MSG_MAX_SIZE, + 0); + if (!pdata->ipc_config_data) { + tr_err(&dp_tr, "Failed to allocate memory for get-config"); + goto e_stack; + } + } else { + pdata->ipc_config_data = NULL; + } + pdata->flat = &task_memory->flat; pdata->event = k_object_alloc(K_OBJ_EVENT); if (!pdata->event) { tr_err(&dp_tr, "Event object allocation failed"); ret = -ENOMEM; - goto e_stack; + goto e_config; } pdata->thread = k_object_alloc(K_OBJ_THREAD); if (!pdata->thread) { tr_err(&dp_tr, "Thread object allocation failed"); ret = -ENOMEM; - goto e_kobj; + goto e_event; } memset(&pdata->thread->arch, 0, sizeof(pdata->thread->arch)); @@ -636,10 +699,11 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, if (on_pool) objpool_free(&dp_mdom_head, mdom); k_thread_abort(pdata->thread_id); -e_kobj: - /* k_object_free looks for a pointer in the list, any invalid value can be passed */ k_object_free(pdata->thread); +e_event: k_object_free(pdata->event); +e_config: + mod_free(mod, pdata->ipc_config_data); e_stack: user_stack_free(p_stack); e_tmem: From ba3abba6a363f5dc5f0450b6eb66a58ca1d7dc51 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 14 Sep 2026 14:51:12 +0200 Subject: [PATCH 6/7] ipc: (cosmetic) add comments for buffer binding It is unclear in which context buffer binding should be executed when an LL and a DP modules are being bound with both running in userspace context. Since DP modules have limited visibility into the system and the userspace LL context on the other hand has access to most DP assets, perform binding and unbinding in LL thread context. Add comments to explain that. Signed-off-by: Guennadi Liakhovetski --- src/ipc/ipc4/handler-user.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ipc/ipc4/handler-user.c b/src/ipc/ipc4/handler-user.c index 72b00bc32496..94360942934b 100644 --- a/src/ipc/ipc4/handler-user.c +++ b/src/ipc/ipc4/handler-user.c @@ -1685,6 +1685,13 @@ __cold int ipc4_user_process_module_message(struct ipc4_message_request *ipc4, break; case SOF_IPC4_MOD_BIND: #ifdef CONFIG_SOF_USERSPACE_LL + /* + * bind and unbind can connect LL with DP. In that case it isn't + * immediately clear whether the handler should run in the DP + * thread context or in the LL IPC thread context. The LL IPC + * thread has access to DP modules, so we have to perform + * binding in the LL IPC thread context + */ ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat, ipc4_user_target_core_module(ipc4)); #else @@ -1693,6 +1700,7 @@ __cold int ipc4_user_process_module_message(struct ipc4_message_request *ipc4, break; case SOF_IPC4_MOD_UNBIND: #ifdef CONFIG_SOF_USERSPACE_LL + /* DP / LL: see comment above */ ret = ipc_user_forward_cmd(ipc4->primary.dat, ipc4->extension.dat, ipc4_user_target_core_module(ipc4)); #else From be08b4477e1260743ed1b4103595a560b0f43ec8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 16 Sep 2026 10:17:28 +0200 Subject: [PATCH 7/7] audio: data-blob: enable module-bound allocation DP modules cannot access the common userspace heap, to fix data blob allocations for them those allocations have to use mod_alloc() and mod_free() to eventually allocate memory on module accessible vregion. Signed-off-by: Guennadi Liakhovetski --- src/audio/data_blob.c | 66 ++++++++++++++++++------------- src/include/sof/audio/data_blob.h | 4 +- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/audio/data_blob.c b/src/audio/data_blob.c index 94ea19e829a7..a48ea1b150e7 100644 --- a/src/audio/data_blob.c +++ b/src/audio/data_blob.c @@ -29,8 +29,8 @@ struct comp_data_blob_handler { uint32_t single_blob:1; /**< Allocate only one blob. Module can not * be active while reconfguring. */ - void *(*alloc)(size_t size); /**< alternate allocator, maybe null */ - void (*free)(void *buf); /**< alternate free(), maybe null */ + void *(*alloc)(struct comp_data_blob_handler *, size_t);/**< allocator, maybe null */ + void (*free)(struct comp_data_blob_handler *, void *); /**< deallocator, maybe null */ /** validator for new data, maybe null */ int (*validator)(struct comp_dev *dev, void *new_data, uint32_t new_data_size); @@ -43,8 +43,8 @@ static void comp_free_data_blob(struct comp_data_blob_handler *blob_handler) if (!blob_handler->data) return; - blob_handler->free(blob_handler->data); - blob_handler->free(blob_handler->data_new); + blob_handler->free(blob_handler, blob_handler->data); + blob_handler->free(blob_handler, blob_handler->data_new); blob_handler->data = NULL; blob_handler->data_new = NULL; blob_handler->data_size = 0; @@ -75,7 +75,7 @@ void *comp_get_data_blob(struct comp_data_blob_handler *blob_handler, comp_dbg(blob_handler->dev, "new data available"); /* Free "old" data blob and set data to data_new pointer */ - blob_handler->free(blob_handler->data); + blob_handler->free(blob_handler, blob_handler->data); blob_handler->data = blob_handler->data_new; blob_handler->data_size = blob_handler->new_data_size; @@ -143,7 +143,7 @@ int comp_init_data_blob(struct comp_data_blob_handler *blob_handler, return 0; /* Data blob allocation */ - blob_handler->data = blob_handler->alloc(size); + blob_handler->data = blob_handler->alloc(blob_handler, size); if (!blob_handler->data) { comp_err(blob_handler->dev, "model->data allocation failed"); return -ENOMEM; @@ -232,7 +232,7 @@ int comp_data_blob_set(struct comp_data_blob_handler *blob_handler, if (blob_handler->single_blob) { if (data_offset_size != blob_handler->data_size) { - blob_handler->free(blob_handler->data); + blob_handler->free(blob_handler, blob_handler->data); blob_handler->data = NULL; } else { blob_handler->data_new = blob_handler->data; @@ -241,7 +241,7 @@ int comp_data_blob_set(struct comp_data_blob_handler *blob_handler, } if (!blob_handler->data_new) { - blob_handler->data_new = blob_handler->alloc(data_offset_size); + blob_handler->data_new = blob_handler->alloc(blob_handler, data_offset_size); if (!blob_handler->data_new) { comp_err(blob_handler->dev, "blob_handler->data_new allocation failed."); return -ENOMEM; @@ -277,7 +277,7 @@ int comp_data_blob_set(struct comp_data_blob_handler *blob_handler, blob_handler->new_data_size); if (ret < 0) { comp_err(blob_handler->dev, "new data is invalid! discarding it..."); - blob_handler->free(blob_handler->data_new); + blob_handler->free(blob_handler, blob_handler->data_new); blob_handler->data_new = NULL; return ret; } @@ -288,7 +288,7 @@ int comp_data_blob_set(struct comp_data_blob_handler *blob_handler, * the new configuration presence is checked in copy(). */ if (blob_handler->dev->state == COMP_STATE_READY) { - blob_handler->free(blob_handler->data); + blob_handler->free(blob_handler, blob_handler->data); blob_handler->data = NULL; } @@ -347,7 +347,7 @@ int ipc4_comp_data_blob_set(struct comp_data_blob_handler *blob_handler, if (blob_handler->single_blob) { if (data_offset != blob_handler->data_size) { - blob_handler->free(blob_handler->data); + blob_handler->free(blob_handler, blob_handler->data); blob_handler->data = NULL; } else { blob_handler->data_new = blob_handler->data; @@ -357,7 +357,7 @@ int ipc4_comp_data_blob_set(struct comp_data_blob_handler *blob_handler, if (!blob_handler->data_new) { blob_handler->data_new = - blob_handler->alloc(data_offset); + blob_handler->alloc(blob_handler, data_offset); if (!blob_handler->data_new) { comp_err(blob_handler->dev, @@ -376,7 +376,7 @@ int ipc4_comp_data_blob_set(struct comp_data_blob_handler *blob_handler, blob_handler->new_data_size, data, valid_data_size); if (ret) { comp_err(blob_handler->dev, "failed to copy fragment"); - blob_handler->free(blob_handler->data_new); + blob_handler->free(blob_handler, blob_handler->data_new); blob_handler->data_new = NULL; blob_handler->new_data_size = 0; blob_handler->data_pos = 0; @@ -411,7 +411,7 @@ int ipc4_comp_data_blob_set(struct comp_data_blob_handler *blob_handler, data, valid_data_size); if (ret) { comp_err(blob_handler->dev, "failed to copy fragment"); - blob_handler->free(blob_handler->data_new); + blob_handler->free(blob_handler, blob_handler->data_new); blob_handler->data_new = NULL; blob_handler->new_data_size = 0; blob_handler->data_pos = 0; @@ -431,7 +431,7 @@ int ipc4_comp_data_blob_set(struct comp_data_blob_handler *blob_handler, * the new configuration presence is checked in copy(). */ if (blob_handler->dev->state == COMP_STATE_READY) { - blob_handler->free(blob_handler->data); + blob_handler->free(blob_handler, blob_handler->data); blob_handler->data = NULL; } @@ -510,7 +510,7 @@ int comp_data_blob_set_cmd(struct comp_data_blob_handler *blob_handler, if (blob_handler->single_blob) { if (cdata->data->size != blob_handler->data_size) { - blob_handler->free(blob_handler->data); + blob_handler->free(blob_handler, blob_handler->data); blob_handler->data = NULL; } else { blob_handler->data_new = blob_handler->data; @@ -520,7 +520,7 @@ int comp_data_blob_set_cmd(struct comp_data_blob_handler *blob_handler, if (!blob_handler->data_new) { blob_handler->data_new = - blob_handler->alloc(cdata->data->size); + blob_handler->alloc(blob_handler, cdata->data->size); if (!blob_handler->data_new) { comp_err(blob_handler->dev, "blob_handler->data_new allocation failed."); return -ENOMEM; @@ -556,7 +556,7 @@ int comp_data_blob_set_cmd(struct comp_data_blob_handler *blob_handler, blob_handler->new_data_size); if (ret < 0) { comp_err(blob_handler->dev, "new data blob invalid, discarding"); - blob_handler->free(blob_handler->data_new); + blob_handler->free(blob_handler, blob_handler->data_new); blob_handler->data_new = NULL; return ret; } @@ -567,7 +567,7 @@ int comp_data_blob_set_cmd(struct comp_data_blob_handler *blob_handler, * the new configuration presence is checked in copy(). */ if (blob_handler->dev->state == COMP_STATE_READY) { - blob_handler->free(blob_handler->data); + blob_handler->free(blob_handler, blob_handler->data); blob_handler->data = NULL; } @@ -659,28 +659,37 @@ int comp_data_blob_get_cmd(struct comp_data_blob_handler *blob_handler, } EXPORT_SYMBOL(comp_data_blob_get_cmd); -static void *default_alloc(size_t size) +static void *default_alloc(struct comp_data_blob_handler *handler, size_t size) { + if (handler->dev->mod) + return mod_alloc_ext(handler->dev->mod, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, size, 0); return sof_heap_alloc(sof_sys_user_heap_get(), SOF_MEM_FLAG_USER | SOF_MEM_FLAG_LARGE_BUFFER, size, 0); } -static void default_free(void *buf) +static void default_free(struct comp_data_blob_handler *handler, void *buf) { - sof_heap_free(sof_sys_user_heap_get(), buf); + if (handler->dev->mod) + mod_free(handler->dev->mod, buf); + else + sof_heap_free(sof_sys_user_heap_get(), buf); } struct comp_data_blob_handler * comp_data_blob_handler_new_ext(struct comp_dev *dev, bool single_blob, - void *(*alloc)(size_t size), - void (*free)(void *buf)) + void *(*alloc)(struct comp_data_blob_handler *, size_t), + void (*free)(struct comp_data_blob_handler *, void *)) { struct comp_data_blob_handler *handler; comp_dbg(dev, "entry"); - handler = sof_heap_alloc(sof_sys_user_heap_get(), SOF_MEM_FLAG_USER, - sizeof(struct comp_data_blob_handler), 0); + if (dev->mod) + handler = mod_alloc(dev->mod, sizeof(struct comp_data_blob_handler)); + else + handler = sof_heap_alloc(sof_sys_user_heap_get(), SOF_MEM_FLAG_USER, + sizeof(struct comp_data_blob_handler), 0); if (handler) { memset(handler, 0, sizeof(*handler)); @@ -701,6 +710,9 @@ void comp_data_blob_handler_free(struct comp_data_blob_handler *blob_handler) comp_free_data_blob(blob_handler); - sof_heap_free(sof_sys_user_heap_get(), blob_handler); + if (blob_handler->dev->mod) + mod_free(blob_handler->dev->mod, blob_handler); + else + sof_heap_free(sof_sys_user_heap_get(), blob_handler); } EXPORT_SYMBOL(comp_data_blob_handler_free); diff --git a/src/include/sof/audio/data_blob.h b/src/include/sof/audio/data_blob.h index d2eb8a74d66d..610a2bd85e1e 100644 --- a/src/include/sof/audio/data_blob.h +++ b/src/include/sof/audio/data_blob.h @@ -116,8 +116,8 @@ int comp_data_blob_get_cmd(struct comp_data_blob_handler *blob_handler, */ struct comp_data_blob_handler * comp_data_blob_handler_new_ext(struct comp_dev *dev, bool single_blob, - void *(*alloc)(size_t size), - void (*free)(void *buf)); + void *(*alloc)(struct comp_data_blob_handler *, size_t), + void (*free)(struct comp_data_blob_handler *, void *)); /** * Returns new data blob handler.