summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mm/bootmem.c92
1 files changed, 54 insertions, 38 deletions
diff --git a/mm/bootmem.c b/mm/bootmem.c
index e5415a5414a..89646f77b42 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -408,6 +408,7 @@ static void * __init alloc_bootmem_core(struct bootmem_data *bdata,
unsigned long size, unsigned long align,
unsigned long goal, unsigned long limit)
{
+ unsigned long fallback = 0;
unsigned long min, max, start, sidx, midx, step;
BUG_ON(!size);
@@ -443,8 +444,11 @@ static void * __init alloc_bootmem_core(struct bootmem_data *bdata,
midx = max - PFN_DOWN(bdata->node_boot_start);
if (bdata->hint_idx > sidx) {
- /* Make sure we retry on failure */
- goal = 1;
+ /*
+ * Handle the valid case of sidx being zero and still
+ * catch the fallback below.
+ */
+ fallback = sidx + 1;
sidx = ALIGN(bdata->hint_idx, step);
}
@@ -492,10 +496,39 @@ find_block:
return region;
}
+ if (fallback) {
+ sidx = ALIGN(fallback - 1, step);
+ fallback = 0;
+ goto find_block;
+ }
+
+ return NULL;
+}
+
+static void * __init ___alloc_bootmem_nopanic(unsigned long size,
+ unsigned long align,
+ unsigned long goal,
+ unsigned long limit)
+{
+ bootmem_data_t *bdata;
+
+restart:
+ list_for_each_entry(bdata, &bdata_list, list) {
+ void *region;
+
+ if (goal && bdata->node_low_pfn <= PFN_DOWN(goal))
+ continue;
+ if (limit && bdata->node_boot_start >= limit)
+ break;
+
+ region = alloc_bootmem_core(bdata, size, align, goal, limit);
+ if (region)
+ return region;
+ }
+
if (goal) {
goal = 0;
- sidx = 0;
- goto find_block;
+ goto restart;
}
return NULL;
@@ -515,16 +548,23 @@ find_block:
* Returns NULL on failure.
*/
void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
- unsigned long goal)
+ unsigned long goal)
{
- bootmem_data_t *bdata;
- void *ptr;
+ return ___alloc_bootmem_nopanic(size, align, goal, 0);
+}
- list_for_each_entry(bdata, &bdata_list, list) {
- ptr = alloc_bootmem_core(bdata, size, align, goal, 0);
- if (ptr)
- return ptr;
- }
+static void * __init ___alloc_bootmem(unsigned long size, unsigned long align,
+ unsigned long goal, unsigned long limit)
+{
+ void *mem = ___alloc_bootmem_nopanic(size, align, goal, limit);
+
+ if (mem)
+ return mem;
+ /*
+ * Whoops, we cannot satisfy the allocation request.
+ */
+ printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size);
+ panic("Out of memory");
return NULL;
}
@@ -544,16 +584,7 @@ void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
void * __init __alloc_bootmem(unsigned long size, unsigned long align,
unsigned long goal)
{
- void *mem = __alloc_bootmem_nopanic(size,align,goal);
-
- if (mem)
- return mem;
- /*
- * Whoops, we cannot satisfy the allocation request.
- */
- printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size);
- panic("Out of memory");
- return NULL;
+ return ___alloc_bootmem(size, align, goal, 0);
}
/**
@@ -653,22 +684,7 @@ void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,
unsigned long goal)
{
- bootmem_data_t *bdata;
- void *ptr;
-
- list_for_each_entry(bdata, &bdata_list, list) {
- ptr = alloc_bootmem_core(bdata, size, align, goal,
- ARCH_LOW_ADDRESS_LIMIT);
- if (ptr)
- return ptr;
- }
-
- /*
- * Whoops, we cannot satisfy the allocation request.
- */
- printk(KERN_ALERT "low bootmem alloc of %lu bytes failed!\n", size);
- panic("Out of low memory");
- return NULL;
+ return ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);
}
/**