Skip to content

Commit 5a9394a

Browse files
alexivanov-googlenashif
authored andcommitted
llext: add dynamic heap allocation support
Some applications require loading extensions into the memory which does not exist during the boot time and cannot be allocated statically. Make the application responsible for LLEXT heap allocation. Do not allocate LLEXT heap statically. Signed-off-by: Alex Ivanov <alexivanov@google.com>
1 parent a76ed22 commit 5a9394a

File tree

7 files changed

+148
-3
lines changed

7 files changed

+148
-3
lines changed

doc/services/llext/config.rst

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,29 @@ The following Kconfig options are available for the LLEXT subsystem:
88
Heap size
99
----------
1010

11-
The LLEXT subsystem needs a static heap to be allocated for extension related
12-
data. The following option controls this allocation.
11+
The LLEXT subsystem needs a heap to be allocated for extension related data.
12+
The following option controls this allocation, when allocating a static heap.
1313

1414
:kconfig:option:`CONFIG_LLEXT_HEAP_SIZE`
1515

1616
Size of the LLEXT heap in kilobytes.
1717

18+
Alternatively the application can configure a dynamic heap using the following
19+
option.
20+
21+
:kconfig:option:`CONFIG_LLEXT_HEAP_DYNAMIC`
22+
23+
Some applications require loading extensions into the memory which does
24+
not exist during the boot time and cannot be allocated statically. Make
25+
the application responsible for LLEXT heap allocation. Do not allocate
26+
LLEXT heap statically.
27+
28+
Application must call :c:func:`llext_heap_init` in order to assign a
29+
buffer to be used as the LLEXT heap, otherwise LLEXT modules will not
30+
load. When the application does not need LLEXT functionality any more,
31+
it should call :c:func:`llext_heap_uninit` which releases control of
32+
the buffer back to the application.
33+
1834
.. note::
1935

2036
When :ref:`user mode <usermode_api>` is enabled, the heap size must be

include/zephyr/llext/llext.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,27 @@ void arch_elf_relocate_local(struct llext_loader *loader, struct llext *ext, con
427427
void arch_elf_relocate_global(struct llext_loader *loader, struct llext *ext, const elf_rela_t *rel,
428428
const elf_sym_t *sym, uint8_t *rel_addr, const void *link_addr);
429429

430+
/**
431+
* @brief Initialize LLEXT heap dynamically
432+
*
433+
* Use the provided memory block as the LLEXT heap at runtime.
434+
*
435+
* @param mem Pointer to memory.
436+
* @param bytes Size of memory region, in bytes
437+
*
438+
* @returns 0 on success, or a negative error code.
439+
* @retval -ENOSYS Option @kconfig{CONFIG_LLEXT_HEAP_DYNAMIC} is not enabled or supported
440+
*/
441+
int llext_heap_init(void *mem, size_t bytes);
442+
443+
/**
444+
* @brief Mark LLEXT heap as uninitialized.
445+
*
446+
* @returns 0 on success, or a negative error code.
447+
* @retval -ENOSYS Option @kconfig{CONFIG_LLEXT_HEAP_DYNAMIC} is not enabled or supported
448+
* @retval -EBUSY On heap not empty
449+
*/
450+
int llext_heap_uninit(void);
430451
/**
431452
* @}
432453
*/

subsys/llext/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,21 @@ config LLEXT_TYPE_ELF_SHAREDLIB
4444

4545
endchoice
4646

47+
config LLEXT_HEAP_DYNAMIC
48+
bool "Do not allocate static LLEXT heap"
49+
help
50+
Some applications require loading extensions into the memory which does not
51+
exist during the boot time and cannot be allocated statically. Make the application
52+
responsible for LLEXT heap allocation. Do not allocate LLEXT heap statically.
53+
54+
Application must call llext_heap_init() in order to assign a buffer to be used
55+
as the LLEXT heap, otherwise LLEXT modules will not load. When the application
56+
does not need LLEXT functionality any more, it should call llext_heap_uninit(),
57+
which releases control of the buffer back to the application.
58+
4759
config LLEXT_HEAP_SIZE
4860
int "llext heap memory size in kilobytes"
61+
depends on !LLEXT_HEAP_DYNAMIC
4962
default 8
5063
help
5164
Heap size in kilobytes available to llext for dynamic allocation

subsys/llext/llext_mem.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL);
2727
#define LLEXT_PAGE_SIZE 32
2828
#endif
2929

30+
#ifdef CONFIG_LLEXT_HEAP_DYNAMIC
31+
struct k_heap llext_heap;
32+
bool llext_heap_inited;
33+
#else
3034
K_HEAP_DEFINE(llext_heap, CONFIG_LLEXT_HEAP_SIZE * 1024);
35+
#endif
3136

