Skip to content

Commit a511a61

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 adabf5d commit a511a61

File tree

7 files changed

+381
-1
lines changed

7 files changed

+381
-1
lines changed

src/include/ipc4/notification.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ enum sof_ipc4_resource_type {
114114
(((SOF_IPC4_NOTIFY_FW_READY) << (SOF_IPC4_GLB_NOTIFY_TYPE_SHIFT)) |\
115115
((SOF_IPC4_GLB_NOTIFICATION) << (SOF_IPC4_GLB_NOTIFY_MSG_TYPE_SHIFT)))
116116

117+
#define SOF_IPC4_FW_READY_LIB_RESTORED BIT(15)
118+
117119
#define SOF_IPC4_NOTIF_HEADER(notif_type) \
118120
((notif_type) << (SOF_IPC4_GLB_NOTIFY_TYPE_SHIFT) | \
119121
((SOF_IPC4_GLB_NOTIFICATION) << (SOF_IPC4_GLB_NOTIFY_MSG_TYPE_SHIFT)))

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() -ENOSYS
47+
#endif
48+
4149
#endif

src/ipc/ipc4/handler.c

Lines changed: 12 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,17 @@ __cold static int ipc4_module_process_dx(struct ipc4_message_request *ipc4)
14331434
return IPC4_BUSY;
14341435
}
14351436

1437+
#if !CONFIG_ADSP_IMR_CONTEXT_SAVE
1438+
ret = llext_manager_store_to_dram();
1439+
if (ret < 0)
1440+
ipc_cmd_err(&ipc_tr, "Error %d saving LLEXT context. Resume might fail.",
1441+
ret);
1442+
1443+
#if CONFIG_L3_HEAP
1444+
l3_heap_save();
1445+
#endif
1446+
#endif
1447+
14361448
#if defined(CONFIG_PM)
14371449
ipc_get()->task_mask |= IPC_TASK_POWERDOWN;
14381450
#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: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
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+
/*
28+
* This holds the complete LLEXT manager context in DRAM over DSP shut down to
29+
* be restored during the next boot
30+
*/
31+
__imrdata static struct lib_manager_dram_storage lib_manager_dram;
32+
33+
/* Store LLEXT manager context in DRAM to be restored during the next boot. */
34+
int llext_manager_store_to_dram(void)
35+
{
36+
struct ext_library *_ext_lib = ext_lib_get();
37+
unsigned int i, j, k, l, n_lib, n_mod, n_llext, n_sect, n_sym;
38+
size_t buf_size;
39+
40+
if (lib_manager_dram.n_llext) {
41+
tr_err(&lib_manager_tr, "context already saved");
42+
return 0;
43+
}
44+
45+
/*
46+
* Count libraries, modules, instantiated extensions, sections and exported
47+
* symbols in them. Allocate a buffer of required size.
48+
*/
49+
lib_manager_dram.ext_lib = *_ext_lib;
50+
for (i = 0, n_lib = 0, n_mod = 0, n_llext = 0, n_sect = 0, n_sym = 0;
51+
i < ARRAY_SIZE(_ext_lib->desc); i++)
52+
if (_ext_lib->desc[i]) {
53+
n_lib++;
54+
n_mod += _ext_lib->desc[i]->n_mod;
55+
for (k = 0; k < _ext_lib->desc[i]->n_mod; k++)
56+
if (_ext_lib->desc[i]->mod[k].ebl) {
57+
n_llext++;
58+
n_sect += _ext_lib->desc[i]->mod[k].llext->sect_cnt;
59+
n_sym += _ext_lib->desc[i]->mod[k].llext->exp_tab.sym_cnt;
60+
tr_dbg(&lib_manager_tr, "add %u exported syms",
61+
_ext_lib->desc[i]->mod[k].llext->exp_tab.sym_cnt);
62+
}
63+
}
64+
65+
buf_size = sizeof(lib_manager_dram.ctx[0]) * n_lib +
66+
sizeof(lib_manager_dram.mod[0]) * n_mod +
67+
sizeof(lib_manager_dram.sect[0]) * n_sect +
68+
sizeof(lib_manager_dram.sym[0]) * n_sym +
69+
(sizeof(lib_manager_dram.llext[0]) + sizeof(lib_manager_dram.bldr[0])) * n_llext;
70+
71+
lib_manager_dram.ctx = rmalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_L3,
72+
buf_size);
73+
if (!lib_manager_dram.ctx)
74+
return -ENOMEM;
75+
76+
/* Save pointers to buffer parts, holding parts of the context */
77+
lib_manager_dram.mod = (struct lib_manager_module *)(lib_manager_dram.ctx + n_lib);
78+
lib_manager_dram.sect = (struct llext_elf_sect_map *)(lib_manager_dram.mod + n_mod);
79+
lib_manager_dram.llext = (struct llext *)(lib_manager_dram.sect + n_sect);
80+
lib_manager_dram.bldr = (struct llext_buf_loader *)(lib_manager_dram.llext + n_llext);
81+
lib_manager_dram.sym = (struct llext_symbol *)(lib_manager_dram.bldr + n_llext);
82+
83+
tr_dbg(&lib_manager_tr, "backup %u libs of %u modules with %u LLEXT with %u sections",
84+
n_lib, n_mod, n_llext, n_sect);
85+
86+
tr_dbg(&lib_manager_tr, "backup %p to %p, mod %p, loader %p",
87+
lib_manager_dram.ctx, (void *)((uint8_t *)lib_manager_dram.ctx + buf_size),
88+
lib_manager_dram.mod, lib_manager_dram.bldr);
89+
90+
/* Walk all libraries */
91+
for (i = 0, j = 0, l = 0, n_mod = 0, n_sect = 0, n_sym = 0;
92+
i < ARRAY_SIZE(_ext_lib->desc); i++) {
93+
if (!_ext_lib->desc[i])
94+
continue;
95+
96+
struct lib_manager_module *mod = _ext_lib->desc[i]->mod;
97+
98+
/* Copy all modules in each library */
99+
lib_manager_dram.ctx[j++] = *_ext_lib->desc[i];
100+
memcpy(lib_manager_dram.mod + n_mod, mod,
101+
sizeof(lib_manager_dram.mod[0]) * _ext_lib->desc[i]->n_mod);
102+
tr_dbg(&lib_manager_tr, "lib %u base %p", j - 1,
103+
lib_manager_dram.ctx[j - 1].base_addr);
104+
n_mod += _ext_lib->desc[i]->n_mod;
105+
106+
/*
107+
* Copy instantiated extensions. Note that only modules, that
108+
* were used, have their LLEXT context instantiated.
109+
*/
110+
for (k = 0; k < _ext_lib->desc[i]->n_mod; k++) {
111+
if (!mod[k].llext)
112+
continue;
113+
114+
tr_dbg(&lib_manager_tr, "mod %u of %u sections", k,
115+
mod[k].llext->sect_cnt);
116+
117+
/* Copy the extension and the loader */
118+
lib_manager_dram.llext[l] = *mod[k].llext;
119+
lib_manager_dram.bldr[l] = *mod[k].ebl;
120+
121+
/* Copy the section map */
122+
memcpy(lib_manager_dram.sect + n_sect, mod[k].ebl->loader.sect_map,
123+
mod[k].llext->sect_cnt * sizeof(lib_manager_dram.sect[0]));
124+
n_sect += mod[k].llext->sect_cnt;
125+
126+
/* Copy exported symbols */
127+
if (mod[k].llext->exp_tab.sym_cnt) {
128+
memcpy(lib_manager_dram.sym + n_sym, mod[k].llext->exp_tab.syms,
129+
mod[k].llext->exp_tab.sym_cnt *
130+
sizeof(lib_manager_dram.sym[0]));
131+
lib_manager_dram.llext[l].exp_tab.syms = lib_manager_dram.sym +
132+
n_sym;
133+
n_sym += mod[k].llext->exp_tab.sym_cnt;
134+
}
135+
136+
l++;
137+
}
138+
}
139+
140+
/* Also flatten dependency lists */
141+
int ret = llext_relink_dependency(lib_manager_dram.llext, n_llext);
142+
143+
if (ret < 0) {
144+
tr_err(&lib_manager_tr, "Inconsistent dependencies!");
145+
return ret;
146+
}
147+
148+
lib_manager_dram.n_llext = n_llext;
149+
/* Make sure, that the data is actually in the DRAM, not just in data cache */
150+
dcache_writeback_region((__sparse_force void __sparse_cache *)&lib_manager_dram,
151+
sizeof(lib_manager_dram));
152+
dcache_writeback_region((__sparse_force void __sparse_cache *)lib_manager_dram.ctx,
153+
buf_size);
154+
155+
return 0;
156+
}
157+
158+
int llext_manager_restore_from_dram(void)
159+
{
160+
lib_manager_init();
161+
162+
struct ext_library *_ext_lib = ext_lib_get();
163+
unsigned int i, j, k, n_mod, n_llext, n_sect, n_sym;
164+
165+
if (!lib_manager_dram.n_llext || !lib_manager_dram.ctx) {
166+
tr_dbg(&lib_manager_tr, "No modules saved");
167+
dcache_writeback_region((__sparse_force void __sparse_cache *)&lib_manager_dram,
168+
sizeof(lib_manager_dram));
169+
return 0;
170+
}
171+
172+
/* arrays of pointers for llext_restore() */
173+
void **ptr_array = rmalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
174+
sizeof(*ptr_array) * lib_manager_dram.n_llext * 2);
175+
176+
if (!ptr_array)
177+
return -ENOMEM;
178+
179+
struct llext_loader **ldr = (struct llext_loader **)ptr_array;
180+
struct llext **llext = (struct llext **)(ptr_array + lib_manager_dram.n_llext);
181+
182+
*_ext_lib = lib_manager_dram.ext_lib;
183+
184+
/* The external loop walks all the libraries */
185+
for (i = 0, j = 0, n_mod = 0, n_llext = 0, n_sect = 0, n_sym = 0;
186+
i < ARRAY_SIZE(_ext_lib->desc); i++) {
187+
if (!lib_manager_dram.ext_lib.desc[i]) {
188+
_ext_lib->desc[i] = NULL;
189+
continue;
190+
}
191+
192+
/* Panics on failure - use the same zone as during the first boot */
193+
struct lib_manager_mod_ctx *ctx = rmalloc(SOF_MEM_ZONE_SYS, SOF_MEM_FLAG_COHERENT,
194+
SOF_MEM_CAPS_RAM, sizeof(*ctx));
195+
196+
/* Restore the library context */
197+
*ctx = lib_manager_dram.ctx[j++];
198+
199+
/* Allocate and restore all the modules in the library */
200+
struct lib_manager_module *mod = rmalloc(SOF_MEM_ZONE_RUNTIME_SHARED,
201+
SOF_MEM_FLAG_COHERENT, SOF_MEM_CAPS_RAM,
202+
ctx->n_mod * sizeof(ctx->mod[0]));
203+
204+
if (!mod) {
205+
tr_err(&lib_manager_tr, "module allocation failure");
206+
goto nomem;
207+
}
208+
tr_dbg(&lib_manager_tr, "%u modules alloc %p base %p copy %#zx",
209+
ctx->n_mod, (void *)mod, ctx->base_addr, ctx->n_mod * sizeof(ctx->mod[0]));
210+
211+
memcpy(mod, lib_manager_dram.mod + n_mod, sizeof(mod[0]) * ctx->n_mod);
212+
n_mod += ctx->n_mod;
213+
ctx->mod = mod;
214+
215+
/* Second level: enumerate modules in each library */
216+
for (k = 0; k < ctx->n_mod; k++) {
217+
if (!mod[k].llext)
218+
/* Not instantiated - nothing to restore */
219+
continue;
220+
221+
/* Loaders are supplied by the caller */
222+
struct llext_buf_loader *bldr = rmalloc(SOF_MEM_ZONE_RUNTIME_SHARED,
223+
0, SOF_MEM_CAPS_RAM, sizeof(*bldr));
224+
225+
if (!bldr) {
226+
tr_err(&lib_manager_tr, "loader allocation failure");
227+
goto nomem;
228+
}
229+
230+
/* Extensions have to be restored by Zephyr, collect pointers first */
231+
llext[n_llext] = lib_manager_dram.llext + n_llext;
232+
233+
*bldr = lib_manager_dram.bldr[n_llext];
234+
235+
bldr->loader.sect_map = lib_manager_dram.sect + n_sect;
236+
237+
n_sect += llext[n_llext]->sect_cnt;
238+
if (llext[n_llext]->exp_tab.sym_cnt) {
239+
/*
240+
* Just a check, that we're restoring exported
241+
* symbols correctly
242+
*/
243+
tr_dbg(&lib_manager_tr, "got %u exported symbols",
244+
llext[n_llext]->exp_tab.sym_cnt);
245+
246+
if (llext[n_llext]->exp_tab.syms != lib_manager_dram.sym + n_sym) {
247+
tr_err(&lib_manager_tr,
248+
"bug detected! pointer mismatch %p vs. %p",
249+
(void *)llext[n_llext]->exp_tab.syms,
250+
(void *)(lib_manager_dram.sym + n_sym));
251+
goto nomem;
252+
}
253+
254+
n_sym += llext[n_llext]->exp_tab.sym_cnt;
255+
}
256+
257+
mod[k].ebl = bldr;
258+
259+
ldr[n_llext++] = &bldr->loader;
260+
}
261+
262+
_ext_lib->desc[i] = ctx;
263+
}
264+
265+
/* Let Zephyr restore extensions and its own internal bookkeeping */
266+
int ret = llext_restore(llext, ldr, lib_manager_dram.n_llext);
267+
268+
if (ret < 0) {
269+
tr_err(&lib_manager_tr, "Zephyr failed to restore: %d", ret);
270+
goto nomem;
271+
}
272+
273+
/* Rewrite to correct LLEXT pointers, created by Zephyr */
274+
for (i = 0, n_llext = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) {
275+
struct lib_manager_mod_ctx *ctx = _ext_lib->desc[i];
276+
277+
if (!ctx)
278+
continue;
279+
280+
struct lib_manager_module *mod = ctx->mod;
281+
282+
for (k = 0; k < ctx->n_mod; k++) {
283+
if (mod[k].llext)
284+
mod[k].llext = llext[n_llext++];
285+
}
286+
}
287+
288+
tr_info(&lib_manager_tr, "restored %u modules with %u LLEXT", n_mod, n_llext);
289+
290+
rfree(lib_manager_dram.ctx);
291+
lib_manager_dram.ctx = NULL;
292+
lib_manager_dram.sect = NULL;
293+
lib_manager_dram.llext = NULL;
294+
lib_manager_dram.bldr = NULL;
295+
lib_manager_dram.sym = NULL;
296+
rfree(ldr);
297+
298+
lib_manager_dram.n_llext = 0;
299+
300+
return 0;
301+
302+
nomem:
303+
tr_err(&lib_manager_tr, "Restore failed");
304+
for (i = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) {
305+
struct lib_manager_mod_ctx *ctx = _ext_lib->desc[i];
306+
307+
if (!ctx)
308+
continue;
309+
310+
struct lib_manager_module *mod = ctx->mod;
311+
312+
if (!mod)
313+
continue;
314+
315+
for (k = 0; k < ctx->n_mod; k++) {
316+
if (mod[k].llext)
317+
llext_unload(&mod[k].llext);
318+
319+
if (mod[k].ebl)
320+
rfree(mod[k].ebl);
321+
}
322+
323+
rfree(mod);
324+
rfree(ctx);
325+
}
326+
327+
/* at least create a sane empty lib-manager context */
328+
memset(_ext_lib->desc, 0, sizeof(_ext_lib->desc));
329+
330+
rfree(ldr);
331+
332+
return -ENOMEM;
333+
}

src/platform/intel/ace/platform.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ __cold int platform_boot_complete(uint32_t boot_message)
6767

6868
/* get any IPC specific boot message and optional data */
6969
ipc_boot_complete_msg(&header, 0);
70+
header.pri |= boot_message;
7071

7172
struct ipc_msg msg = {
7273
.header = header.pri,

0 commit comments

Comments
 (0)