From 8803c23f086b6adbf1cf86c02c8853506949c81e Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sun, 24 Dec 2023 09:39:28 +0000 Subject: [PATCH] vm: improve memory usage under sparse with a high reserved memory ratio previously, sparse would attempt to create a smaller number of larger sectors on systems with lots of reserved memory, often causing an out-of-memory condition. the reserved memory ratio calculation now compares reserved memory to free memory, rather than to the address of the last byte in physical memory. this improved heuristic means sparse is now better at choosing an appropriate sector size, allowing sparse to operate on systems with high amounts of reserved memory. --- vm/sparse.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/vm/sparse.c b/vm/sparse.c index 1944a95..43ae313 100644 --- a/vm/sparse.c +++ b/vm/sparse.c @@ -24,6 +24,7 @@ */ #include #include +#include #include #include #include @@ -63,6 +64,10 @@ static struct vm_page *get_or_create_page(phys_addr_t addr) size_t nr_pages = vm_page_order_to_pages(sector->s_size); sector->s_pages = kzalloc(nr_pages * sizeof(struct vm_page), 0); + if (!sector->s_pages) { + panic("out of memory!"); + } + for (size_t i = 0; i < nr_pages; i++) { sector->s_pages[i].p_flags = VM_PAGE_RESERVED; } @@ -73,17 +78,16 @@ static struct vm_page *get_or_create_page(phys_addr_t addr) return §or->s_pages[page_number]; } -static enum vm_page_order find_minimum_sector_size(size_t pmem_size) +static enum vm_page_order find_minimum_sector_size(phys_addr_t pmem_end) { for (enum vm_page_order i = VM_PAGE_4K; i < VM_PAGE_64G; i++) { size_t order_bytes = vm_page_order_to_bytes(i); - if (order_bytes * VM_MAX_SECTORS >= pmem_size) { + if (order_bytes * VM_MAX_SECTORS >= pmem_end) { return i; } } - /* TODO panic here, once panic() is implemented. */ - return VM_PAGE_64G; + panic("cannot find suitable sector size for memory map."); } /* this function is called to calculate the optimal sector size for the system, @@ -93,12 +97,14 @@ static enum vm_page_order find_minimum_sector_size(size_t pmem_size) this function uses some heuristics and thresholds that are untested and are in need of improvement to ensure that sparse works well on a wide range of systems. */ -static void calculate_sector_size_and_count(size_t pmem_size, size_t reserved_size, unsigned int *out_sector_count, enum vm_page_order *out_sector_size) +static void calculate_sector_size_and_count( + phys_addr_t pmem_end, size_t reserved_size, size_t free_size, + unsigned int *out_sector_count, enum vm_page_order *out_sector_size) { /* we can support up to VM_MAX_SECTORS memory sectors. the minimum sector size is what ever is required to cover all of physical memory in the maximum number of sectors */ - enum vm_page_order sector_size = find_minimum_sector_size(pmem_size); + enum vm_page_order sector_size = find_minimum_sector_size(pmem_end); if (sector_size <= VM_PAGE_2M) { /* override really small sector sizes with something @@ -107,7 +113,6 @@ static void calculate_sector_size_and_count(size_t pmem_size, size_t reserved_si sector_size = VM_PAGE_2M; } - size_t free_size = pmem_size - reserved_size; /* the absolute difference between the amount of free memory and the amount of reserved memory. */ size_t memdiff = absdiff64(free_size, reserved_size); @@ -133,12 +138,12 @@ static void calculate_sector_size_and_count(size_t pmem_size, size_t reserved_si power of 2. */ size_t sector_bytes = vm_page_order_to_bytes(sector_size); - if (pmem_size & (sector_bytes - 1)) { - pmem_size &= ~(sector_bytes - 1); - pmem_size += sector_bytes; + if (pmem_end & (sector_bytes - 1)) { + pmem_end &= ~(sector_bytes - 1); + pmem_end += sector_bytes; } - size_t sector_count = div64_pow2(pmem_size, sector_bytes); + size_t sector_count = div64_pow2(pmem_end, sector_bytes); *out_sector_count = sector_count; *out_sector_size = sector_size; @@ -146,15 +151,19 @@ static void calculate_sector_size_and_count(size_t pmem_size, size_t reserved_si void vm_sparse_init(void) { - size_t pmem_size = 0, reserved_size = 0; + size_t pmem_limit = 0, reserved_size = 0, free_size = 0; struct memblock_iter it; for_each_mem_range (&it, 0x0, UINTPTR_MAX) { - if (pmem_size < it.it_limit + 1) { - pmem_size = it.it_limit + 1; + if (pmem_limit < it.it_limit + 1) { + pmem_limit = it.it_limit + 1; } } + for_each_free_mem_range (&it, 0x0, UINTPTR_MAX) { + free_size += it.it_limit - it.it_base + 1; + } + for_each_reserved_mem_range (&it, 0x0, UINTPTR_MAX) { reserved_size += it.it_limit - it.it_base + 1; } @@ -162,7 +171,9 @@ void vm_sparse_init(void) enum vm_page_order sector_size; size_t sector_bytes = 0; unsigned int nr_sectors = 0; - calculate_sector_size_and_count(pmem_size, reserved_size, &nr_sectors, §or_size); + calculate_sector_size_and_count( + pmem_limit, reserved_size, free_size, + &nr_sectors, §or_size); sector_bytes = vm_page_order_to_bytes(sector_size); char sector_size_str[64];