Skip to content

Commit 7bc8a28

Browse files
committed
llext: avoid reloading libraries during resume
Currently when resuming from D3 we lose the complete LLEXT context, which forces SOF to reload all the libraries, which costs time. This isn't necessary, because the libraries are stored in DRAM, which preserves its contents across DSP reset. Instead of reloading save LLEXT context before power down and reload it when resuming. Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
1 parent ecde5a7 commit 7bc8a28

5 files changed

Lines changed: 324 additions & 0 deletions

File tree

src/include/sof/llext_manager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,17 @@ int llext_manager_free_module(const uint32_t component_id);
3030
int llext_manager_add_library(uint32_t module_id);
3131

3232
bool comp_is_llext(struct comp_dev *comp);
33+
34+
int llext_manager_store_to_dram(void);
35+
int llext_manager_restore_from_dram(void);
3336
#else
3437
#define module_is_llext(mod) false
3538
#define llext_manager_allocate_module(ipc_config, ipc_specific_config) 0
3639
#define llext_manager_free_module(component_id) 0
3740
#define llext_manager_add_library(module_id) 0
3841
#define comp_is_llext(comp) false
42+
#define llext_manager_store_to_dram() 0
43+
#define llext_manager_restore_from_dram() 0
3944
#endif
4045

4146
#endif

src/ipc/ipc4/handler.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <sof/lib/mailbox.h>
2424
#include <sof/lib/memory.h>
2525
#include <sof/lib/pm_runtime.h>
26+
#include <sof/llext_manager.h>
2627
#include <sof/math/numbers.h>
2728
#include <sof/tlv.h>
2829
#include <sof/trace/trace.h>
@@ -1433,6 +1434,11 @@ __cold static int ipc4_module_process_dx(struct ipc4_message_request *ipc4)
14331434
return IPC4_BUSY;
14341435
}
14351436

1437+
ret = llext_manager_store_to_dram();
1438+
if (ret < 0)
1439+
ipc_cmd_err(&ipc_tr, "Error %d saving LLEXT context. Resume might fail.",
1440+
ret);
1441+
l3_heap_save();
14361442
#if defined(CONFIG_PM)
14371443
ipc_get()->task_mask |= IPC_TASK_POWERDOWN;
14381444
#endif

src/library_manager/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ if(CONFIG_LIBRARY_MANAGER)
55

