| /* Copyright (c) 2009, 2010 The Regents of the University of California. |
| * Copyright (c) 2016 Google Inc |
| * See LICENSE for details. |
| * |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * Kevin Klues <klueska@cs.berkeley.edu> */ |
| |
| #include <page_alloc.h> |
| #include <pmap.h> |
| #include <kmalloc.h> |
| #include <arena.h> |
| |
| /* Helper, allocates a free page. */ |
| static struct page *get_a_free_page(void) |
| { |
| void *addr; |
| |
| addr = kpages_alloc(PGSIZE, MEM_ATOMIC); |
| if (!addr) |
| return NULL; |
| return kva2page(addr); |
| } |
| |
| /** |
| * @brief Allocates a physical page from a pool of unused physical memory. |
| * |
| * Zeroes the page. |
| * |
| * @param[out] page set to point to the Page struct |
| * of the newly allocated page |
| * |
| * @return ESUCCESS on success |
| * @return -ENOMEM otherwise |
| */ |
| error_t upage_alloc(struct proc *p, page_t **page, bool zero) |
| { |
| struct page *pg = get_a_free_page(); |
| |
| if (!pg) |
| return -ENOMEM; |
| *page = pg; |
| if (zero) |
| memset(page2kva(*page), 0, PGSIZE); |
| return 0; |
| } |
| |
| error_t kpage_alloc(page_t **page) |
| { |
| struct page *pg = get_a_free_page(); |
| |
| if (!pg) |
| return -ENOMEM; |
| *page = pg; |
| return 0; |
| } |
| |
| /* Helper: allocates a refcounted page of memory for the kernel's use and |
| * returns the kernel address (kernbase), or 0 on error. */ |
| void *kpage_alloc_addr(void) |
| { |
| struct page *pg = get_a_free_page(); |
| |
| if (!pg) |
| return 0; |
| return page2kva(pg); |
| } |
| |
| void *kpage_zalloc_addr(void) |
| { |
| void *retval = kpage_alloc_addr(); |
| if (retval) |
| memset(retval, 0, PGSIZE); |
| return retval; |
| } |
| |
| /* Helper function for allocating from the kpages_arena. This may be useful |
| * later since we might send the caller to a different NUMA domain. */ |
| void *kpages_alloc(size_t size, int flags) |
| { |
| return arena_alloc(kpages_arena, size, flags); |
| } |
| |
| void *kpages_zalloc(size_t size, int flags) |
| { |
| void *ret = arena_alloc(kpages_arena, size, flags); |
| |
| if (!ret) |
| return NULL; |
| memset(ret, 0, size); |
| return ret; |
| } |
| |
| void kpages_free(void *addr, size_t size) |
| { |
| arena_free(kpages_arena, addr, size); |
| } |
| |
| /* Returns naturally aligned, contiguous pages of amount PGSIZE << order. Linux |
| * code might assume its allocations are aligned. (see dma_alloc_coherent and |
| * bnx2x). */ |
| void *get_cont_pages(size_t order, int flags) |
| { |
| return arena_xalloc(kpages_arena, PGSIZE << order, PGSIZE << order, |
| 0, 0, NULL, NULL, flags); |
| } |
| |
| void free_cont_pages(void *buf, size_t order) |
| { |
| arena_xfree(kpages_arena, buf, PGSIZE << order); |
| } |
| |
| /* Frees the page */ |
| void page_decref(page_t *page) |
| { |
| assert(!page_is_pagemap(page)); |
| kpages_free(page2kva(page), PGSIZE); |
| } |
| |
| /* Attempts to get a lock on the page for IO operations. If it is already |
| * locked, it will block the kthread until it is unlocked. Note that this is |
| * really a "sleep on some event", not necessarily the IO, but it is "the page |
| * is ready". */ |
| void lock_page(struct page *page) |
| { |
| /* when this returns, we have are the ones to have locked the page */ |
| sem_down(&page->pg_sem); |
| assert(!(atomic_read(&page->pg_flags) & PG_LOCKED)); |
| atomic_or(&page->pg_flags, PG_LOCKED); |
| } |
| |
| /* Unlocks the page, and wakes up whoever is waiting on the lock */ |
| void unlock_page(struct page *page) |
| { |
| atomic_and(&page->pg_flags, ~PG_LOCKED); |
| sem_up(&page->pg_sem); |
| } |
| |
| static void *__jumbo_pml2_alloc(struct arena *a, size_t size, int flags) |
| { |
| return arena_xalloc(a, size, PML2_PTE_REACH, 0, 0, NULL, NULL, flags); |
| } |
| |
| static struct arena *jumbo_pml2_arena; |
| |
| /* Just for example; we could add qcaches too. Do this after kmalloc_init(). */ |
| void jumbo_arena_init(void) |
| { |
| jumbo_pml2_arena = arena_create("jumbo_pml2", NULL, 0, PML2_PTE_REACH, |
| __jumbo_pml2_alloc, arena_xfree, |
| base_arena, 0, MEM_WAIT); |
| assert(jumbo_pml2_arena); |
| } |
| |
| void *jumbo_page_alloc(size_t nr, int flags) |
| { |
| return arena_alloc(jumbo_pml2_arena, nr * PML2_PTE_REACH, flags); |
| } |
| |
| void jumbo_page_free(void *buf, size_t nr) |
| { |
| arena_free(jumbo_pml2_arena, buf, nr * PML2_PTE_REACH); |
| } |