diff options
author | Tejun Heo <tj@kernel.org> | 2011-07-14 05:43:42 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2011-07-14 14:47:43 -0400 |
commit | 7c0caeb866b0f648d91bb75b8bc6f86af95bb033 (patch) | |
tree | 042804fe716310a4de4effbbaa4461237e2b5d4a /mm/memblock.c | |
parent | 67e24bcb725cabd15ef577bf301275d03d6086d7 (diff) |
memblock: Add optional region->nid
From 83103b92f3234ec830852bbc5c45911bd6cbdb20 Mon Sep 17 00:00:00 2001
From: Tejun Heo <tj@kernel.org>
Date: Thu, 14 Jul 2011 11:22:16 +0200
Add optional region->nid which can be enabled by arch using
CONFIG_HAVE_MEMBLOCK_NODE_MAP. When enabled, memblock also carries
NUMA node information and replaces early_node_map[].
Newly added memblocks have MAX_NUMNODES as nid. Arch can then call
memblock_set_node() to set node information. memblock takes care of
merging and node affine allocations w.r.t. node information.
When MEMBLOCK_NODE_MAP is enabled, early_node_map[], related data
structures and functions to manipulate and iterate it are disabled.
memblock version of __next_mem_pfn_range() is provided such that
for_each_mem_pfn_range() behaves the same and its users don't have to
be updated.
-v2: Yinghai spotted section mismatch caused by missing
__init_memblock in memblock_set_node(). Fixed.
Signed-off-by: Tejun Heo <tj@kernel.org>
Link: http://lkml.kernel.org/r/20110714094342.GF3455@htj.dyndns.org
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'mm/memblock.c')
-rw-r--r-- | mm/memblock.c | 142 |
1 files changed, 124 insertions, 18 deletions
diff --git a/mm/memblock.c b/mm/memblock.c index 992aa1807473..e815f4b75809 100644 --- a/mm/memblock.c +++ b/mm/memblock.c | |||
@@ -161,12 +161,8 @@ int __init_memblock memblock_reserve_reserved_regions(void) | |||
161 | 161 | ||
162 | static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r) | 162 | static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r) |
163 | { | 163 | { |
164 | unsigned long i; | 164 | memmove(&type->regions[r], &type->regions[r + 1], |
165 | 165 | (type->cnt - (r + 1)) * sizeof(type->regions[r])); | |
166 | for (i = r; i < type->cnt - 1; i++) { | ||
167 | type->regions[i].base = type->regions[i + 1].base; | ||
168 | type->regions[i].size = type->regions[i + 1].size; | ||
169 | } | ||
170 | type->cnt--; | 166 | type->cnt--; |
171 | 167 | ||
172 | /* Special case for empty arrays */ | 168 | /* Special case for empty arrays */ |
@@ -174,6 +170,7 @@ static void __init_memblock memblock_remove_region(struct memblock_type *type, u | |||
174 | type->cnt = 1; | 170 | type->cnt = 1; |
175 | type->regions[0].base = 0; | 171 | type->regions[0].base = 0; |
176 | type->regions[0].size = 0; | 172 | type->regions[0].size = 0; |
173 | memblock_set_region_node(&type->regions[0], MAX_NUMNODES); | ||
177 | } | 174 | } |
178 | } | 175 | } |
179 | 176 | ||
@@ -266,7 +263,9 @@ static void __init_memblock memblock_merge_regions(struct memblock_type *type) | |||
266 | struct memblock_region *this = &type->regions[i]; | 263 | struct memblock_region *this = &type->regions[i]; |
267 | struct memblock_region *next = &type->regions[i + 1]; | 264 | struct memblock_region *next = &type->regions[i + 1]; |
268 | 265 | ||
269 | if (this->base + this->size != next->base) { | 266 | if (this->base + this->size != next->base || |
267 | memblock_get_region_node(this) != | ||
268 | memblock_get_region_node(next)) { | ||
270 | BUG_ON(this->base + this->size > next->base); | 269 | BUG_ON(this->base + this->size > next->base); |
271 | i++; | 270 | i++; |
272 | continue; | 271 | continue; |
@@ -290,7 +289,7 @@ static void __init_memblock memblock_merge_regions(struct memblock_type *type) | |||
290 | */ | 289 | */ |
291 | static void __init_memblock memblock_insert_region(struct memblock_type *type, | 290 | static void __init_memblock memblock_insert_region(struct memblock_type *type, |
292 | int idx, phys_addr_t base, | 291 | int idx, phys_addr_t base, |
293 | phys_addr_t size) | 292 | phys_addr_t size, int nid) |
294 | { | 293 | { |
295 | struct memblock_region *rgn = &type->regions[idx]; | 294 | struct memblock_region *rgn = &type->regions[idx]; |
296 | 295 | ||
@@ -298,6 +297,7 @@ static void __init_memblock memblock_insert_region(struct memblock_type *type, | |||
298 | memmove(rgn + 1, rgn, (type->cnt - idx) * sizeof(*rgn)); | 297 | memmove(rgn + 1, rgn, (type->cnt - idx) * sizeof(*rgn)); |
299 | rgn->base = base; | 298 | rgn->base = base; |
300 | rgn->size = size; | 299 | rgn->size = size; |
300 | memblock_set_region_node(rgn, nid); | ||
301 | type->cnt++; | 301 | type->cnt++; |
302 | } | 302 | } |
303 | 303 | ||
@@ -327,6 +327,7 @@ static long __init_memblock memblock_add_region(struct memblock_type *type, | |||
327 | WARN_ON(type->cnt != 1); | 327 | WARN_ON(type->cnt != 1); |
328 | type->regions[0].base = base; | 328 | type->regions[0].base = base; |
329 | type->regions[0].size = size; | 329 | type->regions[0].size = size; |
330 | memblock_set_region_node(&type->regions[0], MAX_NUMNODES); | ||
330 | return 0; | 331 | return 0; |
331 | } | 332 | } |
332 | repeat: | 333 | repeat: |
@@ -355,7 +356,7 @@ repeat: | |||
355 | nr_new++; | 356 | nr_new++; |
356 | if (insert) | 357 | if (insert) |
357 | memblock_insert_region(type, i++, base, | 358 | memblock_insert_region(type, i++, base, |
358 | rbase - base); | 359 | rbase - base, MAX_NUMNODES); |
359 | } | 360 | } |
360 | /* area below @rend is dealt with, forget about it */ | 361 | /* area below @rend is dealt with, forget about it */ |
361 | base = min(rend, end); | 362 | base = min(rend, end); |
@@ -365,7 +366,8 @@ repeat: | |||
365 | if (base < end) { | 366 | if (base < end) { |
366 | nr_new++; | 367 | nr_new++; |
367 | if (insert) | 368 | if (insert) |
368 | memblock_insert_region(type, i, base, end - base); | 369 | memblock_insert_region(type, i, base, end - base, |
370 | MAX_NUMNODES); | ||
369 | } | 371 | } |
370 | 372 | ||
371 | /* | 373 | /* |
@@ -459,6 +461,101 @@ long __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size) | |||
459 | return memblock_add_region(_rgn, base, size); | 461 | return memblock_add_region(_rgn, base, size); |
460 | } | 462 | } |
461 | 463 | ||
464 | #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP | ||
465 | /* | ||
466 | * Common iterator interface used to define for_each_mem_range(). | ||
467 | */ | ||
468 | void __init_memblock __next_mem_pfn_range(int *idx, int nid, | ||
469 | unsigned long *out_start_pfn, | ||
470 | unsigned long *out_end_pfn, int *out_nid) | ||
471 | { | ||
472 | struct memblock_type *type = &memblock.memory; | ||
473 | struct memblock_region *r; | ||
474 | |||
475 | while (++*idx < type->cnt) { | ||
476 | r = &type->regions[*idx]; | ||
477 | |||
478 | if (PFN_UP(r->base) >= PFN_DOWN(r->base + r->size)) | ||
479 | continue; | ||
480 | if (nid == MAX_NUMNODES || nid == r->nid) | ||
481 | break; | ||
482 | } | ||
483 | if (*idx >= type->cnt) { | ||
484 | *idx = -1; | ||
485 | return; | ||
486 | } | ||
487 | |||
488 | if (out_start_pfn) | ||
489 | *out_start_pfn = PFN_UP(r->base); | ||
490 | if (out_end_pfn) | ||
491 | *out_end_pfn = PFN_DOWN(r->base + r->size); | ||
492 | if (out_nid) | ||
493 | *out_nid = r->nid; | ||
494 | } | ||
495 | |||
496 | /** | ||
497 | * memblock_set_node - set node ID on memblock regions | ||
498 | * @base: base of area to set node ID for | ||
499 | * @size: size of area to set node ID for | ||
500 | * @nid: node ID to set | ||
501 | * | ||
502 | * Set the nid of memblock memory regions in [@base,@base+@size) to @nid. | ||
503 | * Regions which cross the area boundaries are split as necessary. | ||
504 | * | ||
505 | * RETURNS: | ||
506 | * 0 on success, -errno on failure. | ||
507 | */ | ||
508 | int __init_memblock memblock_set_node(phys_addr_t base, phys_addr_t size, | ||
509 | int nid) | ||
510 | { | ||
511 | struct memblock_type *type = &memblock.memory; | ||
512 | phys_addr_t end = base + size; | ||
513 | int i; | ||
514 | |||
515 | /* we'll create at most two more regions */ | ||
516 | while (type->cnt + 2 > type->max) | ||
517 | if (memblock_double_array(type) < 0) | ||
518 | return -ENOMEM; | ||
519 | |||
520 | for (i = 0; i < type->cnt; i++) { | ||
521 | struct memblock_region *rgn = &type->regions[i]; | ||
522 | phys_addr_t rbase = rgn->base; | ||
523 | phys_addr_t rend = rbase + rgn->size; | ||
524 | |||
525 | if (rbase >= end) | ||
526 | break; | ||
527 | if (rend <= base) | ||
528 | continue; | ||
529 | |||
530 | if (rbase < base) { | ||
531 | /* | ||
532 | * @rgn intersects from below. Split and continue | ||
533 | * to process the next region - the new top half. | ||
534 | */ | ||
535 | rgn->base = base; | ||
536 | rgn->size = rend - rgn->base; | ||
537 | memblock_insert_region(type, i, rbase, base - rbase, | ||
538 | rgn->nid); | ||
539 | } else if (rend > end) { | ||
540 | /* | ||
541 | * @rgn intersects from above. Split and redo the | ||
542 | * current region - the new bottom half. | ||
543 | */ | ||
544 | rgn->base = end; | ||
545 | rgn->size = rend - rgn->base; | ||
546 | memblock_insert_region(type, i--, rbase, end - rbase, | ||
547 | rgn->nid); | ||
548 | } else { | ||
549 | /* @rgn is fully contained, set ->nid */ | ||
550 | rgn->nid = nid; | ||
551 | } | ||
552 | } | ||
553 | |||
554 | memblock_merge_regions(type); | ||
555 | return 0; | ||
556 | } | ||
557 | #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ | ||
558 | |||
462 | phys_addr_t __init __memblock_alloc_base(phys_addr_t size, phys_addr_t align, phys_addr_t max_addr) | 559 | phys_addr_t __init __memblock_alloc_base(phys_addr_t size, phys_addr_t align, phys_addr_t max_addr) |
463 | { | 560 | { |
464 | phys_addr_t found; | 561 | phys_addr_t found; |
@@ -689,19 +786,26 @@ void __init_memblock memblock_set_current_limit(phys_addr_t limit) | |||
689 | memblock.current_limit = limit; | 786 | memblock.current_limit = limit; |
690 | } | 787 | } |
691 | 788 | ||
692 | static void __init_memblock memblock_dump(struct memblock_type *region, char *name) | 789 | static void __init_memblock memblock_dump(struct memblock_type *type, char *name) |
693 | { | 790 | { |
694 | unsigned long long base, size; | 791 | unsigned long long base, size; |
695 | int i; | 792 | int i; |
696 | 793 | ||
697 | pr_info(" %s.cnt = 0x%lx\n", name, region->cnt); | 794 | pr_info(" %s.cnt = 0x%lx\n", name, type->cnt); |
698 | |||
699 | for (i = 0; i < region->cnt; i++) { | ||
700 | base = region->regions[i].base; | ||
701 | size = region->regions[i].size; | ||
702 | 795 | ||
703 | pr_info(" %s[%#x]\t[%#016llx-%#016llx], %#llx bytes\n", | 796 | for (i = 0; i < type->cnt; i++) { |
704 | name, i, base, base + size - 1, size); | 797 | struct memblock_region *rgn = &type->regions[i]; |
798 | char nid_buf[32] = ""; | ||
799 | |||
800 | base = rgn->base; | ||
801 | size = rgn->size; | ||
802 | #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP | ||
803 | if (memblock_get_region_node(rgn) != MAX_NUMNODES) | ||
804 | snprintf(nid_buf, sizeof(nid_buf), " on node %d", | ||
805 | memblock_get_region_node(rgn)); | ||
806 | #endif | ||
807 | pr_info(" %s[%#x]\t[%#016llx-%#016llx], %#llx bytes%s\n", | ||
808 | name, i, base, base + size - 1, size, nid_buf); | ||
705 | } | 809 | } |
706 | } | 810 | } |
707 | 811 | ||
@@ -759,11 +863,13 @@ void __init memblock_init(void) | |||
759 | */ | 863 | */ |
760 | memblock.memory.regions[0].base = 0; | 864 | memblock.memory.regions[0].base = 0; |
761 | memblock.memory.regions[0].size = 0; | 865 | memblock.memory.regions[0].size = 0; |
866 | memblock_set_region_node(&memblock.memory.regions[0], MAX_NUMNODES); | ||
762 | memblock.memory.cnt = 1; | 867 | memblock.memory.cnt = 1; |
763 | 868 | ||
764 | /* Ditto. */ | 869 | /* Ditto. */ |
765 | memblock.reserved.regions[0].base = 0; | 870 | memblock.reserved.regions[0].base = 0; |
766 | memblock.reserved.regions[0].size = 0; | 871 | memblock.reserved.regions[0].size = 0; |
872 | memblock_set_region_node(&memblock.reserved.regions[0], MAX_NUMNODES); | ||
767 | memblock.reserved.cnt = 1; | 873 | memblock.reserved.cnt = 1; |
768 | 874 | ||
769 | memblock.current_limit = MEMBLOCK_ALLOC_ANYWHERE; | 875 | memblock.current_limit = MEMBLOCK_ALLOC_ANYWHERE; |