diff options
Diffstat (limited to 'mm/memblock.c')
| -rw-r--r-- | mm/memblock.c | 104 |
1 files changed, 102 insertions, 2 deletions
diff --git a/mm/memblock.c b/mm/memblock.c index e5f3f9bdc311..0787790b1ce0 100644 --- a/mm/memblock.c +++ b/mm/memblock.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
| 14 | #include <linux/slab.h> | ||
| 14 | #include <linux/init.h> | 15 | #include <linux/init.h> |
| 15 | #include <linux/bitops.h> | 16 | #include <linux/bitops.h> |
| 16 | #include <linux/poison.h> | 17 | #include <linux/poison.h> |
| @@ -18,12 +19,23 @@ | |||
| 18 | 19 | ||
| 19 | struct memblock memblock; | 20 | struct memblock memblock; |
| 20 | 21 | ||
| 21 | static int memblock_debug; | 22 | static int memblock_debug, memblock_can_resize; |
| 22 | static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS + 1]; | 23 | static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS + 1]; |
| 23 | static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS + 1]; | 24 | static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS + 1]; |
| 24 | 25 | ||
| 25 | #define MEMBLOCK_ERROR (~(phys_addr_t)0) | 26 | #define MEMBLOCK_ERROR (~(phys_addr_t)0) |
| 26 | 27 | ||
| 28 | /* inline so we don't get a warning when pr_debug is compiled out */ | ||
| 29 | static inline const char *memblock_type_name(struct memblock_type *type) | ||
| 30 | { | ||
| 31 | if (type == &memblock.memory) | ||
| 32 | return "memory"; | ||
| 33 | else if (type == &memblock.reserved) | ||
| 34 | return "reserved"; | ||
| 35 | else | ||
| 36 | return "unknown"; | ||
| 37 | } | ||
| 38 | |||
| 27 | /* | 39 | /* |
| 28 | * Address comparison utilities | 40 | * Address comparison utilities |
| 29 | */ | 41 | */ |
| @@ -156,6 +168,79 @@ static void memblock_coalesce_regions(struct memblock_type *type, | |||
| 156 | memblock_remove_region(type, r2); | 168 | memblock_remove_region(type, r2); |
| 157 | } | 169 | } |
| 158 | 170 | ||
| 171 | /* Defined below but needed now */ | ||
| 172 | static long memblock_add_region(struct memblock_type *type, phys_addr_t base, phys_addr_t size); | ||
| 173 | |||
| 174 | static int memblock_double_array(struct memblock_type *type) | ||
| 175 | { | ||
| 176 | struct memblock_region *new_array, *old_array; | ||
| 177 | phys_addr_t old_size, new_size, addr; | ||
| 178 | int use_slab = slab_is_available(); | ||
| 179 | |||
| 180 | /* We don't allow resizing until we know about the reserved regions | ||
| 181 | * of memory that aren't suitable for allocation | ||
| 182 | */ | ||
| 183 | if (!memblock_can_resize) | ||
| 184 | return -1; | ||
| 185 | |||
| 186 | pr_debug("memblock: %s array full, doubling...", memblock_type_name(type)); | ||
| 187 | |||
| 188 | /* Calculate new doubled size */ | ||
| 189 | old_size = type->max * sizeof(struct memblock_region); | ||
| 190 | new_size = old_size << 1; | ||
| 191 | |||
| 192 | /* Try to find some space for it. | ||
| 193 | * | ||
| 194 | * WARNING: We assume that either slab_is_available() and we use it or | ||
| 195 | * we use MEMBLOCK for allocations. That means that this is unsafe to use | ||
| 196 | * when bootmem is currently active (unless bootmem itself is implemented | ||
| 197 | * on top of MEMBLOCK which isn't the case yet) | ||
| 198 | * | ||
| 199 | * This should however not be an issue for now, as we currently only | ||
| 200 | * call into MEMBLOCK while it's still active, or much later when slab is | ||
| 201 | * active for memory hotplug operations | ||
| 202 | */ | ||
| 203 | if (use_slab) { | ||
| 204 | new_array = kmalloc(new_size, GFP_KERNEL); | ||
| 205 | addr = new_array == NULL ? MEMBLOCK_ERROR : __pa(new_array); | ||
| 206 | } else | ||
| 207 | addr = memblock_find_base(new_size, sizeof(phys_addr_t), MEMBLOCK_ALLOC_ACCESSIBLE); | ||
| 208 | if (addr == MEMBLOCK_ERROR) { | ||
| 209 | pr_err("memblock: Failed to double %s array from %ld to %ld entries !\n", | ||
| 210 | memblock_type_name(type), type->max, type->max * 2); | ||
| 211 | return -1; | ||
| 212 | } | ||
| 213 | new_array = __va(addr); | ||
| 214 | |||
| 215 | /* Found space, we now need to move the array over before | ||
| 216 | * we add the reserved region since it may be our reserved | ||
| 217 | * array itself that is full. | ||
| 218 | */ | ||
| 219 | memcpy(new_array, type->regions, old_size); | ||
| 220 | memset(new_array + type->max, 0, old_size); | ||
| 221 | old_array = type->regions; | ||
| 222 | type->regions = new_array; | ||
| 223 | type->max <<= 1; | ||
| 224 | |||
| 225 | /* If we use SLAB that's it, we are done */ | ||
| 226 | if (use_slab) | ||
| 227 | return 0; | ||
| 228 | |||
| 229 | /* Add the new reserved region now. Should not fail ! */ | ||
| 230 | BUG_ON(memblock_add_region(&memblock.reserved, addr, new_size) < 0); | ||
| 231 | |||
| 232 | /* If the array wasn't our static init one, then free it. We only do | ||
| 233 | * that before SLAB is available as later on, we don't know whether | ||
| 234 | * to use kfree or free_bootmem_pages(). Shouldn't be a big deal | ||
| 235 | * anyways | ||
| 236 | */ | ||
| 237 | if (old_array != memblock_memory_init_regions && | ||
| 238 | old_array != memblock_reserved_init_regions) | ||
| 239 | memblock_free(__pa(old_array), old_size); | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 159 | static long memblock_add_region(struct memblock_type *type, phys_addr_t base, phys_addr_t size) | 244 | static long memblock_add_region(struct memblock_type *type, phys_addr_t base, phys_addr_t size) |
| 160 | { | 245 | { |
| 161 | unsigned long coalesced = 0; | 246 | unsigned long coalesced = 0; |
| @@ -196,7 +281,11 @@ static long memblock_add_region(struct memblock_type *type, phys_addr_t base, ph | |||
| 196 | 281 | ||
| 197 | if (coalesced) | 282 | if (coalesced) |
| 198 | return coalesced; | 283 | return coalesced; |
| 199 | if (type->cnt >= type->max) | 284 | |
| 285 | /* If we are out of space, we fail. It's too late to resize the array | ||
| 286 | * but then this shouldn't have happened in the first place. | ||
| 287 | */ | ||
| 288 | if (WARN_ON(type->cnt >= type->max)) | ||
| 200 | return -1; | 289 | return -1; |
| 201 | 290 | ||
| 202 | /* Couldn't coalesce the MEMBLOCK, so add it to the sorted table. */ | 291 | /* Couldn't coalesce the MEMBLOCK, so add it to the sorted table. */ |
| @@ -217,6 +306,14 @@ static long memblock_add_region(struct memblock_type *type, phys_addr_t base, ph | |||
| 217 | } | 306 | } |
| 218 | type->cnt++; | 307 | type->cnt++; |
| 219 | 308 | ||
| 309 | /* The array is full ? Try to resize it. If that fails, we undo | ||
| 310 | * our allocation and return an error | ||
| 311 | */ | ||
| 312 | if (type->cnt == type->max && memblock_double_array(type)) { | ||
| 313 | type->cnt--; | ||
| 314 | return -1; | ||
| 315 | } | ||
| 316 | |||
| 220 | return 0; | 317 | return 0; |
| 221 | } | 318 | } |
| 222 | 319 | ||
| @@ -541,6 +638,9 @@ void __init memblock_analyze(void) | |||
| 541 | 638 | ||
| 542 | for (i = 0; i < memblock.memory.cnt; i++) | 639 | for (i = 0; i < memblock.memory.cnt; i++) |
| 543 | memblock.memory_size += memblock.memory.regions[i].size; | 640 | memblock.memory_size += memblock.memory.regions[i].size; |
| 641 | |||
| 642 | /* We allow resizing from there */ | ||
| 643 | memblock_can_resize = 1; | ||
| 544 | } | 644 | } |
| 545 | 645 | ||
| 546 | void __init memblock_init(void) | 646 | void __init memblock_init(void) |
