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:
Nicolas Pitre 2024-08-01 20:41:25 -04:00 committed by Anas Nashif
parent 7e847eca25
commit db9e5ec6f8
5 changed files with 169 additions and 0 deletions

View 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)

View 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.

View 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

View 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

View 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;
}