Remove ONE_xBYTE #defines
[akaros.git] / kern / src / page_alloc.c
index 80dd5cb..4fd1943 100644 (file)
-/* Copyright (c) 2009 The Regents of the University  of California. 
- * See the COPYRIGHT files at the top of this source tree for full 
- * license information.
- * 
- * Kevin Klues <klueska@cs.berkeley.edu>    
- */
+/* 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 <sys/queue.h>
 #include <page_alloc.h>
 #include <pmap.h>
-#include <string.h>
+#include <kmalloc.h>
+#include <arena.h>
 
-/*
- * Clear a Page structure.
- * The result has null links and 0 refcount.
- * Note that the corresponding physical page is NOT initialized!
- */
-static void page_clear(page_t *SAFE page)
+/* Helper, allocates a free page. */
+static struct page *get_a_free_page(void)
 {
-       memset(page, 0, sizeof(page_t));
+       void *addr;
+
+       addr = kpages_alloc(PGSIZE, MEM_ATOMIC);
+       if (!addr)
+               return NULL;
+       return kva2page(addr);
 }
 
-/*
- * Allocates a physical page.
- * Does NOT set the contents of the physical page to zero -
- * the caller must do that if necessary.
- *
- * *page       -- is set to point to the Page struct 
- *                of the newly allocated page
- *
- * RETURNS 
- *   ESUCCESS  -- on success
- *   -ENOMEM   -- otherwise 
- */
-error_t page_alloc(page_t** page) 
-{
-       //TODO: Put a lock around this
-       if(!LIST_EMPTY(&page_free_list)) {
-               *page = LIST_FIRST(&page_free_list);
-               LIST_REMOVE(*page, global_link);
-               REMOVE_CACHE_COLORING_PAGE_FROM_FREE_LISTS(page);
-               page_clear(*page);
-               return ESUCCESS;
-       }
-       return -ENOMEM;
-}
-
-/* 
- * This macro defines multiple functions of the form:
- * error_t _cache##_page_alloc(page_t** page, size_t color)
+/**
+ * @brief Allocates a physical page from a pool of unused physical memory.
  *
- * Each of these functions operates on a different level of 
- * of the cache heirarchy, and allocates a physical page
- * from the list of pages corresponding to the supplied 
- * color for the given cache.  
- * 
- * Does NOT set the contents of the physical page to zero -
- * the caller must do that if necessary.
+ * Zeroes the page.
  *
- * color       -- the color from which to allocate a page
- * *page       -- is set to point to the Page struct 
- *                of the newly allocated page
+ * @param[out] page  set to point to the Page struct
+ *                   of the newly allocated page
  *
- * RETURNS 
- *   ESUCCESS  -- on success
- *   -ENOMEM   -- otherwise 
- *
- * error_t _cache##_page_alloc(page_t** page, size_t color)
- * {
- *      if(!LIST_EMPTY(&(_cache##_cache_colored_page_list)[(color)])) {
- *       *(page) = LIST_FIRST(&(_cache##_cache_colored_page_list)[(color)]);
- *              LIST_REMOVE(*page, global_link);
- *              REMOVE_CACHE_COLORING_PAGE_FROM_FREE_LISTS(page);
- *              page_clear(*page);
- *              return ESUCCESS;
- *      }
- *      return -ENOMEM;
- * }
+ * @return ESUCCESS on success
+ * @return -ENOMEM  otherwise
  */
-DECLARE_CACHE_COLORED_PAGE_ALLOC_FUNCTIONS();
+error_t upage_alloc(struct proc *p, page_t **page, bool zero)
+{
+       struct page *pg = get_a_free_page();
 
-/*
- * Allocates a specific physical page.
- * Does NOT set the contents of the physical page to zero -
- * the caller must do that if necessary.
- *
- * ppn         -- the page number to allocate
- * *page       -- is set to point to the Page struct 
- *                of the newly allocated page
- *
- * RETURNS 
- *   ESUCCESS  -- on success
- *   -ENOMEM   -- otherwise 
- */
-error_t page_alloc_specific(page_t** page, size_t ppn)
+       if (!pg)
+               return -ENOMEM;
+       *page = pg;
+       if (zero)
+               memset(page2kva(*page), 0, PGSIZE);
+       return 0;
+}
+
+error_t kpage_alloc(page_t **page)
 {
-       //TODO: Put a lock around this
-       page_t* sp_page = ppn2page(ppn);
-       if( sp_page->page_ref != 0 )
+       struct page *pg = get_a_free_page();
+
+       if (!pg)
                return -ENOMEM;
-       *page = sp_page;
-       LIST_REMOVE(*page, global_link);
-       REMOVE_CACHE_COLORING_PAGE_FROM_FREE_LISTS(page);
-       page_clear(*page);
+       *page = pg;
        return 0;
 }
 
-/*
- * Return a page to the free list.
- * (This function should only be called when pp->page_ref reaches 0.)
- */
-error_t page_free(page_t* page) 
+/* 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)
 {
-       //TODO: Put a lock around this
-       page_clear(page);
-       LIST_INSERT_HEAD(&page_free_list, page, global_link);
-       INSERT_CACHE_COLORING_PAGE_ONTO_FREE_LISTS(page);       
-       return ESUCCESS;
+       struct page *pg = get_a_free_page();
+
+       if (!pg)
+               return 0;
+       return page2kva(pg);
 }
 
-/*
- * Check if a page with the given pyhysical page # is free
- */
-int page_is_free(size_t ppn) {
-       //TODO: Put a lock around this
-       page_t* page = ppn2page(ppn);
-       if( page->page_ref == 0 )
-               return TRUE;
-       return FALSE;
+void *kpage_zalloc_addr(void)
+{
+       void *retval = kpage_alloc_addr();
+       if (retval)
+               memset(retval, 0, PGSIZE);
+       return retval;
 }
 
-/*
- * Increment the reference count on a page
- */
-void page_incref(page_t *page)
+/* 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)
 {
-       page->page_ref++;
+       return arena_alloc(kpages_arena, size, flags);
 }
 
-/*
- * Decrement the reference count on a page,
- * freeing it if there are no more refs.
- */
+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)
 {
-       if (--page->page_ref == 0)
-               page_free(page);
+       assert(!page_is_pagemap(page));
+       kpages_free(page2kva(page), PGSIZE);
 }
 
-/*
- * Set the reference count on a page to a specific value
- */
-void page_setref(page_t *page, size_t val)
+/* 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)
 {
-       page->page_ref = val;
+       /* 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);
 }
 
-/*
- * Get the reference count on a page
- */
-size_t page_getref(page_t *page)
+/* Unlocks the page, and wakes up whoever is waiting on the lock */
+void unlock_page(struct page *page)
 {
-       return page->page_ref;
+       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);
+}