memblock: Add debugfs files to dump the arrays content
[deliverable/linux.git] / mm / memblock.c
index 0787790b1ce0b0f7fd30b86f9563c16e9acde63f..cc15be29fd0ac44ad0eb5d64e087af6587be7737 100644 (file)
@@ -15,6 +15,9 @@
 #include <linux/init.h>
 #include <linux/bitops.h>
 #include <linux/poison.h>
+#include <linux/pfn.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <linux/memblock.h>
 
 struct memblock memblock;
@@ -117,19 +120,18 @@ static phys_addr_t __init memblock_find_region(phys_addr_t start, phys_addr_t en
        return MEMBLOCK_ERROR;
 }
 
-static phys_addr_t __init memblock_find_base(phys_addr_t size, phys_addr_t align, phys_addr_t max_addr)
+static phys_addr_t __init memblock_find_base(phys_addr_t size, phys_addr_t align,
+                                       phys_addr_t start, phys_addr_t end)
 {
        long i;
-       phys_addr_t base = 0;
-       phys_addr_t res_base;
 
        BUG_ON(0 == size);
 
        size = memblock_align_up(size, align);
 
        /* Pump up max_addr */
-       if (max_addr == MEMBLOCK_ALLOC_ACCESSIBLE)
-               max_addr = memblock.current_limit;
+       if (end == MEMBLOCK_ALLOC_ACCESSIBLE)
+               end = memblock.current_limit;
 
        /* We do a top-down search, this tends to limit memory
         * fragmentation by keeping early boot allocs near the
@@ -138,13 +140,19 @@ static phys_addr_t __init memblock_find_base(phys_addr_t size, phys_addr_t align
        for (i = memblock.memory.cnt - 1; i >= 0; i--) {
                phys_addr_t memblockbase = memblock.memory.regions[i].base;
                phys_addr_t memblocksize = memblock.memory.regions[i].size;
+               phys_addr_t bottom, top, found;
 
                if (memblocksize < size)
                        continue;
-               base = min(memblockbase + memblocksize, max_addr);
-               res_base = memblock_find_region(memblockbase, base, size, align);
-               if (res_base != MEMBLOCK_ERROR)
-                       return res_base;
+               if ((memblockbase + memblocksize) <= start)
+                       break;
+               bottom = max(memblockbase, start);
+               top = min(memblockbase + memblocksize, end);
+               if (bottom >= top)
+                       continue;
+               found = memblock_find_region(bottom, top, size, align);
+               if (found != MEMBLOCK_ERROR)
+                       return found;
        }
        return MEMBLOCK_ERROR;
 }
@@ -204,7 +212,7 @@ static int memblock_double_array(struct memblock_type *type)
                new_array = kmalloc(new_size, GFP_KERNEL);
                addr = new_array == NULL ? MEMBLOCK_ERROR : __pa(new_array);
        } else
-               addr = memblock_find_base(new_size, sizeof(phys_addr_t), MEMBLOCK_ALLOC_ACCESSIBLE);
+               addr = memblock_find_base(new_size, sizeof(phys_addr_t), 0, MEMBLOCK_ALLOC_ACCESSIBLE);
        if (addr == MEMBLOCK_ERROR) {
                pr_err("memblock: Failed to double %s array from %ld to %ld entries !\n",
                       memblock_type_name(type), type->max, type->max * 2);
@@ -241,6 +249,12 @@ static int memblock_double_array(struct memblock_type *type)
        return 0;
 }
 
+extern int __weak memblock_memory_can_coalesce(phys_addr_t addr1, phys_addr_t size1,
+                                         phys_addr_t addr2, phys_addr_t size2)
+{
+       return 1;
+}
+
 static long memblock_add_region(struct memblock_type *type, phys_addr_t base, phys_addr_t size)
 {
        unsigned long coalesced = 0;
@@ -262,6 +276,10 @@ static long memblock_add_region(struct memblock_type *type, phys_addr_t base, ph
                        return 0;
 
                adjacent = memblock_addrs_adjacent(base, size, rgnbase, rgnsize);
+               /* Check if arch allows coalescing */
+               if (adjacent != 0 && type == &memblock.memory &&
+                   !memblock_memory_can_coalesce(base, size, rgnbase, rgnsize))
+                       break;
                if (adjacent > 0) {
                        type->regions[i].base -= size;
                        type->regions[i].size += size;
@@ -274,7 +292,14 @@ static long memblock_add_region(struct memblock_type *type, phys_addr_t base, ph
                }
        }
 
-       if ((i < type->cnt - 1) && memblock_regions_adjacent(type, i, i+1)) {
+       /* If we plugged a hole, we may want to also coalesce with the
+        * next region
+        */
+       if ((i < type->cnt - 1) && memblock_regions_adjacent(type, i, i+1) &&
+           ((type != &memblock.memory || memblock_memory_can_coalesce(type->regions[i].base,
+                                                            type->regions[i].size,
+                                                            type->regions[i+1].base,
+                                                            type->regions[i+1].size)))) {
                memblock_coalesce_regions(type, i, i+1);
                coalesced++;
        }
@@ -399,7 +424,7 @@ phys_addr_t __init __memblock_alloc_base(phys_addr_t size, phys_addr_t align, ph
         */
        size = memblock_align_up(size, align);
 
-       found = memblock_find_base(size, align, max_addr);
+       found = memblock_find_base(size, align, 0, max_addr);
        if (found != MEMBLOCK_ERROR &&
            memblock_add_region(&memblock.reserved, found, size) >= 0)
                return found;
@@ -429,11 +454,36 @@ phys_addr_t __init memblock_alloc(phys_addr_t size, phys_addr_t align)
 /*
  * Additional node-local allocators. Search for node memory is bottom up
  * and walks memblock regions within that node bottom-up as well, but allocation
- * within an memblock region is top-down.
+ * within an memblock region is top-down. XXX I plan to fix that at some stage
+ *
+ * WARNING: Only available after early_node_map[] has been populated,
+ * on some architectures, that is after all the calls to add_active_range()
+ * have been done to populate it.
  */
 
 phys_addr_t __weak __init memblock_nid_range(phys_addr_t start, phys_addr_t end, int *nid)
 {
+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP
+       /*
+        * This code originates from sparc which really wants use to walk by addresses
+        * and returns the nid. This is not very convenient for early_pfn_map[] users
+        * as the map isn't sorted yet, and it really wants to be walked by nid.
+        *
+        * For now, I implement the inefficient method below which walks the early
+        * map multiple times. Eventually we may want to use an ARCH config option
+        * to implement a completely different method for both case.
+        */
+       unsigned long start_pfn, end_pfn;
+       int i;
+
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               get_pfn_range_for_nid(i, &start_pfn, &end_pfn);
+               if (start < PFN_PHYS(start_pfn) || start >= PFN_PHYS(end_pfn))
+                       continue;
+               *nid = i;
+               return min(end, PFN_PHYS(end_pfn));
+       }
+#endif
        *nid = 0;
 
        return end;
@@ -489,9 +539,23 @@ phys_addr_t __init memblock_alloc_nid(phys_addr_t size, phys_addr_t align, int n
                        return ret;
        }
 
-       return memblock_alloc(size, align);
+       return 0;
 }
 
+phys_addr_t __init memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, int nid)
+{
+       phys_addr_t res = memblock_alloc_nid(size, align, nid);
+
+       if (res)
+               return res;
+       return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ANYWHERE);
+}
+
+
+/*
+ * Remaining API functions
+ */
+
 /* You must call memblock_analyze() before this. */
 phys_addr_t __init memblock_phys_mem_size(void)
 {
@@ -678,3 +742,52 @@ static int __init early_memblock(char *p)
 }
 early_param("memblock", early_memblock);
 
