Skip to content

Commit

Permalink
llext: add dependencies
Browse files Browse the repository at this point in the history
Add support for auxiliary modules, exporting symbols to other
modules. In such cases Zephyr LLEXT API generates a dependency list,
available while the dependent module is loaded. SOF now preserves the
minimum module context even while it isn't used, that includes
dependency lists, so on a repeated load they're still available.

Signed-off-by: Guennadi Liakhovetski <[email protected]>
  • Loading branch information
lyakh authored and lgirdwood committed Feb 18, 2025
1 parent 0d35248 commit 94ce70e
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 9 deletions.
3 changes: 3 additions & 0 deletions src/include/sof/llext_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config

int llext_manager_free_module(const uint32_t component_id);

int llext_manager_add_library(uint32_t module_id);

bool comp_is_llext(struct comp_dev *comp);
#else
#define module_is_llext(mod) false
#define llext_manager_allocate_module(ipc_config, ipc_specific_config) 0
#define llext_manager_free_module(component_id) 0
#define llext_manager_add_library(module_id) 0
#define comp_is_llext(comp) false
#endif

Expand Down
14 changes: 11 additions & 3 deletions src/library_manager/lib_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -1032,14 +1032,22 @@ int lib_manager_load_library(uint32_t dma_id, uint32_t lib_id, uint32_t type)
rfree((__sparse_force void *)man_tmp_buffer);

cleanup:
#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL
core_kcps_adjust(cpu_get_id(), -(CLK_MAX_CPU_HZ / 1000));
#endif
rfree((void *)dma_ext->dma_addr);
lib_manager_dma_deinit(dma_ext, dma_id);
rfree(dma_ext);
_ext_lib->runtime_data = NULL;

uint32_t module_id = lib_id << LIB_MANAGER_LIB_ID_SHIFT;
const struct sof_man_module *mod = lib_manager_get_module_manifest(module_id);

if (module_is_llext(mod) && !ret)
/* Auxiliary LLEXT libraries need to be linked upon loading */
ret = llext_manager_add_library(module_id);

#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL
core_kcps_adjust(cpu_get_id(), -(CLK_MAX_CPU_HZ / 1000));
#endif

if (!ret)
tr_info(&ipc_tr, "loaded library id: %u", lib_id);

Expand Down
123 changes: 117 additions & 6 deletions src/library_manager/llext_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,6 @@ static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw

tr_dbg(&lib_manager_tr, "mod_id: %u", module_id);

if (!ctx->mod)
llext_manager_mod_init(ctx, desc);

if (entry_index >= desc->header.num_module_entries) {
tr_err(&lib_manager_tr, "Invalid driver index %u exceeds %d",
entry_index, desc->header.num_module_entries - 1);
Expand Down Expand Up @@ -462,12 +459,41 @@ static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw
return mod_ctx_idx;
}

static int llext_lib_find(const struct llext *llext, struct lib_manager_module **dep_ctx)
{
struct ext_library *_ext_lib = ext_lib_get();
unsigned int i, j;

if (!llext)
return -EINVAL;

for (i = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) {
if (!_ext_lib->desc[i])
continue;

for (j = 0; j < _ext_lib->desc[i]->n_mod; j++)
if (_ext_lib->desc[i]->mod[j].llext == llext) {
*dep_ctx = _ext_lib->desc[i]->mod + j;
return i;
}
}

return -ENOENT;
}

static void llext_depend_unlink(struct lib_manager_module *dep_ctx[], int n)
{
for (; n >= 0; n--)
if (dep_ctx[n] && dep_ctx[n]->llext->use_count == 1)
llext_manager_unload_module(dep_ctx[n]);
}

uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config,
const void *ipc_specific_config)
{
uint32_t module_id = IPC4_MOD_ID(ipc_config->id);
/* Library manifest */
struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *)
const struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *)
lib_manager_get_library_manifest(module_id);
/* Library context */
struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id);
Expand Down Expand Up @@ -500,9 +526,50 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config
}

