Skip to content

Commit 5e6ae48

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 5e6ae48

File tree

5 files changed

+336
-0
lines changed

5 files changed

+336
-0
lines changed

src/include/sof/llext_manager.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,12 @@ bool comp_is_llext(struct comp_dev *comp);
3838
#define comp_is_llext(comp) false
3939
#endif
4040

41+
#if CONFIG_LLEXT && !CONFIG_ADSP_IMR_CONTEXT_SAVE
42+
int llext_manager_store_to_dram(void);
43+
int llext_manager_restore_from_dram(void);
44+
#else
45+
#define llext_manager_store_to_dram() 0
46+
#define llext_manager_restore_from_dram() 0
47+
#endif
48+
4149
#endif

src/ipc/ipc4/handler.c

Lines changed: 10 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,15 @@ __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+
1442+
#if CONFIG_L3_HEAP && !CONFIG_ADSP_IMR_CONTEXT_SAVE
1443+
l3_heap_save();
1444+
#endif
1445+
14361446
#if defined(CONFIG_PM)
14371447
ipc_get()->task_mask |= IPC_TASK_POWERDOWN;
14381448
#endif

src/library_manager/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@ if(CONFIG_LIBRARY_MANAGER)
55

66
if (CONFIG_MM_DRV AND CONFIG_LLEXT)
77
add_local_sources(sof llext_manager.c)
8+
if(NOT CONFIG_ADSP_IMR_CONTEXT_SAVE)
9+
add_local_sources_ifdef(CONFIG_L3_HEAP sof llext_manager_dram.c)
10+
endif()
811
endif()
912
endif()
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation.
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((__sparse_force void __sparse_cache *)&lib_manager_dram,
141+
sizeof(lib_manager_dram));
142+
dcache_writeback_region((__sparse_force void __sparse_cache *)lib_manager_dram.ctx,
143+
buf_size);
144+
145+
return 0;
146+
}
147+
148+
int llext_manager_restore_from_dram(void)
149+
{
150+
lib_manager_init();
151+
152+
struct ext_library *_ext_lib = ext_lib_get();
153+
unsigned int i, j, k, n_mod, n_llext, n_sect, n_sym;
154+
155+
if (!lib_manager_dram.n_llext || !lib_manager_dram.ctx) {
156+
tr_dbg(&lib_manager_tr, "No modules saved");
157+
dcache_writeback_region((__sparse_force void __sparse_cache *)&lib_manager_dram,
158+
sizeof(lib_manager_dram));
159+
return 0;
160+
}
161+
162+
/* arrays of pointers for llext_restore() */
163+
void **ptr_array = rmalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
164+
sizeof(*ptr_array) * lib_manager_dram.n_llext * 2);
165+
166+
if (!ptr_array)
167+
return -ENOMEM;
168+
169+
struct llext_loader **ldr = (struct llext_loader **)ptr_array;
170+
struct llext **llext = (struct llext **)(ptr_array + lib_manager_dram.n_llext);
171+
172+
*_ext_lib = lib_manager_dram.ext_lib;
173+
174+
for (i = 0, j = 0, n_mod = 0, n_llext = 0, n_sect = 0, n_sym = 0;
175+
i < ARRAY_SIZE(_ext_lib->desc); i++) {
176+
if (!lib_manager_dram.ext_lib.desc[i]) {
177+
_ext_lib->desc[i] = NULL;
178+
continue;
179+
}
180+
181+
/* panics on failure */
182+
struct lib_manager_mod_ctx *ctx = rmalloc(SOF_MEM_ZONE_SYS, SOF_MEM_FLAG_COHERENT,
183+
SOF_MEM_CAPS_RAM, sizeof(*ctx));
184+
185+
*ctx = lib_manager_dram.ctx[j++];
186+
187+
struct lib_manager_module *mod = rmalloc(SOF_MEM_ZONE_RUNTIME_SHARED,
188+
SOF_MEM_FLAG_COHERENT, SOF_MEM_CAPS_RAM,
189+
ctx->n_mod * sizeof(ctx->mod[0]));
190+
191+
if (!mod) {
192+
tr_err(&lib_manager_tr, "module allocation failure");
193+
goto nomem;
194+
}
195+
tr_dbg(&lib_manager_tr, "%u modules alloc %p base %p copy %#zx",
196+
ctx->n_mod, (void *)mod, ctx->base_addr, ctx->n_mod * sizeof(ctx->mod[0]));
197+
198+
memcpy(mod, lib_manager_dram.mod + n_mod, sizeof(mod[0]) * ctx->n_mod);
199+
n_mod += ctx->n_mod;
200+
ctx->mod = mod;
201+
202+
for (k = 0; k < ctx->n_mod; k++) {
203+
if (!mod[k].llext)
204+
continue;
205+
206+
struct llext_buf_loader *bldr = rmalloc(SOF_MEM_ZONE_RUNTIME_SHARED,
207+
0, SOF_MEM_CAPS_RAM, sizeof(*bldr));
208+
209+
if (!bldr) {
210+
tr_err(&lib_manager_tr, "loader allocation failure");
211+
goto nomem;
212+
}
213+
214+
llext[n_llext] = lib_manager_dram.llext + n_llext;
215+
216+
*bldr = lib_manager_dram.bldr[n_llext];
217+
218+
bldr->loader.sect_map = lib_manager_dram.sect + n_sect;
219+
220+
n_sect += llext[n_llext]->sect_cnt;
221+
if (llext[n_llext]->exp_tab.sym_cnt) {
222+
tr_dbg(&lib_manager_tr, "got %u exported symbols",
223+
llext[n_llext]->exp_tab.sym_cnt);
224+
225+
if (llext[n_llext]->exp_tab.syms != lib_manager_dram.sym + n_sym) {
226+
tr_err(&lib_manager_tr,
227+
"bug detected! pointer mismatch %p vs. %p",
228+
(void *)llext[n_llext]->exp_tab.syms,
229+
(void *)(lib_manager_dram.sym + n_sym));
230+
goto nomem;
231+
}
232+
233+
n_sym += llext[n_llext]->exp_tab.sym_cnt;
234+
}
235+
236+
mod[k].ebl = bldr;
237+
238+
ldr[n_llext++] = &bldr->loader;
239+
}
240+
241+
_ext_lib->desc[i] = ctx;
242+
}
243+
244+
int ret = llext_restore(llext, ldr, lib_manager_dram.n_llext);
245+
246+
if (ret < 0) {
247+
tr_err(&lib_manager_tr, "Zephyr failed to restore: %d", ret);
248+
goto nomem;
249+
}
250+
251+
/* Rewrite to correct LLEXT pointers, created by Zephyr */
252+
for (i = 0, n_llext = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) {
253+
struct lib_manager_mod_ctx *ctx = _ext_lib->desc[i];
254+
255+
if (!ctx)
256+
continue;
257+
258+
struct lib_manager_module *mod = ctx->mod;
259+
260+
for (k = 0; k < ctx->n_mod; k++) {
261+
if (mod[k].llext)
262+
mod[k].llext = llext[n_llext++];
263+
}
264+
}
265+
266+
tr_info(&lib_manager_tr, "restored %u modules with %u LLEXT", n_mod, n_llext);
267+
268+
rfree(lib_manager_dram.ctx);
269+
lib_manager_dram.ctx = NULL;
270+
lib_manager_dram.sect = NULL;
271+
lib_manager_dram.llext = NULL;
272+
lib_manager_dram.bldr = NULL;
273+
lib_manager_dram.sym = NULL;
274+
rfree(ldr);
275+
276+
lib_manager_dram.n_llext = 0;
277+
278+
return 0;
279+
280+
nomem:
281+
tr_err(&lib_manager_tr, "Restore failed");
282+
for (i = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) {
283+
struct lib_manager_mod_ctx *ctx = _ext_lib->desc[i];
284+
285+
if (!ctx)
286+
continue;
287+
288+
struct lib_manager_module *mod = ctx->mod;
289+
290+
if (!mod)
291+
continue;
292+
293+
for (k = 0; k < ctx->n_mod; k++) {
294+
if (mod[k].llext)
295+
llext_unload(&mod[k].llext);
296+
297+
if (mod[k].ebl)
298+
rfree(mod[k].ebl);
299+
}
300+
301+
rfree(mod);
302+
rfree(ctx);
303+
}
304+
305+
/* at least create a sane empty lib-manager context */
306+
memset(_ext_lib->desc, 0, sizeof(_ext_lib->desc));
307+
308+
rfree(ldr);
309+
310+
return -ENOMEM;
311+
}

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)