3237
/*
3338
* Initialize the memory partition associated with the specified memory region
@@ -319,3 +324,40 @@ int llext_add_domain(struct llext *ext, struct k_mem_domain *domain)
319324
return -ENOSYS;
320325
#endif
321326
}
327+
328+
int llext_heap_init(void *mem, size_t bytes)
329+
{
330+
#ifdef CONFIG_LLEXT_HEAP_DYNAMIC
331+
if (llext_heap_inited) {
332+
return -EEXIST;
333+
}
334+
k_heap_init(&llext_heap, mem, bytes);
335+
llext_heap_inited = true;
336+
return 0;
337+
#else
338+
return -ENOSYS;
339+
#endif
340+
}
341+
342+
#ifdef CONFIG_LLEXT_HEAP_DYNAMIC
343+
static int llext_loaded(struct llext *ext, void *arg)
344+
{
345+
return 1;
346+
}
347+
#endif
348+
349+
int llext_heap_uninit(void)
350+
{
351+
#ifdef CONFIG_LLEXT_HEAP_DYNAMIC
352+
if (!llext_heap_inited) {
353+
return -EEXIST;
354+
}
355+
if (llext_iterate(llext_loaded, NULL)) {
356+
return -EBUSY;
357+
}
358+
llext_heap_inited = false;
359+
return 0;
360+
#else
361+
return -ENOSYS;
362+
#endif
363+
}

subsys/llext/llext_priv.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,44 @@ int llext_copy_regions(struct llext_loader *ldr, struct llext *ext,
2222
void llext_free_regions(struct llext *ext);
2323
void llext_adjust_mmu_permissions(struct llext *ext);
2424

25+
static inline bool llext_heap_is_inited(void)
26+
{
27+
#ifdef CONFIG_LLEXT_HEAP_DYNAMIC
28+
extern bool llext_heap_inited;
29+
30+
return llext_heap_inited;
31+
#else
32+
return true;
33+
#endif
34+
}
35+
2536
static inline void *llext_alloc(size_t bytes)
2637
{
2738
extern struct k_heap llext_heap;
2839

40+
if (!llext_heap_is_inited()) {
41+
return NULL;
42+
}
2943
return k_heap_alloc(&llext_heap, bytes, K_NO_WAIT);
3044
}
3145

3246
static inline void *llext_aligned_alloc(size_t align, size_t bytes)
3347
{
3448
extern struct k_heap llext_heap;
3549

50+
if (!llext_heap_is_inited()) {
51+
return NULL;
52+
}
3653
return k_heap_aligned_alloc(&llext_heap, align, bytes, K_NO_WAIT);
3754
}
3855

3956
static inline void llext_free(void *ptr)
4057
{
4158
extern struct k_heap llext_heap;
4259

60+
if (!llext_heap_is_inited()) {
61+
return;
62+
}
4363
k_heap_free(&llext_heap, ptr);
4464
}
4565

tests/subsys/llext/src/test_llext.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,4 +658,29 @@ ZTEST(llext, test_ext_syscall_fail)
658658
zassert_is_null(esf_fn, "est_fn should be NULL");
659659
}
660660

661-
ZTEST_SUITE(llext, NULL, NULL, NULL, NULL, NULL);
661+
#ifdef CONFIG_LLEXT_HEAP_DYNAMIC
662+
#define TEST_LLEXT_HEAP_DYNAMIC_SIZE KB(64)
663+
static uint8_t llext_heap_data[TEST_LLEXT_HEAP_DYNAMIC_SIZE];
664+
#endif
665+
666+
static void *ztest_suite_setup(void)
667+
{
668+
#ifdef CONFIG_LLEXT_HEAP_DYNAMIC
669+
/* Test runtime allocation of the LLEXT loader heap */
670+
zassert_ok(llext_heap_init(llext_heap_data, sizeof(llext_heap_data)));
671+
LOG_INF("Allocated LLEXT dynamic heap of size %uKB\n",
672+
(unsigned int)(sizeof(llext_heap_data)/KB(1)));
673+
#endif
674+
return NULL;
675+
}
676+
677+
static void ztest_suite_teardown(void *data)
678+
{
679+
ARG_UNUSED(data);
680+
681+
#ifdef CONFIG_LLEXT_HEAP_DYNAMIC
682+
zassert_ok(llext_heap_uninit());
683+
#endif
684+
}
685+
686+
ZTEST_SUITE(llext, NULL, ztest_suite_setup, NULL, NULL, ztest_suite_teardown);

tests/subsys/llext/testcase.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,11 @@ tests:
151151
- CONFIG_LLEXT_STORAGE_WRITABLE=n
152152
- CONFIG_LLEXT_EXPORT_DEV_IDS_BY_HASH=y
153153
- CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y
154+
155+
# Test dynamic heap allocation
156+
llext.dynamic_heap:
157+
arch_allow: arm
158+
filter: not CONFIG_MPU and not CONFIG_MMU
159+
extra_conf_files: ['no_mem_protection.conf']
160+
extra_configs:
161+
- CONFIG_LLEXT_HEAP_DYNAMIC=y

0 commit comments

Comments
 (0)