if (!mctx->mapped) {
/* Map executable code and data */
int ret = llext_manager_load_module(mctx);
int i, ret;

/*
* Check if any dependencies need to be mapped - collect
* pointers to library contexts
*/
struct lib_manager_module *dep_ctx[LLEXT_MAX_DEPENDENCIES] = {};

for (i = 0; i < ARRAY_SIZE(mctx->llext->dependency); i++) {
/* Dependencies are filled from the beginning of the array upwards */
if (!mctx->llext->dependency[i])
break;

/*
* Protected by the IPC serialization, but maybe we should protect the
* use-count explicitly too. Currently the use-count is first incremented
* when an auxiliary library is loaded, it was then additionally incremented
* when the current dependent module was mapped. If it's higher than two,
* then some other modules also depend on it and have already mapped it.
*/
if (mctx->llext->dependency[i]->use_count > 2)
continue;

/* First user of this dependency, load it into SRAM */
ret = llext_lib_find(mctx->llext->dependency[i], &dep_ctx[i]);
if (ret < 0) {
tr_err(&lib_manager_tr,
"Unmet dependency: cannot find dependency %u", i);
continue;
}

tr_dbg(&lib_manager_tr, "%s depending on %s index %u, %u users",
mctx->llext->name, mctx->llext->dependency[i]->name,
dep_ctx[i]->start_idx, mctx->llext->dependency[i]->use_count);

ret = llext_manager_load_module(dep_ctx[i]);
if (ret < 0) {
llext_depend_unlink(dep_ctx, i - 1);
return 0;
}
}

/* Map executable code and data */
ret = llext_manager_load_module(mctx);
if (ret < 0)
return 0;
}
Expand Down Expand Up @@ -547,6 +614,17 @@ int llext_manager_free_module(const uint32_t component_id)
return 0;
}

struct lib_manager_module *dep_ctx[LLEXT_MAX_DEPENDENCIES] = {};
int i; /* signed to match llext_depend_unlink() */

for (i = 0; i < ARRAY_SIZE(mctx->llext->dependency); i++)
if (llext_lib_find(mctx->llext->dependency[i], &dep_ctx[i]) < 0)
break;

/* Last user cleaning up, put dependencies */
if (i)
llext_depend_unlink(dep_ctx, i - 1);

/*
* The last instance of the module has been destroyed and it can now be
* unloaded from SRAM
Expand All @@ -559,6 +637,39 @@ int llext_manager_free_module(const uint32_t component_id)
return llext_manager_unload_module(mctx);
}

/* An auxiliary library has been loaded, need to read in its exported symbols */
int llext_manager_add_library(uint32_t module_id)
{
struct lib_manager_mod_ctx *const ctx = lib_manager_get_mod_ctx(module_id);

if (ctx->mod) {
tr_err(&lib_manager_tr, "module_id: %#x: repeated load!", module_id);
return -EBUSY;
}

const struct sof_man_fw_desc *desc = lib_manager_get_library_manifest(module_id);
unsigned int i;

if (!ctx->mod)
llext_manager_mod_init(ctx, desc);

for (i = 0; i < ctx->n_mod; i++) {
const struct sof_man_module *mod = lib_manager_get_module_manifest(module_id + i);

if (mod->type.load_type == SOF_MAN_MOD_TYPE_LLEXT_AUX) {
const struct sof_man_module_manifest *mod_manifest;
const struct sof_module_api_build_info *buildinfo;
int ret = llext_manager_link_single(module_id + i, desc, ctx,
(const void **)&buildinfo, &mod_manifest);

if (ret < 0)
return ret;
}
}

return 0;
}

bool comp_is_llext(struct comp_dev *comp)
{
const uint32_t module_id = IPC4_MOD_ID(comp->ipc_config.id);
Expand Down

0 comments on commit 94ce70e

Please sign in to comment.