Browse Source

pk: avoid out-of-memory errors

Estimate available memory and return -1 from mmap if not enough
is available, rather than assert-failing.
pull/237/head
Andrew Waterman 5 years ago
parent
commit
e63232e82d
  1. 1
      pk/boot.h
  2. 156
      pk/mmap.c

1
pk/boot.h

@ -20,6 +20,7 @@ typedef struct {
size_t brk_max; size_t brk_max;
size_t mmap_max; size_t mmap_max;
size_t stack_top; size_t stack_top;
size_t vm_alloc_guess;
uint64_t time0; uint64_t time0;
uint64_t cycle0; uint64_t cycle0;
uint64_t instret0; uint64_t instret0;

156
pk/mmap.c

@ -7,9 +7,11 @@
#include "bits.h" #include "bits.h"
#include "mtrap.h" #include "mtrap.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <errno.h> #include <errno.h>
typedef struct { typedef struct vmr_t {
struct vmr_t* next;
uintptr_t addr; uintptr_t addr;
size_t length; size_t length;
file_t* file; file_t* file;
@ -18,92 +20,126 @@ typedef struct {
int prot; int prot;
} vmr_t; } vmr_t;
static vmr_t* vmr_freelist_head;
#define RISCV_PGLEVELS ((VA_BITS - RISCV_PGSHIFT) / RISCV_PGLEVEL_BITS) #define RISCV_PGLEVELS ((VA_BITS - RISCV_PGSHIFT) / RISCV_PGLEVEL_BITS)
#define MAX_VMR (RISCV_PGSIZE / sizeof(vmr_t))
static spinlock_t vm_lock = SPINLOCK_INIT; static spinlock_t vm_lock = SPINLOCK_INIT;
static vmr_t* vmrs;
static uintptr_t first_free_page; static uintptr_t first_free_page;
static size_t next_free_page; static size_t next_free_page;
static size_t free_pages; static size_t free_pages;
static size_t pages_promised;
int demand_paging = 1; // unless -p flag is given int demand_paging = 1; // unless -p flag is given
struct freelist_node_t { typedef struct freelist_node_t {
struct freelist_node_t* next; struct freelist_node_t* next;
uintptr_t addr; uintptr_t addr;
}; } freelist_node_t;
static struct freelist_node_t* freelist_head; static freelist_node_t* page_freelist_head;
static struct freelist_node_t* freelist_node_array; size_t page_freelist_depth;
static freelist_node_t* page_freelist_node_array;
static void __augment_freelist() static bool __augment_freelist()
{ {
if (next_free_page == free_pages) if (next_free_page == free_pages)
panic("Out of memory!"); return false;
struct freelist_node_t* node = &freelist_node_array[next_free_page]; freelist_node_t* node = &page_freelist_node_array[next_free_page];
node->addr = first_free_page + RISCV_PGSIZE * next_free_page; node->addr = first_free_page + RISCV_PGSIZE * next_free_page;
node->next = freelist_head; node->next = page_freelist_head;
freelist_head = node; page_freelist_head = node;
page_freelist_depth++;
next_free_page++; next_free_page++;
return true;
} }
static uintptr_t __page_alloc() static uintptr_t __page_alloc()
{ {
if (freelist_head == NULL) if (page_freelist_head == NULL && !__augment_freelist())
__augment_freelist(); return 0;
struct freelist_node_t* node = freelist_head; freelist_node_t* node = page_freelist_head;
uintptr_t addr = node->addr; uintptr_t addr = node->addr;
freelist_head = node->next; page_freelist_head = node->next;
node->next = NULL; node->next = NULL;
page_freelist_depth--;
return (uintptr_t)memset((void*)addr, 0, RISCV_PGSIZE); return (uintptr_t)memset((void*)addr, 0, RISCV_PGSIZE);
} }
static uintptr_t __page_alloc_assert()
{
uintptr_t res = __page_alloc();
if (!res)
panic("Out of memory!");
return res;
}
static void __page_free(uintptr_t addr) static void __page_free(uintptr_t addr)
{ {
size_t idx = (addr - first_free_page) / RISCV_PGSIZE; size_t idx = (addr - first_free_page) / RISCV_PGSIZE;
kassert(idx < free_pages); kassert(idx < free_pages);
struct freelist_node_t* node = &freelist_node_array[idx]; freelist_node_t* node = &page_freelist_node_array[idx];
kassert(node->addr == addr); kassert(node->addr == addr);
kassert(node->next == NULL); kassert(node->next == NULL);
node->next = freelist_head; node->next = page_freelist_head;
freelist_head = node; page_freelist_head = node;
page_freelist_depth++;
}
static size_t __num_free_pages()
{
return page_freelist_depth + (free_pages - next_free_page);
} }
static vmr_t* __vmr_alloc(uintptr_t addr, size_t length, file_t* file, static vmr_t* __vmr_alloc(uintptr_t addr, size_t length, file_t* file,
size_t offset, unsigned refcnt, int prot) size_t offset, unsigned refcnt, int prot)
{ {
if (!vmrs) if (vmr_freelist_head == NULL) {
vmrs = (vmr_t*)__page_alloc(); vmr_t* new_vmrs = (vmr_t*)__page_alloc();
if (new_vmrs == NULL)
for (vmr_t* v = vmrs; v < vmrs + MAX_VMR; v++) { return NULL;
if (v->refcnt == 0) {
if (file) vmr_freelist_head = new_vmrs;
file_incref(file);
v->addr = addr; for (size_t i = 0; i < (RISCV_PGSIZE / sizeof(vmr_t)) - 1; i++)
v->length = length; new_vmrs[i].next = &new_vmrs[i+1];
v->file = file;
v->offset = offset;
v->refcnt = refcnt;
v->prot = prot;
return v;
}
} }
return NULL;
vmr_t* v = vmr_freelist_head;
vmr_freelist_head = v->next;
pages_promised += refcnt;
if (file)
file_incref(file);
v->addr = addr;
v->length = length;
v->file = file;
v->offset = offset;
v->refcnt = refcnt;
v->prot = prot;
return v;
} }
static void __vmr_decref(vmr_t* v, unsigned dec) static void __vmr_decref(vmr_t* v, unsigned dec)
{ {
if ((v->refcnt -= dec) == 0) pages_promised -= dec;
{
if ((v->refcnt -= dec) == 0) {
if (v->file) if (v->file)
file_decref(v->file); file_decref(v->file);
v->next = vmr_freelist_head;
vmr_freelist_head = v;
} }
} }
@ -129,10 +165,14 @@ static pte_t* __walk_internal(uintptr_t addr, int create, int level)
for (int i = RISCV_PGLEVELS - 1; i > level; i--) { for (int i = RISCV_PGLEVELS - 1; i > level; i--) {
size_t idx = pt_idx(addr, i); size_t idx = pt_idx(addr, i);
if (unlikely(!(t[idx] & PTE_V))) { if (unlikely(!(t[idx] & PTE_V))) {
if (create) if (create) {
t[idx] = ptd_create(ppn(__page_alloc())); uintptr_t new_ptd = __page_alloc();
else if (!new_ptd)
return 0;
t[idx] = ptd_create(ppn(new_ptd));
} else {
return 0; return 0;
}
} }
t = (pte_t*)(pte_ppn(t[idx]) << RISCV_PGSHIFT); t = (pte_t*)(pte_ppn(t[idx]) << RISCV_PGSHIFT);
} }
@ -155,11 +195,9 @@ static int __va_avail(uintptr_t vaddr)
return pte == 0 || *pte == 0; return pte == 0 || *pte == 0;
} }
static uintptr_t __vm_alloc(size_t npage) static uintptr_t __vm_alloc_at(uintptr_t start, uintptr_t end, size_t npage)
{ {
uintptr_t start = current.brk, end = current.mmap_max - npage*RISCV_PGSIZE; for (uintptr_t a = start; a <= end; a += RISCV_PGSIZE) {
for (uintptr_t a = start; a <= end; a += RISCV_PGSIZE)
{
if (!__va_avail(a)) if (!__va_avail(a))
continue; continue;
uintptr_t first = a, last = a + (npage-1) * RISCV_PGSIZE; uintptr_t first = a, last = a + (npage-1) * RISCV_PGSIZE;
@ -172,6 +210,18 @@ static uintptr_t __vm_alloc(size_t npage)
return 0; return 0;
} }
static uintptr_t __vm_alloc(size_t npage)
{
uintptr_t end = current.mmap_max - npage * RISCV_PGSIZE;
if (current.vm_alloc_guess) {
uintptr_t ret = __vm_alloc_at(current.vm_alloc_guess, end, npage);
if (ret)
return ret;
}
return __vm_alloc_at(current.brk, end, npage);
}
static inline pte_t prot_to_type(int prot, int user) static inline pte_t prot_to_type(int prot, int user)
{ {
pte_t pte = 0; pte_t pte = 0;
@ -201,7 +251,7 @@ static int __handle_page_fault(uintptr_t vaddr, int prot)
return -1; return -1;
else if (!(*pte & PTE_V)) else if (!(*pte & PTE_V))
{ {
uintptr_t kva = __page_alloc(); uintptr_t kva = __page_alloc_assert();
uintptr_t ppn = kva / RISCV_PGSIZE; uintptr_t ppn = kva / RISCV_PGSIZE;
vmr_t* v = (vmr_t*)*pte; vmr_t* v = (vmr_t*)*pte;
@ -258,6 +308,10 @@ static void __do_munmap(uintptr_t addr, size_t len)
uintptr_t __do_mmap(uintptr_t addr, size_t length, int prot, int flags, file_t* f, off_t offset) uintptr_t __do_mmap(uintptr_t addr, size_t length, int prot, int flags, file_t* f, off_t offset)
{ {
size_t npage = (length-1)/RISCV_PGSIZE+1; size_t npage = (length-1)/RISCV_PGSIZE+1;
if (npage * 17 / 16 + 16 + pages_promised >= __num_free_pages())
return (uintptr_t)-1;
if (flags & MAP_FIXED) if (flags & MAP_FIXED)
{ {
if ((addr & (RISCV_PGSIZE-1)) || !__valid_user_range(addr, length)) if ((addr & (RISCV_PGSIZE-1)) || !__valid_user_range(addr, length))
@ -285,6 +339,8 @@ uintptr_t __do_mmap(uintptr_t addr, size_t length, int prot, int flags, file_t*
for (uintptr_t a = addr; a < addr + length; a += RISCV_PGSIZE) for (uintptr_t a = addr; a < addr + length; a += RISCV_PGSIZE)
kassert(__handle_page_fault(a, prot) == 0); kassert(__handle_page_fault(a, prot) == 0);
current.vm_alloc_guess = addr + npage * RISCV_PGSIZE;
return addr; return addr;
} }
@ -435,11 +491,11 @@ uintptr_t pk_vm_init()
free_pages = (mem_size - (first_free_page - MEM_START)) / RISCV_PGSIZE; free_pages = (mem_size - (first_free_page - MEM_START)) / RISCV_PGSIZE;
size_t num_freelist_nodes = mem_size / RISCV_PGSIZE; size_t num_freelist_nodes = mem_size / RISCV_PGSIZE;
size_t freelist_node_array_size = ROUNDUP(num_freelist_nodes * sizeof(struct freelist_node_t), RISCV_PGSIZE); size_t freelist_node_array_size = ROUNDUP(num_freelist_nodes * sizeof(freelist_node_t), RISCV_PGSIZE);
freelist_node_array = (struct freelist_node_t*)first_free_page; page_freelist_node_array = (freelist_node_t*)first_free_page;
next_free_page = freelist_node_array_size / RISCV_PGSIZE; next_free_page = freelist_node_array_size / RISCV_PGSIZE;
root_page_table = (void*)__page_alloc(); root_page_table = (void*)__page_alloc_assert();
__map_kernel_range(MEM_START, MEM_START, mem_size, PROT_READ|PROT_WRITE|PROT_EXEC); __map_kernel_range(MEM_START, MEM_START, mem_size, PROT_READ|PROT_WRITE|PROT_EXEC);
current.mmap_max = current.brk_max = MEM_START; current.mmap_max = current.brk_max = MEM_START;
@ -453,6 +509,6 @@ uintptr_t pk_vm_init()
flush_tlb(); flush_tlb();
write_csr(sptbr, ((uintptr_t)root_page_table >> RISCV_PGSHIFT) | SATP_MODE_CHOICE); write_csr(sptbr, ((uintptr_t)root_page_table >> RISCV_PGSHIFT) | SATP_MODE_CHOICE);
uintptr_t kernel_stack_top = __page_alloc() + RISCV_PGSIZE; uintptr_t kernel_stack_top = __page_alloc_assert() + RISCV_PGSIZE;
return kernel_stack_top; return kernel_stack_top;
} }

Loading…
Cancel
Save