]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - mm/sparse.c
mm: fix free_all_bootmem_core alignment check
[mv-sheeva.git] / mm / sparse.c
index 7e9191381f86d0635e9ec68cda34cff6313ea859..7a3650923d9a8f6a5b4b959982f658d5032cd923 100644 (file)
@@ -8,9 +8,11 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/vmalloc.h>
+#include "internal.h"
 #include <asm/dma.h>
 #include <asm/pgalloc.h>
 #include <asm/pgtable.h>
+#include "internal.h"
 
 /*
  * Permanent SPARSEMEM data:
@@ -146,22 +148,41 @@ static inline int sparse_early_nid(struct mem_section *section)
        return (section->section_mem_map >> SECTION_NID_SHIFT);
 }
 
-/* Record a memory area against a node. */
-void __init memory_present(int nid, unsigned long start, unsigned long end)
+/* Validate the physical addressing limitations of the model */
+void __meminit mminit_validate_memmodel_limits(unsigned long *start_pfn,
+                                               unsigned long *end_pfn)
 {
-       unsigned long max_arch_pfn = 1UL << (MAX_PHYSMEM_BITS-PAGE_SHIFT);
-       unsigned long pfn;
+       unsigned long max_sparsemem_pfn = 1UL << (MAX_PHYSMEM_BITS-PAGE_SHIFT);
 
        /*
         * Sanity checks - do not allow an architecture to pass
         * in larger pfns than the maximum scope of sparsemem:
         */
-       if (start >= max_arch_pfn)
-               return;
-       if (end >= max_arch_pfn)
-               end = max_arch_pfn;
+       if (*start_pfn > max_sparsemem_pfn) {
+               mminit_dprintk(MMINIT_WARNING, "pfnvalidation",
+                       "Start of range %lu -> %lu exceeds SPARSEMEM max %lu\n",
+                       *start_pfn, *end_pfn, max_sparsemem_pfn);
+               WARN_ON_ONCE(1);
+               *start_pfn = max_sparsemem_pfn;
+               *end_pfn = max_sparsemem_pfn;
+       }
+
+       if (*end_pfn > max_sparsemem_pfn) {
+               mminit_dprintk(MMINIT_WARNING, "pfnvalidation",
+                       "End of range %lu -> %lu exceeds SPARSEMEM max %lu\n",
+                       *start_pfn, *end_pfn, max_sparsemem_pfn);
+               WARN_ON_ONCE(1);
+               *end_pfn = max_sparsemem_pfn;
+       }
+}
+
+/* Record a memory area against a node. */
+void __init memory_present(int nid, unsigned long start, unsigned long end)
+{
+       unsigned long pfn;
 
        start &= PAGE_SECTION_MASK;
+       mminit_validate_memmodel_limits(&start, &end);
        for (pfn = start; pfn < end; pfn += PAGES_PER_SECTION) {
                unsigned long section = pfn_to_section_nr(pfn);
                struct mem_section *ms;
@@ -186,6 +207,7 @@ unsigned long __init node_memmap_size_bytes(int nid, unsigned long start_pfn,
        unsigned long pfn;
        unsigned long nr_pages = 0;
 
+       mminit_validate_memmodel_limits(&start_pfn, &end_pfn);
        for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
                if (nid != early_pfn_to_nid(pfn))
                        continue;
@@ -208,12 +230,12 @@ static unsigned long sparse_encode_mem_map(struct page *mem_map, unsigned long p
 }
 
 /*
- * We need this if we ever free the mem_maps.  While not implemented yet,
- * this function is included for parity with its sibling.
+ * Decode mem_map from the coded memmap
  */
-static __attribute((unused))
 struct page *sparse_decode_mem_map(unsigned long coded_mem_map, unsigned long pnum)
 {
+       /* mask off the extra low bits of information */
+       coded_mem_map &= SECTION_MAP_MASK;
        return ((struct page *)coded_mem_map) + section_nr_to_pfn(pnum);
 }
 
@@ -232,7 +254,7 @@ static int __meminit sparse_init_one_section(struct mem_section *ms,
        return 1;
 }
 
-static unsigned long usemap_size(void)
+unsigned long usemap_size(void)
 {
        unsigned long size_bytes;
        size_bytes = roundup(SECTION_BLOCKFLAGS_BITS, 8) / 8;
@@ -260,7 +282,7 @@ static unsigned long *__init sparse_early_usemap_alloc(unsigned long pnum)
        /* Stupid: suppress gcc warning for SPARSEMEM && !NUMA */
        nid = 0;
 
-       printk(KERN_WARNING "%s: allocation failed\n", __FUNCTION__);
+       printk(KERN_WARNING "%s: allocation failed\n", __func__);
        return NULL;
 }
 
@@ -273,8 +295,8 @@ struct page __init *sparse_mem_map_populate(unsigned long pnum, int nid)
        if (map)
                return map;
 
-       map = alloc_bootmem_node(NODE_DATA(nid),
-                       sizeof(struct page) * PAGES_PER_SECTION);
+       map = alloc_bootmem_pages_node(NODE_DATA(nid),
+                      PAGE_ALIGN(sizeof(struct page) * PAGES_PER_SECTION));
        return map;
 }
 #endif /* !CONFIG_SPARSEMEM_VMEMMAP */
@@ -290,7 +312,7 @@ struct page __init *sparse_early_mem_map_alloc(unsigned long pnum)
                return map;
 
        printk(KERN_ERR "%s: sparsemem memory map backing failed "
-                       "some memory will not be available.\n", __FUNCTION__);
+                       "some memory will not be available.\n", __func__);
        ms->section_mem_map = 0;
        return NULL;
 }
