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 <npitre@baylibre.com>
This commit is contained in:
parent
7e847eca25
commit
db9e5ec6f8
5 changed files with 169 additions and 0 deletions
10
samples/subsys/demand_paging/CMakeLists.txt
Normal file
10
samples/subsys/demand_paging/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(demand_paging)
|
||||||
|
|
||||||
|
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/kernel/include)
|
||||||
|
|
||||||
|
target_sources(app PRIVATE src/main.c)
|
||||||
51
samples/subsys/demand_paging/README.rst
Normal file
51
samples/subsys/demand_paging/README.rst
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
.. zephyr:code-sample:: demand-paging
|
||||||
|
:name: Demand paging
|
||||||
|
:relevant-api: mem-demand-paging
|
||||||
|
|
||||||
|
Leverage demand paging to deal with code bigger than available RAM.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
********
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
************
|
||||||
|
|
||||||
|
This demo requires the presence of a supported hardware MMU and a backing
|
||||||
|
store implementation with access to the compiled Zephyr binary.
|
||||||
|
For demonstration purposes, the ondemand_semihost backing store is used on
|
||||||
|
a QEMU ARM64 target with a hardcoded small RAM configuration.
|
||||||
|
|
||||||
|
Building and Running
|
||||||
|
********************
|
||||||
|
|
||||||
|
This application can be built and executed on QEMU as follows:
|
||||||
|
|
||||||
|
.. zephyr-app-commands::
|
||||||
|
:zephyr-app: samples/subsys/demand_paging
|
||||||
|
:host-os: unix
|
||||||
|
:board: qemu_cortex_a53
|
||||||
|
:goals: run
|
||||||
|
:compact:
|
||||||
|
|
||||||
|
Sample Output
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
*** Booting Zephyr OS build v3.7.0-2108-g5975c3785356 ***
|
||||||
|
Calling huge body of code that doesn't fit in memory
|
||||||
|
free memory pages: from 37 to 0, 987 page faults
|
||||||
|
Calling it a second time
|
||||||
|
free memory pages: from 0 to 0, 987 page faults
|
||||||
|
Done.
|
||||||
|
|
||||||
|
Exit QEMU by pressing :kbd:`CTRL+A` :kbd:`x`.
|
||||||
|
|
||||||
|
To actually view the underlying demand paging subsystem at work, debug
|
||||||
|
prints can be enabled using :kconfig:option:`CONFIG_LOG` and
|
||||||
|
:kconfig:option:`CONFIG_KERNEL_LOG_LEVEL_DBG` in the config file.
|
||||||
8
samples/subsys/demand_paging/prj.conf
Normal file
8
samples/subsys/demand_paging/prj.conf
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
CONFIG_SRAM_SIZE=256
|
||||||
|
CONFIG_DEMAND_PAGING=y
|
||||||
|
CONFIG_DEMAND_PAGING_ALLOW_IRQ=y
|
||||||
|
CONFIG_DEMAND_PAGING_STATS=y
|
||||||
|
CONFIG_DEMAND_MAPPING=y
|
||||||
|
CONFIG_LINKER_USE_ONDEMAND_SECTION=y
|
||||||
|
CONFIG_SEMIHOST=y
|
||||||
|
CONFIG_BACKING_STORE_ONDEMAND_SEMIHOST=y
|
||||||
25
samples/subsys/demand_paging/sample.yaml
Normal file
25
samples/subsys/demand_paging/sample.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
sample:
|
||||||
|
description: On-Demand paging of firmware larger than available memory
|
||||||
|
name: demand-paging
|
||||||
|
common:
|
||||||
|
tags:
|
||||||
|
- kernel
|
||||||
|
- mmu
|
||||||
|
- demand_paging
|
||||||
|
integration_platforms:
|
||||||
|
- qemu_cortex_a53
|
||||||
|
harness: console
|
||||||
|
harness_config:
|
||||||
|
type: multi_line
|
||||||
|
ordered: true
|
||||||
|
regex:
|
||||||
|
- "Calling huge body of code that doesn't fit in memory"
|
||||||
|
- "free memory pages: from (.*) to 0, (.*) page faults"
|
||||||
|
- "Calling it a second time"
|
||||||
|
- "free memory pages: from 0 to 0, (.*) page faults"
|
||||||
|
- "Done."
|
||||||
|
tests:
|
||||||
|
sample.demand_paging.ondemand_section:
|
||||||
|
platform_allow:
|
||||||
|
- qemu_cortex_a53
|
||||||
|
- qemu_cortex_a53/qemu_cortex_a53/smp
|
||||||
75
samples/subsys/demand_paging/src/main.c
Normal file
75
samples/subsys/demand_paging/src/main.c
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 BayLibre SAS
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/linker/sections.h>
|
||||||
|
|
||||||
|
#include <mmu.h> /* for k_mem_num_pagefaults_get() */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We want to artificially create a huge body of code hence the volatile
|
||||||
|
* to prevent the compiler from optimizing everything down.
|
||||||
|
*/
|
||||||
|
volatile long foo_val1;
|
||||||
|
volatile long foo_val2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is tagged with __ondemand_func to be placed in a special
|
||||||
|
* "ondemand" segment by the linker. This means it is not loaded into memory
|
||||||
|
* at boot time but rather page-by-page on demand when actually executed.
|
||||||
|
* It is also subject to being evicted when memory reclamation occurs.
|
||||||
|
*/
|
||||||
|
static void __ondemand_func huge_evictable_function(void)
|
||||||
|
{
|
||||||
|
foo_val1 = 13131313;
|
||||||
|
foo_val2 = 45454545;
|
||||||
|
|
||||||
|
#define CODE \
|
||||||
|
foo_val1 *= foo_val2; \
|
||||||
|
foo_val2 += 1234567; \
|
||||||
|
foo_val1 -= 9876543210; \
|
||||||
|
__asm__ __volatile__ (".rep 1000; nop; .endr");
|
||||||
|
|
||||||
|
#define REPEAT_10(x) x x x x x x x x x x
|
||||||
|
#define REPEAT_1000(x) REPEAT_10(REPEAT_10(REPEAT_10(x)))
|
||||||
|
|
||||||
|
REPEAT_1000(CODE)
|
||||||
|
|
||||||
|
#undef REPEAT_1000
|
||||||
|
#undef REPEAT_10
|
||||||
|
#undef CODE
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
size_t free_pages_before, free_pages_after;
|
||||||
|
unsigned long faults_before, faults_after;
|
||||||
|
|
||||||
|
printk("Calling huge body of code that doesn't fit in memory\n");
|
||||||
|
free_pages_before = k_mem_free_get() / CONFIG_MMU_PAGE_SIZE;
|
||||||
|
faults_before = k_mem_num_pagefaults_get();
|
||||||
|
|
||||||
|
huge_evictable_function();
|
||||||
|
|
||||||
|
free_pages_after = k_mem_free_get() / CONFIG_MMU_PAGE_SIZE;
|
||||||
|
faults_after = k_mem_num_pagefaults_get();
|
||||||
|
printk("free memory pages: from %zd to %zd, %ld page faults\n",
|
||||||
|
free_pages_before, free_pages_after, faults_after - faults_before);
|
||||||
|
|
||||||
|
printk("Calling it a second time\n");
|
||||||
|
free_pages_before = k_mem_free_get() / CONFIG_MMU_PAGE_SIZE;
|
||||||
|
faults_before = k_mem_num_pagefaults_get();
|
||||||
|
|
||||||
|
huge_evictable_function();
|
||||||
|
|
||||||
|
free_pages_after = k_mem_free_get() / CONFIG_MMU_PAGE_SIZE;
|
||||||
|
faults_after = k_mem_num_pagefaults_get();
|
||||||
|
printk("free memory pages: from %zd to %zd, %ld page faults\n",
|
||||||
|
free_pages_before, free_pages_after, faults_after - faults_before);
|
||||||
|
|
||||||
|
printk("Done.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue