demand_paging: add NRU algorithm

Simple textbook Not Recently Used eviction algorithm.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2020-12-09 14:39:31 -08:00 committed by Anas Nashif
parent d50b2419b8
commit 367cfa4946
3 changed files with 132 additions and 1 deletions

View file

@ -1,2 +1,14 @@
# Copyright (c) 2020 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
add_definitions(-D__ZEPHYR_SUPERVISOR__)
include_directories(
${ZEPHYR_BASE}/kernel/include
${ARCH_DIR}/${ARCH}/include
)
if(NOT DEFINED CONFIG_EVICTION_CUSTOM)
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_EVICTION_NRU nru.c)
endif()

View file

@ -5,7 +5,7 @@
choice EVICTION_CHOICE
prompt "Page frame eviction algorithms"
default EVICTION_CUSTOM
default EVICTION_NRU
depends on DEMAND_PAGING
config EVICTION_CUSTOM
@ -14,4 +14,27 @@ config EVICTION_CUSTOM
This option is chosen when the eviction algorithm will be implemented
by the application, instead of using one included in Zephyr.
config EVICTION_NRU
bool "Not Recently Used (NRU) page eviction algorithm"
help
This implements a Not Recently Used page eviction algorithm.
A periodic timer will clear the accessed state of all virtual pages.
When a page frame needs to be evicted, the algorithm will prefer to
evict page frames using an ascending order of priority:
- recently accessed, dirty
- recently accessed, clean
- not recently accessed, dirty
- not recently accessed, clean
endchoice
if EVICTION_NRU
config EVICTION_NRU_PERIOD
int "Recently accessed period, in milliseconds"
default 100
help
A periodic timer will fire that clears the accessed state of all virtual
pages that are capable of being paged out. At eviction time, if a page
still has the accessed property, it will be considered as recently used.
endif # EVICTION_NRU

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*
* Not Recently Used (NRU) eviction algorithm for demand paging
*/
#include <kernel.h>
#include <mmu.h>
#include <kernel_arch_interface.h>
#include <init.h>
/* The accessed and dirty states of each page frame are used to create
* a hierarchy with a numerical value. When evicting a page, try to evict
* page with the highest value (we prefer clean, not accessed pages).
*
* In this ontology, "accessed" means "recently accessed" and gets cleared
* during the periodic update.
*
* 0 not accessed, clean
* 1 not accessed, dirty
* 2 accessed, clean
* 3 accessed, dirty
*/
static void nru_periodic_update(struct k_timer *timer)
{
uintptr_t phys;
struct z_page_frame *pf;
int key = irq_lock();
Z_PAGE_FRAME_FOREACH(phys, pf) {
if (!z_page_frame_is_evictable(pf)) {
continue;
}
/* Clear accessed bit in page tables */
(void)arch_page_info_get(pf->addr, NULL, true);
}
irq_unlock(key);
}
struct z_page_frame *z_eviction_select(bool *dirty_ptr)
{
unsigned int last_prec = 4U;
struct z_page_frame *last_pf = NULL, *pf;
bool accessed;
bool dirty = false;
uintptr_t flags, phys;
Z_PAGE_FRAME_FOREACH(phys, pf) {
unsigned int prec;
if (!z_page_frame_is_evictable(pf)) {
continue;
}
flags = arch_page_info_get(pf->addr, NULL, false);
accessed = (flags & ARCH_DATA_PAGE_ACCESSED) != 0UL;
dirty = (flags & ARCH_DATA_PAGE_DIRTY) != 0UL;
/* Implies a mismatch with page frame ontology and page
* tables
*/
__ASSERT((flags & ARCH_DATA_PAGE_LOADED) != 0U,
"non-present page, %s",
((flags & ARCH_DATA_PAGE_NOT_MAPPED) != 0U) ?
"un-mapped" : "paged out");
prec = (dirty ? 1U : 0U) + (accessed ? 2U : 0U);
if (prec == 0) {
/* If we find a not accessed, clean page we're done */
last_pf = pf;
break;
}
if (prec < last_prec) {
last_prec = prec;
last_pf = pf;
}
}
/* Shouldn't ever happen unless every page is pinned */
__ASSERT(last_pf != NULL, "no page to evict");
*dirty_ptr = dirty;
return last_pf;
}
static K_TIMER_DEFINE(nru_timer, nru_periodic_update, NULL);
void z_eviction_init(void)
{
k_timer_start(&nru_timer, K_NO_WAIT,
K_MSEC(CONFIG_EVICTION_NRU_PERIOD));
}