diff --git a/subsys/demand_paging/eviction/CMakeLists.txt b/subsys/demand_paging/eviction/CMakeLists.txt index dd1bf8032a..4b28d8ebe2 100644 --- a/subsys/demand_paging/eviction/CMakeLists.txt +++ b/subsys/demand_paging/eviction/CMakeLists.txt @@ -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() diff --git a/subsys/demand_paging/eviction/Kconfig b/subsys/demand_paging/eviction/Kconfig index 0a39d9ad8a..05f8b8d333 100644 --- a/subsys/demand_paging/eviction/Kconfig +++ b/subsys/demand_paging/eviction/Kconfig @@ -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 diff --git a/subsys/demand_paging/eviction/nru.c b/subsys/demand_paging/eviction/nru.c new file mode 100644 index 0000000000..bfdb9ea2bf --- /dev/null +++ b/subsys/demand_paging/eviction/nru.c @@ -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 +#include +#include +#include + +/* 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)); +}