Skip to content

Commit

Permalink
Memory management: add generic memory cleaner interface
Browse files Browse the repository at this point in the history
With this change, it is possible to register different "memory
cleaner" closures, which will be invoked to free up memory when the
system is low on available physical memory. The virtio balloon
deflater and the pagecache drain function have been converted to
use this interface.

Partially addresses #1130.
  • Loading branch information
francescolavra committed Jul 15, 2022
1 parent 20dd6d9 commit 339ce1d
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 25 deletions.
5 changes: 1 addition & 4 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,13 @@
#define XENNET_TX_SERVICEQUEUE_DEPTH 512

/* mm stuff */
#define PAGECACHE_DRAIN_CUTOFF (64 * MB)
#define MEM_CLEAN_THRESHOLD (64 * MB)
#define PAGECACHE_SCAN_PERIOD_SECONDS 5
#define LOW_MEMORY_THRESHOLD (64 * MB)

/* don't go below this minimum amount of physical memory when inflating balloon */
#define BALLOON_MEMORY_MINIMUM (16 * MB)

/* attempt to deflate balloon when physical memory is below this threshold */
#define BALLOON_DEFLATE_THRESHOLD (16 * MB)

/* must be large enough for vendor code that use malloc/free interface */
#define MAX_MCACHE_ORDER 16

Expand Down
77 changes: 60 additions & 17 deletions src/kernel/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
#define init_debug(x, ...)
#endif

typedef struct mm_cleaner {
struct list l;
mem_cleaner cleaner;
} *mm_cleaner;

BSS_RO_AFTER_INIT filesystem root_fs;
BSS_RO_AFTER_INIT static kernel_heaps init_heaps;

Expand Down Expand Up @@ -184,11 +189,47 @@ closure_function(2, 2, void, fsstarted,
#define mm_debug(x, ...) do { } while(0)
#endif

static balloon_deflater mm_balloon_deflater;
static struct list mm_cleaners;
static struct spinlock mm_lock;

static u64 mm_clean(u64 clean_bytes)
{
s64 remain = clean_bytes;
spin_lock(&mm_lock);
list end = list_end(&mm_cleaners);
list last = end->prev;
list e = list_begin(&mm_cleaners);
while (e != end) {
mm_cleaner mmc = struct_from_list(e, mm_cleaner, l);
remain -= apply(mmc->cleaner, remain);
if (remain <= 0)
break;

/* This cleaner couldn't satisfy the clean request: move it to the back of the list, i.e.
* de-prioritize it for future requests. */
list next = e->next;
list_delete(e);
list_push_back(&mm_cleaners, e);

if (e == last)
/* any further elements down the list are cleaners that couldn't satisfy this request */
break;
e = next;
}
spin_unlock(&mm_lock);
return clean_bytes - remain;
}

void mm_register_balloon_deflater(balloon_deflater deflater)
boolean mm_register_mem_cleaner(mem_cleaner cleaner)
{
mm_balloon_deflater = deflater;
mm_cleaner mmc = allocate(heap_locked(init_heaps), sizeof(*mmc));
if (mmc == INVALID_ADDRESS)
return false;
mmc->cleaner = cleaner;
spin_lock(&mm_lock);
list_push_back(&mm_cleaners, &mmc->l);
spin_unlock(&mm_lock);
return true;
}

void mm_service(void)
Expand All @@ -197,20 +238,11 @@ void mm_service(void)
u64 free = heap_free(phys);
mm_debug("%s: total %ld, alloc %ld, free %ld\n", __func__,
heap_total(phys), heap_allocated(phys), free);
if (free < PAGECACHE_DRAIN_CUTOFF) {
u64 drain_bytes = PAGECACHE_DRAIN_CUTOFF - free;
u64 drained = pagecache_drain(drain_bytes);
if (drained > 0)
mm_debug(" drained %ld / %ld requested...\n", drained, drain_bytes);
free = heap_free(phys);
}

if (mm_balloon_deflater && free < BALLOON_DEFLATE_THRESHOLD) {
u64 deflate_bytes = BALLOON_DEFLATE_THRESHOLD - free;
mm_debug(" requesting %ld bytes from deflater\n", deflate_bytes);
u64 deflated = apply(mm_balloon_deflater, deflate_bytes);
mm_debug(" deflated %ld bytes\n", deflated);
(void)deflated;
if (free < MEM_CLEAN_THRESHOLD) {
u64 clean_bytes = MEM_CLEAN_THRESHOLD - free;
u64 cleaned = mm_clean(clean_bytes);
if (cleaned > 0)
mm_debug(" cleaned %ld / %ld requested...\n", cleaned, clean_bytes);
}
}

Expand Down Expand Up @@ -315,6 +347,12 @@ closure_function(0, 3, void, attach_storage,
apply(req_handler, &req);
}

closure_function(0, 1, u64, mm_pagecache_cleaner,
u64, clean_bytes)
{
return pagecache_drain(clean_bytes);
}

void kernel_runtime_init(kernel_heaps kh)
{
heap misc = heap_general(kh);
Expand All @@ -327,7 +365,12 @@ void kernel_runtime_init(kernel_heaps kh)
#endif
init_runtime(misc, locked);
init_sg(locked);
list_init(&mm_cleaners);
spin_lock_init(&mm_lock);
init_pagecache(locked, (heap)heap_linear_backed(kh), (heap)heap_physical(kh), PAGESIZE);
mem_cleaner pc_cleaner = closure(misc, mm_pagecache_cleaner);
assert(pc_cleaner != INVALID_ADDRESS);
assert(mm_register_mem_cleaner(pc_cleaner));
unmap(0, PAGESIZE); /* unmap zero page */
init_extra_prints();
init_pci(kh);
Expand Down
4 changes: 2 additions & 2 deletions src/kernel/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -757,8 +757,8 @@ void init_scheduler(heap);
void init_scheduler_cpus(heap h);
void mm_service(void);

typedef closure_type(balloon_deflater, u64, u64);
void mm_register_balloon_deflater(balloon_deflater deflater);
typedef closure_type(mem_cleaner, u64, u64);
boolean mm_register_mem_cleaner(mem_cleaner cleaner);

kernel_heaps get_kernel_heaps(void);

Expand Down
5 changes: 3 additions & 2 deletions src/virtio/virtio_balloon.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,10 @@ static boolean virtio_balloon_attach(heap general, backed_heap backed, id_heap p
vtdev_set_status(v, VIRTIO_CONFIG_STATUS_DRIVER_OK);
update_actual_pages(0);
virtio_balloon_update();
balloon_deflater bd = closure(general, virtio_balloon_deflater);
mem_cleaner bd = closure(general, virtio_balloon_deflater);
assert(bd != INVALID_ADDRESS);
mm_register_balloon_deflater(bd);
if (!mm_register_mem_cleaner(bd))
deallocate_closure(bd);
if (balloon_has_stats_vq())
virtio_balloon_init_statsq();
return true;
Expand Down

0 comments on commit 339ce1d

Please sign in to comment.