From e79b129acb7ced89812ff733024e1f73f913ef54 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorkiewicz Date: Fri, 24 Nov 2023 16:30:07 +0100 Subject: [PATCH] mm/pmm: KTF must not run out of 4k frames at any point If we run out of 4k frames it becomes impossible to perform a higher order frames splitting, since new frames creation may need new frames array, which is 4k as well. To prevent these kind of situations, always keep at least two free 4k frames available. --- mm/pmm.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/mm/pmm.c b/mm/pmm.c index 279bb84e..949a4349 100644 --- a/mm/pmm.c +++ b/mm/pmm.c @@ -41,6 +41,7 @@ static frames_array_t early_frames; static list_head_t free_frames[MAX_PAGE_ORDER + 1]; static list_head_t busy_frames[MAX_PAGE_ORDER + 1]; +#define MIN_NUM_4K_FRAMES 2 static size_t frames_count[MAX_PAGE_ORDER + 1]; static spinlock_t lock = SPINLOCK_INIT; @@ -572,19 +573,37 @@ static void merge_frames(frame_t *first) { merge_frames(first); } +static inline bool enough_4k_frames(void) { + frame_t *frame; + int count = 0; + + list_for_each_entry (frame, &free_frames[PAGE_ORDER_4K], list) { + if (++count >= MIN_NUM_4K_FRAMES) + return true; + } + + return false; +} + +static void try_create_4k_frames(void) { + while (!enough_4k_frames()) { + frame_t *frame = find_larger_frame(free_frames, PAGE_ORDER_4K); + if (!frame) + panic("No more frames available to create 4K frames"); + split_frame(frame); + } +} + /* Reserves and returns the first free frame fulfilling * the condition specified by the callback. * This function does not split larger frames. */ frame_t *get_free_frames_cond(free_frames_cond_t cb) { spin_lock(&lock); + try_create_4k_frames(); for_each_order (order) { frame_t *frame; - if (list_is_empty(&free_frames[order])) { - continue; - } - list_for_each_entry (frame, &free_frames[order], list) { if (cb(frame)) { reserve_frame(frame); @@ -605,7 +624,11 @@ frame_t *get_free_frames(unsigned int order) { return NULL; spin_lock(&lock); + if (order == PAGE_ORDER_4K) + try_create_4k_frames(); + while (list_is_empty(&free_frames[order])) { + BUG_ON(order == PAGE_ORDER_4K); frame = find_larger_frame(free_frames, order); if (!frame) { spin_unlock(&lock);