Merge tag 'staging-4.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[cascardo/linux.git] / mm / page_ext.c
index 1629282..121dcff 100644 (file)
  * and page extension core can skip to allocate memory. As result,
  * none of memory is wasted.
  *
+ * When need callback returns true, page_ext checks if there is a request for
+ * extra memory through size in struct page_ext_operations. If it is non-zero,
+ * extra space is allocated for each page_ext entry and offset is returned to
+ * user through offset in struct page_ext_operations.
+ *
  * The init callback is used to do proper initialization after page extension
  * is completely initialized. In sparse memory system, extra memory is
  * allocated some time later than memmap is allocated. In other words, lifetime
@@ -66,18 +71,24 @@ static struct page_ext_operations *page_ext_ops[] = {
 };
 
 static unsigned long total_usage;
+static unsigned long extra_mem;
 
 static bool __init invoke_need_callbacks(void)
 {
        int i;
        int entries = ARRAY_SIZE(page_ext_ops);
+       bool need = false;
 
        for (i = 0; i < entries; i++) {
-               if (page_ext_ops[i]->need && page_ext_ops[i]->need())
-                       return true;
+               if (page_ext_ops[i]->need && page_ext_ops[i]->need()) {
+                       page_ext_ops[i]->offset = sizeof(struct page_ext) +
+                                               extra_mem;
+                       extra_mem += page_ext_ops[i]->size;
+                       need = true;
+               }
        }
 
-       return false;
+       return need;
 }
 
 static void __init invoke_init_callbacks(void)
@@ -91,6 +102,16 @@ static void __init invoke_init_callbacks(void)
        }
 }
 
+static unsigned long get_entry_size(void)
+{
+       return sizeof(struct page_ext) + extra_mem;
+}
+
+static inline struct page_ext *get_entry(void *base, unsigned long index)
+{
+       return base + get_entry_size() * index;
+}
+
 #if !defined(CONFIG_SPARSEMEM)
 
 
@@ -121,7 +142,7 @@ struct page_ext *lookup_page_ext(struct page *page)
 #endif
        index = pfn - round_down(node_start_pfn(page_to_nid(page)),
                                        MAX_ORDER_NR_PAGES);
-       return base + index;
+       return get_entry(base, index);
 }
 
 static int __init alloc_node_page_ext(int nid)
@@ -143,7 +164,7 @@ static int __init alloc_node_page_ext(int nid)
                !IS_ALIGNED(node_end_pfn(nid), MAX_ORDER_NR_PAGES))
                nr_pages += MAX_ORDER_NR_PAGES;
 
-       table_size = sizeof(struct page_ext) * nr_pages;
+       table_size = get_entry_size() * nr_pages;
 
        base = memblock_virt_alloc_try_nid_nopanic(
                        table_size, PAGE_SIZE, __pa(MAX_DMA_ADDRESS),
@@ -196,7 +217,7 @@ struct page_ext *lookup_page_ext(struct page *page)
        if (!section->page_ext)
                return NULL;
 #endif
-       return section->page_ext + pfn;
+       return get_entry(section->page_ext, pfn);
 }
 
 static void *__meminit alloc_page_ext(size_t size, int nid)
@@ -229,7 +250,7 @@ static int __meminit init_section_page_ext(unsigned long pfn, int nid)
        if (section->page_ext)
                return 0;
 
-       table_size = sizeof(struct page_ext) * PAGES_PER_SECTION;
+       table_size = get_entry_size() * PAGES_PER_SECTION;
        base = alloc_page_ext(table_size, nid);
 
        /*
@@ -249,7 +270,7 @@ static int __meminit init_section_page_ext(unsigned long pfn, int nid)
         * we need to apply a mask.
         */
        pfn &= PAGE_SECTION_MASK;
-       section->page_ext = base - pfn;
+       section->page_ext = (void *)base - get_entry_size() * pfn;
        total_usage += table_size;
        return 0;
 }
@@ -262,7 +283,7 @@ static void free_page_ext(void *addr)
                struct page *page = virt_to_page(addr);
                size_t table_size;
 
-               table_size = sizeof(struct page_ext) * PAGES_PER_SECTION;
+               table_size = get_entry_size() * PAGES_PER_SECTION;
 
                BUG_ON(PageReserved(page));
                free_pages_exact(addr, table_size);
@@ -277,7 +298,7 @@ static void __free_page_ext(unsigned long pfn)
        ms = __pfn_to_section(pfn);
        if (!ms || !ms->page_ext)
                return;
-       base = ms->page_ext + pfn;
+       base = get_entry(ms->page_ext, pfn);
        free_page_ext(base);
        ms->page_ext = NULL;
 }