Skip to content

Commit db9e5ec

Browse files
Nicolas Pitrenashif
Nicolas Pitre
authored andcommitted
sample: demand_paging: add a demo about ondemand section usage
This sample demonstrates how demand paging can be leveraged to deal with firmware bigger than the available RAM if XIP is not possible. Select code can be tagged to be loaded into memory on demand, and also be automatically evicted for more code to be executed when memory is exhausted. Signed-off-by: Nicolas Pitre <[email protected]>
1 parent 7e847ec commit db9e5ec

File tree

5 files changed

+169
-0
lines changed

5 files changed

+169
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(demand_paging)
7+
8+
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/kernel/include)
9+
10+
target_sources(app PRIVATE src/main.c)
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
.. zephyr:code-sample:: demand-paging
2+
:name: Demand paging
3+
:relevant-api: mem-demand-paging
4+
5+
Leverage demand paging to deal with code bigger than available RAM.
6+
7+
Overview
8+
********
9+
10+
This sample demonstrates how demand paging can be leveraged to deal with
11+
firmware bigger than the available RAM if XIP is not possible. Select code
12+
can be tagged to be loaded into memory on demand, and also be automatically
13+
evicted for more code to be executed when memory is exhausted.
14+
15+
Requirements
16+
************
17+
18+
This demo requires the presence of a supported hardware MMU and a backing
19+
store implementation with access to the compiled Zephyr binary.
20+
For demonstration purposes, the ondemand_semihost backing store is used on
21+
a QEMU ARM64 target with a hardcoded small RAM configuration.
22+
23+
Building and Running
24+
********************
25+
26+
This application can be built and executed on QEMU as follows:
27+
28+
.. zephyr-app-commands::
29+
:zephyr-app: samples/subsys/demand_paging
30+
:host-os: unix
31+
:board: qemu_cortex_a53
32+
:goals: run
33+
:compact:
34+
35+
Sample Output
36+
=============
37+
38+
.. code-block:: console
39+
40+
*** Booting Zephyr OS build v3.7.0-2108-g5975c3785356 ***
41+
Calling huge body of code that doesn't fit in memory
42+
free memory pages: from 37 to 0, 987 page faults
43+
Calling it a second time
44+
free memory pages: from 0 to 0, 987 page faults
45+
Done.
46+
47+
Exit QEMU by pressing :kbd:`CTRL+A` :kbd:`x`.
48+
49+
To actually view the underlying demand paging subsystem at work, debug
50+
prints can be enabled using :kconfig:option:`CONFIG_LOG` and
51+
:kconfig:option:`CONFIG_KERNEL_LOG_LEVEL_DBG` in the config file.

samples/subsys/demand_paging/prj.conf

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CONFIG_SRAM_SIZE=256
2+
CONFIG_DEMAND_PAGING=y
3+
CONFIG_DEMAND_PAGING_ALLOW_IRQ=y
4+
CONFIG_DEMAND_PAGING_STATS=y
5+
CONFIG_DEMAND_MAPPING=y
6+
CONFIG_LINKER_USE_ONDEMAND_SECTION=y
7+
CONFIG_SEMIHOST=y
8+
CONFIG_BACKING_STORE_ONDEMAND_SEMIHOST=y
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
sample:
2+
description: On-Demand paging of firmware larger than available memory
3+
name: demand-paging
4+
common:
5+
tags:
6+
- kernel
7+
- mmu
8+
- demand_paging
9+
integration_platforms:
10+
- qemu_cortex_a53
11+
harness: console
12+
harness_config:
13+
type: multi_line
14+
ordered: true
15+
regex:
16+
- "Calling huge body of code that doesn't fit in memory"
17+
- "free memory pages: from (.*) to 0, (.*) page faults"
18+
- "Calling it a second time"
19+
- "free memory pages: from 0 to 0, (.*) page faults"
20+
- "Done."
21+
tests:
22+
sample.demand_paging.ondemand_section:
23+
platform_allow:
24+
- qemu_cortex_a53
25+
- qemu_cortex_a53/qemu_cortex_a53/smp
+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2024 BayLibre SAS
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/linker/sections.h>
9+
10+
#include <mmu.h> /* for k_mem_num_pagefaults_get() */
11+
12+
/*
13+
* We want to artificially create a huge body of code hence the volatile
14+
* to prevent the compiler from optimizing everything down.
15+
*/
16+
volatile long foo_val1;
17+
volatile long foo_val2;
18+
19+
/*
20+
* This function is tagged with __ondemand_func to be placed in a special
21+
* "ondemand" segment by the linker. This means it is not loaded into memory
22+
* at boot time but rather page-by-page on demand when actually executed.
23+
* It is also subject to being evicted when memory reclamation occurs.
24+
*/
25+
static void __ondemand_func huge_evictable_function(void)
26+
{
27+
foo_val1 = 13131313;
28+
foo_val2 = 45454545;
29+
30+
#define CODE \
31+
foo_val1 *= foo_val2; \
32+
foo_val2 += 1234567; \
33+
foo_val1 -= 9876543210; \
34+
__asm__ __volatile__ (".rep 1000; nop; .endr");
35+
36+
#define REPEAT_10(x) x x x x x x x x x x
37+
#define REPEAT_1000(x) REPEAT_10(REPEAT_10(REPEAT_10(x)))
38+
39+
REPEAT_1000(CODE)
40+
41+
#undef REPEAT_1000
42+
#undef REPEAT_10
43+
#undef CODE
44+
}
45+
46+
int main(void)
47+
{
48+
size_t free_pages_before, free_pages_after;
49+
unsigned long faults_before, faults_after;
50+
51+
printk("Calling huge body of code that doesn't fit in memory\n");
52+
free_pages_before = k_mem_free_get() / CONFIG_MMU_PAGE_SIZE;
53+
faults_before = k_mem_num_pagefaults_get();
54+
55+
huge_evictable_function();
56+
57+
free_pages_after = k_mem_free_get() / CONFIG_MMU_PAGE_SIZE;
58+
faults_after = k_mem_num_pagefaults_get();
59+
printk("free memory pages: from %zd to %zd, %ld page faults\n",
60+
free_pages_before, free_pages_after, faults_after - faults_before);
61+
62+
printk("Calling it a second time\n");
63+
free_pages_before = k_mem_free_get() / CONFIG_MMU_PAGE_SIZE;
64+
faults_before = k_mem_num_pagefaults_get();
65+
66+
huge_evictable_function();
67+
68+
free_pages_after = k_mem_free_get() / CONFIG_MMU_PAGE_SIZE;
69+
faults_after = k_mem_num_pagefaults_get();
70+
printk("free memory pages: from %zd to %zd, %ld page faults\n",
71+
free_pages_before, free_pages_after, faults_after - faults_before);
72+
73+
printk("Done.\n");
74+
return 0;
75+
}

0 commit comments

Comments
 (0)