diff options
Diffstat (limited to 'arch/arm/mm/init.c')
| -rw-r--r-- | arch/arm/mm/init.c | 155 |
1 files changed, 112 insertions, 43 deletions
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 7fd9b5eb177f..5164069ced42 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/highmem.h> | 18 | #include <linux/highmem.h> |
| 19 | #include <linux/gfp.h> | 19 | #include <linux/gfp.h> |
| 20 | #include <linux/memblock.h> | 20 | #include <linux/memblock.h> |
| 21 | #include <linux/sort.h> | ||
| 21 | 22 | ||
| 22 | #include <asm/mach-types.h> | 23 | #include <asm/mach-types.h> |
| 23 | #include <asm/sections.h> | 24 | #include <asm/sections.h> |
| @@ -121,9 +122,10 @@ void show_mem(void) | |||
| 121 | printk("%d pages swap cached\n", cached); | 122 | printk("%d pages swap cached\n", cached); |
| 122 | } | 123 | } |
| 123 | 124 | ||
| 124 | static void __init find_limits(struct meminfo *mi, | 125 | static void __init find_limits(unsigned long *min, unsigned long *max_low, |
| 125 | unsigned long *min, unsigned long *max_low, unsigned long *max_high) | 126 | unsigned long *max_high) |
| 126 | { | 127 | { |
| 128 | struct meminfo *mi = &meminfo; | ||
| 127 | int i; | 129 | int i; |
| 128 | 130 | ||
| 129 | *min = -1UL; | 131 | *min = -1UL; |
| @@ -147,14 +149,13 @@ static void __init find_limits(struct meminfo *mi, | |||
| 147 | } | 149 | } |
| 148 | } | 150 | } |
| 149 | 151 | ||
| 150 | static void __init arm_bootmem_init(struct meminfo *mi, | 152 | static void __init arm_bootmem_init(unsigned long start_pfn, |
| 151 | unsigned long start_pfn, unsigned long end_pfn) | 153 | unsigned long end_pfn) |
| 152 | { | 154 | { |
| 153 | struct memblock_region *reg; | 155 | struct memblock_region *reg; |
| 154 | unsigned int boot_pages; | 156 | unsigned int boot_pages; |
| 155 | phys_addr_t bitmap; | 157 | phys_addr_t bitmap; |
| 156 | pg_data_t *pgdat; | 158 | pg_data_t *pgdat; |
| 157 | int i; | ||
| 158 | 159 | ||
| 159 | /* | 160 | /* |
| 160 | * Allocate the bootmem bitmap page. This must be in a region | 161 | * Allocate the bootmem bitmap page. This must be in a region |
| @@ -172,30 +173,39 @@ static void __init arm_bootmem_init(struct meminfo *mi, | |||
| 172 | pgdat = NODE_DATA(0); | 173 | pgdat = NODE_DATA(0); |
| 173 | init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn); | 174 | init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn); |
| 174 | 175 | ||
| 175 | for_each_bank(i, mi) { | 176 | /* Free the lowmem regions from memblock into bootmem. */ |
| 176 | struct membank *bank = &mi->bank[i]; | 177 | for_each_memblock(memory, reg) { |
| 177 | if (!bank->highmem) | 178 | unsigned long start = memblock_region_memory_base_pfn(reg); |
| 178 | free_bootmem(bank_phys_start(bank), bank_phys_size(bank)); | 179 | unsigned long end = memblock_region_memory_end_pfn(reg); |
| 180 | |||
| 181 | if (end >= end_pfn) | ||
| 182 | end = end_pfn; | ||
| 183 | if (start >= end) | ||
| 184 | break; | ||
| 185 | |||
| 186 | free_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT); | ||
| 179 | } | 187 | } |
| 180 | 188 | ||
| 181 | /* | 189 | /* Reserve the lowmem memblock reserved regions in bootmem. */ |
| 182 | * Reserve the memblock reserved regions in bootmem. | ||
| 183 | */ | ||
| 184 | for_each_memblock(reserved, reg) { | 190 | for_each_memblock(reserved, reg) { |
| 185 | phys_addr_t start = memblock_region_reserved_base_pfn(reg); | 191 | unsigned long start = memblock_region_reserved_base_pfn(reg); |
| 186 | phys_addr_t end = memblock_region_reserved_end_pfn(reg); | 192 | unsigned long end = memblock_region_reserved_end_pfn(reg); |
| 187 | if (start >= start_pfn && end <= end_pfn) | 193 | |
| 188 | reserve_bootmem_node(pgdat, __pfn_to_phys(start), | 194 | if (end >= end_pfn) |
| 189 | (end - start) << PAGE_SHIFT, | 195 | end = end_pfn; |
| 190 | BOOTMEM_DEFAULT); | 196 | if (start >= end) |
| 197 | break; | ||
| 198 | |||
| 199 | reserve_bootmem(__pfn_to_phys(start), | ||
| 200 | (end - start) << PAGE_SHIFT, BOOTMEM_DEFAULT); | ||
| 191 | } | 201 | } |
| 192 | } | 202 | } |
| 193 | 203 | ||
| 194 | static void __init arm_bootmem_free(struct meminfo *mi, unsigned long min, | 204 | static void __init arm_bootmem_free(unsigned long min, unsigned long max_low, |
| 195 | unsigned long max_low, unsigned long max_high) | 205 | unsigned long max_high) |
| 196 | { | 206 | { |
| 197 | unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES]; | 207 | unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES]; |
| 198 | int i; | 208 | struct memblock_region *reg; |
| 199 | 209 | ||
| 200 | /* | 210 | /* |
| 201 | * initialise the zones. | 211 | * initialise the zones. |
| @@ -217,13 +227,20 @@ static void __init arm_bootmem_free(struct meminfo *mi, unsigned long min, | |||
| 217 | * holes = node_size - sum(bank_sizes) | 227 | * holes = node_size - sum(bank_sizes) |
| 218 | */ | 228 | */ |
| 219 | memcpy(zhole_size, zone_size, sizeof(zhole_size)); | 229 | memcpy(zhole_size, zone_size, sizeof(zhole_size)); |
| 220 | for_each_bank(i, mi) { | 230 | for_each_memblock(memory, reg) { |
| 221 | int idx = 0; | 231 | unsigned long start = memblock_region_memory_base_pfn(reg); |
| 232 | unsigned long end = memblock_region_memory_end_pfn(reg); | ||
| 233 | |||
| 234 | if (start < max_low) { | ||
| 235 | unsigned long low_end = min(end, max_low); | ||
| 236 | zhole_size[0] -= low_end - start; | ||
| 237 | } | ||
| 222 | #ifdef CONFIG_HIGHMEM | 238 | #ifdef CONFIG_HIGHMEM |
| 223 | if (mi->bank[i].highmem) | 239 | if (end > max_low) { |
| 224 | idx = ZONE_HIGHMEM; | 240 | unsigned long high_start = max(start, max_low); |
| 241 | zhole_size[ZONE_HIGHMEM] -= end - high_start; | ||
| 242 | } | ||
| 225 | #endif | 243 | #endif |
| 226 | zhole_size[idx] -= bank_pfn_size(&mi->bank[i]); | ||
| 227 | } | 244 | } |
| 228 | 245 | ||
| 229 | /* | 246 | /* |
| @@ -256,10 +273,19 @@ static void arm_memory_present(void) | |||
| 256 | } | 273 | } |
| 257 | #endif | 274 | #endif |
| 258 | 275 | ||
| 276 | static int __init meminfo_cmp(const void *_a, const void *_b) | ||
| 277 | { | ||
| 278 | const struct membank *a = _a, *b = _b; | ||
| 279 | long cmp = bank_pfn_start(a) - bank_pfn_start(b); | ||
| 280 | return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; | ||
| 281 | } | ||
| 282 | |||
| 259 | void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) | 283 | void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) |
| 260 | { | 284 | { |
| 261 | int i; | 285 | int i; |
| 262 | 286 | ||
| 287 | sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL); | ||
| 288 | |||
| 263 | memblock_init(); | 289 | memblock_init(); |
| 264 | for (i = 0; i < mi->nr_banks; i++) | 290 | for (i = 0; i < mi->nr_banks; i++) |
| 265 | memblock_add(mi->bank[i].start, mi->bank[i].size); | 291 | memblock_add(mi->bank[i].start, mi->bank[i].size); |
| @@ -292,14 +318,13 @@ void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) | |||
| 292 | 318 | ||
| 293 | void __init bootmem_init(void) | 319 | void __init bootmem_init(void) |
| 294 | { | 320 | { |
| 295 | struct meminfo *mi = &meminfo; | ||
| 296 | unsigned long min, max_low, max_high; | 321 | unsigned long min, max_low, max_high; |
| 297 | 322 | ||
| 298 | max_low = max_high = 0; | 323 | max_low = max_high = 0; |
| 299 | 324 | ||
| 300 | find_limits(mi, &min, &max_low, &max_high); | 325 | find_limits(&min, &max_low, &max_high); |
| 301 | 326 | ||
| 302 | arm_bootmem_init(mi, min, max_low); | 327 | arm_bootmem_init(min, max_low); |
| 303 | 328 | ||
| 304 | /* | 329 | /* |
| 305 | * Sparsemem tries to allocate bootmem in memory_present(), | 330 | * Sparsemem tries to allocate bootmem in memory_present(), |
| @@ -317,7 +342,7 @@ void __init bootmem_init(void) | |||
| 317 | * the sparse mem_map arrays initialized by sparse_init() | 342 | * the sparse mem_map arrays initialized by sparse_init() |
| 318 | * for memmap_init_zone(), otherwise all PFNs are invalid. | 343 | * for memmap_init_zone(), otherwise all PFNs are invalid. |
| 319 | */ | 344 | */ |
| 320 | arm_bootmem_free(mi, min, max_low, max_high); | 345 | arm_bootmem_free(min, max_low, max_high); |
| 321 | 346 | ||
| 322 | high_memory = __va((max_low << PAGE_SHIFT) - 1) + 1; | 347 | high_memory = __va((max_low << PAGE_SHIFT) - 1) + 1; |
| 323 | 348 | ||
| @@ -411,6 +436,56 @@ static void __init free_unused_memmap(struct meminfo *mi) | |||
| 411 | } | 436 | } |
| 412 | } | 437 | } |
| 413 | 438 | ||
| 439 | static void __init free_highpages(void) | ||
| 440 | { | ||
| 441 | #ifdef CONFIG_HIGHMEM | ||
| 442 | unsigned long max_low = max_low_pfn + PHYS_PFN_OFFSET; | ||
| 443 | struct memblock_region *mem, *res; | ||
| 444 | |||
| 445 | /* set highmem page free */ | ||
| 446 | for_each_memblock(memory, mem) { | ||
| 447 | unsigned long start = memblock_region_memory_base_pfn(mem); | ||
| 448 | unsigned long end = memblock_region_memory_end_pfn(mem); | ||
| 449 | |||
| 450 | /* Ignore complete lowmem entries */ | ||
| 451 | if (end <= max_low) | ||
| 452 | continue; | ||
| 453 | |||
| 454 | /* Truncate partial highmem entries */ | ||
| 455 | if (start < max_low) | ||
| 456 | start = max_low; | ||
| 457 | |||
| 458 | /* Find and exclude any reserved regions */ | ||
| 459 | for_each_memblock(reserved, res) { | ||
| 460 | unsigned long res_start, res_end; | ||
| 461 | |||
| 462 | res_start = memblock_region_reserved_base_pfn(res); | ||
| 463 | res_end = memblock_region_reserved_end_pfn(res); | ||
| 464 | |||
| 465 | if (res_end < start) | ||
| 466 | continue; | ||
| 467 | if (res_start < start) | ||
| 468 | res_start = start; | ||
| 469 | if (res_start > end) | ||
| 470 | res_start = end; | ||
| 471 | if (res_end > end) | ||
| 472 | res_end = end; | ||
| 473 | if (res_start != start) | ||
| 474 | totalhigh_pages += free_area(start, res_start, | ||
| 475 | NULL); | ||
| 476 | start = res_end; | ||
| 477 | if (start == end) | ||
| 478 | break; | ||
| 479 | } | ||
| 480 | |||
| 481 | /* And now free anything which remains */ | ||
| 482 | if (start < end) | ||
| 483 | totalhigh_pages += free_area(start, end, NULL); | ||
| 484 | } | ||
| 485 | totalram_pages += totalhigh_pages; | ||
| 486 | #endif | ||
| 487 | } | ||
| 488 | |||
| 414 | /* | 489 | /* |
| 415 | * mem_init() marks the free areas in the mem_map and tells us how much | 490 | * mem_init() marks the free areas in the mem_map and tells us how much |
| 416 | * memory is free. This is done after various parts of the system have | 491 | * memory is free. This is done after various parts of the system have |
| @@ -419,6 +494,7 @@ static void __init free_unused_memmap(struct meminfo *mi) | |||
| 419 | void __init mem_init(void) | 494 | void __init mem_init(void) |
| 420 | { | 495 | { |
| 421 | unsigned long reserved_pages, free_pages; | 496 | unsigned long reserved_pages, free_pages; |
| 497 | struct memblock_region *reg; | ||
| 422 | int i; | 498 | int i; |
| 423 | #ifdef CONFIG_HAVE_TCM | 499 | #ifdef CONFIG_HAVE_TCM |
| 424 | /* These pointers are filled in on TCM detection */ | 500 | /* These pointers are filled in on TCM detection */ |
| @@ -439,16 +515,7 @@ void __init mem_init(void) | |||
| 439 | __phys_to_pfn(__pa(swapper_pg_dir)), NULL); | 515 | __phys_to_pfn(__pa(swapper_pg_dir)), NULL); |
| 440 | #endif | 516 | #endif |
| 441 | 517 | ||
| 442 | #ifdef CONFIG_HIGHMEM | 518 | free_highpages(); |
| 443 | /* set highmem page free */ | ||
| 444 | for_each_bank (i, &meminfo) { | ||
| 445 | unsigned long start = bank_pfn_start(&meminfo.bank[i]); | ||
| 446 | unsigned long end = bank_pfn_end(&meminfo.bank[i]); | ||
| 447 | if (start >= max_low_pfn + PHYS_PFN_OFFSET) | ||
| 448 | totalhigh_pages += free_area(start, end, NULL); | ||
| 449 | } | ||
| 450 | totalram_pages += totalhigh_pages; | ||
| 451 | #endif | ||
| 452 | 519 | ||
| 453 | reserved_pages = free_pages = 0; | 520 | reserved_pages = free_pages = 0; |
| 454 | 521 | ||
| @@ -478,9 +545,11 @@ void __init mem_init(void) | |||
| 478 | */ | 545 | */ |
| 479 | printk(KERN_INFO "Memory:"); | 546 | printk(KERN_INFO "Memory:"); |
| 480 | num_physpages = 0; | 547 | num_physpages = 0; |
| 481 | for (i = 0; i < meminfo.nr_banks; i++) { | 548 | for_each_memblock(memory, reg) { |
| 482 | num_physpages += bank_pfn_size(&meminfo.bank[i]); | 549 | unsigned long pages = memblock_region_memory_end_pfn(reg) - |
| 483 | printk(" %ldMB", bank_phys_size(&meminfo.bank[i]) >> 20); | 550 | memblock_region_memory_base_pfn(reg); |
| 551 | num_physpages += pages; | ||
| 552 | printk(" %ldMB", pages >> (20 - PAGE_SHIFT)); | ||
| 484 | } | 553 | } |
| 485 | printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT)); | 554 | printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT)); |
| 486 | 555 | ||