+#ifdef CONFIG_DEBUG_FS
+
+static int memblock_debug_show(struct seq_file *m, void *private)
+{
+       struct memblock_type *type = m->private;
+       struct memblock_region *reg;
+       int i;
+
+       for (i = 0; i < type->cnt; i++) {
+               reg = &type->regions[i];
+               seq_printf(m, "%4d: ", i);
+               if (sizeof(phys_addr_t) == 4)
+                       seq_printf(m, "0x%08lx..0x%08lx\n",
+                                  (unsigned long)reg->base,
+                                  (unsigned long)(reg->base + reg->size - 1));
+               else
+                       seq_printf(m, "0x%016llx..0x%016llx\n",
+                                  (unsigned long long)reg->base,
+                                  (unsigned long long)(reg->base + reg->size - 1));
+
+       }
+       return 0;
+}
+
+static int memblock_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, memblock_debug_show, inode->i_private);
+}
+
+static const struct file_operations memblock_debug_fops = {
+       .open = memblock_debug_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int __init memblock_init_debugfs(void)
+{
+       struct dentry *root = debugfs_create_dir("memblock", NULL);
+       if (!root)
+               return -ENXIO;
+       debugfs_create_file("memory", S_IRUGO, root, &memblock.memory, &memblock_debug_fops);
+       debugfs_create_file("reserved", S_IRUGO, root, &memblock.reserved, &memblock_debug_fops);
+
+       return 0;
+}
+__initcall(memblock_init_debugfs);
+
+#endif /* CONFIG_DEBUG_FS */
This page took 0.037442 seconds and 5 git commands to generate.