@@ -365,6 +387,9 @@ static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
 {
        return; /* XXX: Not implemented yet */
 }
+static void free_map_bootmem(struct page *page, unsigned long nr_pages)
+{
+}
 #else
 static struct page *__kmalloc_section_memmap(unsigned long nr_pages)
 {
@@ -402,8 +427,69 @@ static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
                free_pages((unsigned long)memmap,
                           get_order(sizeof(struct page) * nr_pages));
 }
+
+static void free_map_bootmem(struct page *page, unsigned long nr_pages)
+{
+       unsigned long maps_section_nr, removing_section_nr, i;
+       int magic;
+
+       for (i = 0; i < nr_pages; i++, page++) {
+               magic = atomic_read(&page->_mapcount);
+
+               BUG_ON(magic == NODE_INFO);
+
+               maps_section_nr = pfn_to_section_nr(page_to_pfn(page));
+               removing_section_nr = page->private;
+
+               /*
+                * When this function is called, the removing section is
+                * logical offlined state. This means all pages are isolated
+                * from page allocator. If removing section's memmap is placed
+                * on the same section, it must not be freed.
+                * If it is freed, page allocator may allocate it which will
+                * be removed physically soon.
+                */
+               if (maps_section_nr != removing_section_nr)
+                       put_page_bootmem(page);
+       }
+}
 #endif /* CONFIG_SPARSEMEM_VMEMMAP */
 
+static void free_section_usemap(struct page *memmap, unsigned long *usemap)
+{
+       struct page *usemap_page;
+       unsigned long nr_pages;
+
+       if (!usemap)
+               return;
+
+       usemap_page = virt_to_page(usemap);
+       /*
+        * Check to see if allocation came from hot-plug-add
+        */
+       if (PageSlab(usemap_page)) {
+               kfree(usemap);
+               if (memmap)
+                       __kfree_section_memmap(memmap, PAGES_PER_SECTION);
+               return;
+       }
+
+       /*
+        * The usemap came from bootmem. This is packed with other usemaps
+        * on the section which has pgdat at boot time. Just keep it as is now.
+        */
+
+       if (memmap) {
+               struct page *memmap_page;
+               memmap_page = virt_to_page(memmap);
+
+               nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page))
+                       >> PAGE_SHIFT;
+
+               free_map_bootmem(memmap_page, nr_pages);
+       }
+}
+
 /*
  * returns the number of sections whose mem_maps were properly
  * set.  If this is <=0, then that means that the passed-in
@@ -456,4 +542,20 @@ out:
        }
        return ret;
 }
+
+void sparse_remove_one_section(struct zone *zone, struct mem_section *ms)
+{
+       struct page *memmap = NULL;
+       unsigned long *usemap = NULL;
+
+       if (ms->section_mem_map) {
+               usemap = ms->pageblock_flags;
+               memmap = sparse_decode_mem_map(ms->section_mem_map,
+                                               __section_nr(ms));
+               ms->section_mem_map = 0;
+               ms->pageblock_flags = NULL;
+       }
+
+       free_section_usemap(memmap, usemap);
+}
 #endif