66
if (CONFIG_MM_DRV AND CONFIG_LLEXT)
77
add_local_sources(sof llext_manager.c)
8+
add_local_sources_ifdef(CONFIG_L3_HEAP sof llext_manager_dram.c)
89
endif()
910
endif()
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation. All rights reserved.
4+
5+
#include <rtos/alloc.h>
6+
7+
#include <sof/lib_manager.h>
8+
#include <ipc/topology.h>
9+
10+
#include <zephyr/llext/buf_loader.h>
11+
#include <zephyr/llext/llext.h>
12+
#include <zephyr/logging/log_ctrl.h>
13+
14+
LOG_MODULE_DECLARE(lib_manager, CONFIG_SOF_LOG_LEVEL);
15+
16+
struct lib_manager_dram_storage {
17+
struct ext_library ext_lib;
18+
struct lib_manager_mod_ctx *ctx;
19+
struct lib_manager_module *mod;
20+
struct llext *llext;
21+
struct llext_buf_loader *bldr;
22+
struct llext_elf_sect_map *sect;
23+
struct llext_symbol *sym;
24+
unsigned int n_llext;
25+
};
26+
27+
__imrdata static struct lib_manager_dram_storage lib_manager_dram;
28+
29+
int llext_manager_store_to_dram(void)
30+
{
31+
struct ext_library *_ext_lib = ext_lib_get();
32+
unsigned int i, j, k, l, n_lib, n_mod, n_llext, n_sect, n_sym;
33+
size_t buf_size;
34+
35+
if (lib_manager_dram.n_llext) {
36+
tr_err(&lib_manager_tr, "context already saved");
37+
return 0;
38+
}
39+
40+
/*
41+
* Count libraries, modules, instantiated extensions, sections and exported
42+
* symbols in them. Allocate a buffer of required size.
43+
*/
44+
lib_manager_dram.ext_lib = *_ext_lib;
45+
for (i = 0, n_lib = 0, n_mod = 0, n_llext = 0, n_sect = 0, n_sym = 0;
46+
i < ARRAY_SIZE(_ext_lib->desc); i++)
47+
if (_ext_lib->desc[i]) {
48+
n_lib++;
49+
n_mod += _ext_lib->desc[i]->n_mod;
50+
for (k = 0; k < _ext_lib->desc[i]->n_mod; k++)
51+
if (_ext_lib->desc[i]->mod[k].ebl) {
52+
n_llext++;
53+
n_sect += _ext_lib->desc[i]->mod[k].llext->sect_cnt;
54+
n_sym += _ext_lib->desc[i]->mod[k].llext->exp_tab.sym_cnt;
55+
tr_dbg(&lib_manager_tr, "add %u exported syms",
56+
_ext_lib->desc[i]->mod[k].llext->exp_tab.sym_cnt);
57+
}
58+
}
59+
60+
buf_size = sizeof(lib_manager_dram.ctx[0]) * n_lib +
61+
sizeof(lib_manager_dram.mod[0]) * n_mod +
62+
sizeof(lib_manager_dram.sect[0]) * n_sect +
63+
sizeof(lib_manager_dram.sym[0]) * n_sym +
64+
(sizeof(lib_manager_dram.llext[0]) + sizeof(lib_manager_dram.bldr[0])) * n_llext;
65+
66+
lib_manager_dram.ctx = rmalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_L3,
67+
buf_size);
68+
if (!lib_manager_dram.ctx)
69+
return -ENOMEM;
70+
71+
lib_manager_dram.mod = (struct lib_manager_module *)(lib_manager_dram.ctx + n_lib);
72+
lib_manager_dram.sect = (struct llext_elf_sect_map *)(lib_manager_dram.mod + n_mod);
73+
lib_manager_dram.llext = (struct llext *)(lib_manager_dram.sect + n_sect);
74+
lib_manager_dram.bldr = (struct llext_buf_loader *)(lib_manager_dram.llext + n_llext);
75+
lib_manager_dram.sym = (struct llext_symbol *)(lib_manager_dram.bldr + n_llext);
76+
77+
tr_dbg(&lib_manager_tr, "backup %u libs of %u modules with %u LLEXT with %u sections",
78+
n_lib, n_mod, n_llext, n_sect);
79+
80+
tr_dbg(&lib_manager_tr, "backup %p to %p, mod %p, loader %p",
81+
lib_manager_dram.ctx, (void *)((uint8_t *)lib_manager_dram.ctx + buf_size),
82+
lib_manager_dram.mod, lib_manager_dram.bldr);
83+
84+
/* Walk all libraries */
85+
for (i = 0, j = 0, l = 0, n_mod = 0, n_sect = 0, n_sym = 0;
86+
i < ARRAY_SIZE(_ext_lib->desc); i++) {
87+
if (!_ext_lib->desc[i])
88+
continue;
89+
90+
struct lib_manager_module *mod = _ext_lib->desc[i]->mod;
91+
92+
/* Copy all modules in each library */
93+
lib_manager_dram.ctx[j++] = *_ext_lib->desc[i];
94+
memcpy(lib_manager_dram.mod + n_mod, mod,
95+
sizeof(lib_manager_dram.mod[0]) * _ext_lib->desc[i]->n_mod);
96+
tr_dbg(&lib_manager_tr, "lib %u base %p", j - 1,
97+
lib_manager_dram.ctx[j - 1].base_addr);
98+
n_mod += _ext_lib->desc[i]->n_mod;
99+
100+
/* Copy instantiated extensions */
101+
for (k = 0; k < _ext_lib->desc[i]->n_mod; k++) {
102+
if (!mod[k].llext)
103+
continue;
104+
105+
tr_dbg(&lib_manager_tr, "mod %u of %u sections", k,
106+
mod[k].llext->sect_cnt);
107+
108+
/* Copy the extension and the loader */
109+
lib_manager_dram.llext[l] = *mod[k].llext;
110+
lib_manager_dram.bldr[l] = *mod[k].ebl;
111+
112+
/* Copy the section map */
113+
memcpy(lib_manager_dram.sect + n_sect, mod[k].ebl->loader.sect_map,
114+
mod[k].llext->sect_cnt * sizeof(lib_manager_dram.sect[0]));
115+
n_sect += mod[k].llext->sect_cnt;
116+
117+
/* Copy exported symbols */
118+
if (mod[k].llext->exp_tab.sym_cnt) {
119+
memcpy(lib_manager_dram.sym + n_sym, mod[k].llext->exp_tab.syms,
120+
mod[k].llext->exp_tab.sym_cnt *
121+
sizeof(lib_manager_dram.sym[0]));
122+
lib_manager_dram.llext[l].exp_tab.syms = lib_manager_dram.sym +
123+
n_sym;
124+
n_sym += mod[k].llext->exp_tab.sym_cnt;
125+
}
126+
127+
l++;
128+
}
129+
}
130+
131+
/* Also flatten dependency lists */
132+
int ret = llext_relink_dependency(lib_manager_dram.llext, n_llext);
133+
134+
if (ret < 0) {
135+
tr_err(&lib_manager_tr, "Inconsistent dependencies!");
136+
return ret;
137+
}
138+
139+
lib_manager_dram.n_llext = n_llext;
140+
dcache_writeback_region(&lib_manager_dram, sizeof(lib_manager_dram));
141+
dcache_writeback_region(lib_manager_dram.ctx, buf_size);
142+
143+
return 0;
144+
}
145+
146+
int llext_manager_restore_from_dram(void)
147+
{
148+
lib_manager_init();
149+
150+
struct ext_library *_ext_lib = ext_lib_get();
151+
unsigned int i, j, k, n_mod, n_llext, n_sect, n_sym;
152+
153+
if (!lib_manager_dram.n_llext || !lib_manager_dram.ctx) {
154+
tr_dbg(&lib_manager_tr, "No modules saved");
155+
dcache_writeback_region(&lib_manager_dram, sizeof(lib_manager_dram));
156+
return 0;
157+
}
158+
159+
/* arrays of pointers for llext_restore() */
160+
void **ptr_array = rmalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
161+
sizeof(*ptr_array) * lib_manager_dram.n_llext * 2);
162+
163+
if (!ptr_array)
164+
return -ENOMEM;
165+
166+
struct llext_loader **ldr = (struct llext_loader **)ptr_array;
167+
struct llext **llext = (struct llext **)(ptr_array + lib_manager_dram.n_llext);
168+
169+
*_ext_lib = lib_manager_dram.ext_lib;
170+
171+
for (i = 0, j = 0, n_mod = 0, n_llext = 0, n_sect = 0, n_sym = 0;
172+
i < ARRAY_SIZE(_ext_lib->desc); i++) {
173+
if (!lib_manager_dram.ext_lib.desc[i]) {
174+
_ext_lib->desc[i] = NULL;
175+
continue;
176+
}
177+
178+
/* panics on failure */
179+
struct lib_manager_mod_ctx *ctx = rmalloc(SOF_MEM_ZONE_SYS, SOF_MEM_FLAG_COHERENT,
180+
SOF_MEM_CAPS_RAM, sizeof(*ctx));
181+
182+
*ctx = lib_manager_dram.ctx[j++];
183+
184+
struct lib_manager_module *mod = rmalloc(SOF_MEM_ZONE_RUNTIME_SHARED,
185+
SOF_MEM_FLAG_COHERENT, SOF_MEM_CAPS_RAM,
186+
ctx->n_mod * sizeof(ctx->mod[0]));
187+
188+
if (!mod) {
189+
tr_err(&lib_manager_tr, "module allocation failure");
190+
goto nomem;
191+
}
192+
tr_dbg(&lib_manager_tr, "%u modules alloc %p base %p copy %#zx",
193+
ctx->n_mod, (void *)mod, ctx->base_addr, ctx->n_mod * sizeof(ctx->mod[0]));
194+
195+
memcpy(mod, lib_manager_dram.mod + n_mod, sizeof(mod[0]) * ctx->n_mod);
196+
n_mod += ctx->n_mod;
197+
ctx->mod = mod;
198+
199+
for (k = 0; k < ctx->n_mod; k++) {
200+
if (!mod[k].llext)
201+
continue;
202+
203+
struct llext_buf_loader *bldr = rmalloc(SOF_MEM_ZONE_RUNTIME_SHARED,
204+
0, SOF_MEM_CAPS_RAM, sizeof(*bldr));
205+
206+
if (!bldr) {
207+
tr_err(&lib_manager_tr, "loader allocation failure");
208+
goto nomem;
209+
}
210+
211+
llext[n_llext] = lib_manager_dram.llext + n_llext;
212+
213+
*bldr = lib_manager_dram.bldr[n_llext];
214+
215+
bldr->loader.sect_map = lib_manager_dram.sect + n_sect;
216+
217+
n_sect += llext[n_llext]->sect_cnt;
218+
if (llext[n_llext]->exp_tab.sym_cnt) {
219+
tr_dbg(&lib_manager_tr, "got %u exported symbols",
220+
llext[n_llext]->exp_tab.sym_cnt);
221+
222+
if (llext[n_llext]->exp_tab.syms != lib_manager_dram.sym + n_sym) {
223+
tr_err(&lib_manager_tr,
224+
"bug detected! pointer mismatch %p vs. %p",
225+
(void *)llext[n_llext]->exp_tab.syms,
226+
(void *)(lib_manager_dram.sym + n_sym));
227+
goto nomem;
228+
}
229+
230+
n_sym += llext[n_llext]->exp_tab.sym_cnt;
231+
}
232+
233+
mod[k].ebl = bldr;
234+
235+
ldr[n_llext++] = &bldr->loader;
236+
}
237+
238+
_ext_lib->desc[i] = ctx;
239+
}
240+
241+
int ret = llext_restore(llext, ldr, lib_manager_dram.n_llext);
242+
243+
if (ret < 0) {
244+
tr_err(&lib_manager_tr, "Zephyr failed to restore: %d", ret);
245+
goto nomem;
246+
}
247+
248+
/* Rewrite to correct LLEXT pointers, created by Zephyr */
249+
for (i = 0, n_llext = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) {
250+
struct lib_manager_mod_ctx *ctx = _ext_lib->desc[i];
251+
252+
if (!ctx)
253+
continue;
254+
255+
struct lib_manager_module *mod = ctx->mod;
256+
257+
for (k = 0; k < ctx->n_mod; k++) {
258+
if (mod[k].llext)
259+
mod[k].llext = llext[n_llext++];
260+
}
261+
}
262+
263+
tr_info(&lib_manager_tr, "restored %u modules with %u LLEXT", n_mod, n_llext);
264+
265+
rfree(lib_manager_dram.ctx);
266+
lib_manager_dram.ctx = NULL;
267+
lib_manager_dram.sect = NULL;
268+
lib_manager_dram.llext = NULL;
269+
lib_manager_dram.bldr = NULL;
270+
lib_manager_dram.sym = NULL;
271+
rfree(ldr);
272+
273+
lib_manager_dram.n_llext = 0;
274+
275+
return 0;
276+
277+
nomem:
278+
tr_err(&lib_manager_tr, "Restore failed");
279+
for (i = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) {
280+
struct lib_manager_mod_ctx *ctx = _ext_lib->desc[i];
281+
282+
if (!ctx)
283+
continue;
284+
285+
struct lib_manager_module *mod = ctx->mod;
286+
287+
if (!mod)
288+
continue;
289+
290+
for (k = 0; k < ctx->n_mod; k++) {
291+
if (mod[k].llext)
292+
llext_unload(&mod[k].llext);
293+
294+
if (mod[k].ebl)
295+
rfree(mod[k].ebl);
296+
}
297+
298+
rfree(mod);
299+
rfree(ctx);
300+
}
301+
302+
/* at least create a sane empty lib-manager context */
303+
memset(_ext_lib->desc, 0, sizeof(_ext_lib->desc));
304+
305+
rfree(ldr);
306+
307+
return -ENOMEM;
308+
}

zephyr/wrapper.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
#include <sof/init.h>
9+
#include <sof/llext_manager.h>
910
#include <rtos/idc.h>
1011
#include <rtos/interrupt.h>
1112
#include <sof/drivers/interrupt-map.h>
@@ -187,6 +188,9 @@ static int boot_complete(void)
187188
*/
188189
return 0;
189190
#else
191+
if (llext_manager_restore_from_dram() < 0)
192+
LOG_ERR("LLEXT restore failed");
193+
190194
/* let host know DSP boot is complete */
191195
return platform_boot_complete(0);
192196
#endif /* CONFIG_IMX93_A55 */

0 commit comments

Comments
 (0)