diff options
Diffstat (limited to 'arch/powerpc/mm/numa.c')
-rw-r--r-- | arch/powerpc/mm/numa.c | 185 |
1 files changed, 141 insertions, 44 deletions
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index d9a181351332..6cf5c71c431f 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c | |||
@@ -89,6 +89,46 @@ static int __cpuinit fake_numa_create_new_node(unsigned long end_pfn, | |||
89 | return 0; | 89 | return 0; |
90 | } | 90 | } |
91 | 91 | ||
92 | /* | ||
93 | * get_active_region_work_fn - A helper function for get_node_active_region | ||
94 | * Returns datax set to the start_pfn and end_pfn if they contain | ||
95 | * the initial value of datax->start_pfn between them | ||
96 | * @start_pfn: start page(inclusive) of region to check | ||
97 | * @end_pfn: end page(exclusive) of region to check | ||
98 | * @datax: comes in with ->start_pfn set to value to search for and | ||
99 | * goes out with active range if it contains it | ||
100 | * Returns 1 if search value is in range else 0 | ||
101 | */ | ||
102 | static int __init get_active_region_work_fn(unsigned long start_pfn, | ||
103 | unsigned long end_pfn, void *datax) | ||
104 | { | ||
105 | struct node_active_region *data; | ||
106 | data = (struct node_active_region *)datax; | ||
107 | |||
108 | if (start_pfn <= data->start_pfn && end_pfn > data->start_pfn) { | ||
109 | data->start_pfn = start_pfn; | ||
110 | data->end_pfn = end_pfn; | ||
111 | return 1; | ||
112 | } | ||
113 | return 0; | ||
114 | |||
115 | } | ||
116 | |||
117 | /* | ||
118 | * get_node_active_region - Return active region containing start_pfn | ||
119 | * @start_pfn: The page to return the region for. | ||
120 | * @node_ar: Returned set to the active region containing start_pfn | ||
121 | */ | ||
122 | static void __init get_node_active_region(unsigned long start_pfn, | ||
123 | struct node_active_region *node_ar) | ||
124 | { | ||
125 | int nid = early_pfn_to_nid(start_pfn); | ||
126 | |||
127 | node_ar->nid = nid; | ||
128 | node_ar->start_pfn = start_pfn; | ||
129 | work_with_active_regions(nid, get_active_region_work_fn, node_ar); | ||
130 | } | ||
131 | |||
92 | static void __cpuinit map_cpu_to_node(int cpu, int node) | 132 | static void __cpuinit map_cpu_to_node(int cpu, int node) |
93 | { | 133 | { |
94 | numa_cpu_lookup_table[cpu] = node; | 134 | numa_cpu_lookup_table[cpu] = node; |
@@ -150,6 +190,21 @@ static const int *of_get_associativity(struct device_node *dev) | |||
150 | return of_get_property(dev, "ibm,associativity", NULL); | 190 | return of_get_property(dev, "ibm,associativity", NULL); |
151 | } | 191 | } |
152 | 192 | ||
193 | /* | ||
194 | * Returns the property linux,drconf-usable-memory if | ||
195 | * it exists (the property exists only in kexec/kdump kernels, | ||
196 | * added by kexec-tools) | ||
197 | */ | ||
198 | static const u32 *of_get_usable_memory(struct device_node *memory) | ||
199 | { | ||
200 | const u32 *prop; | ||
201 | u32 len; | ||
202 | prop = of_get_property(memory, "linux,drconf-usable-memory", &len); | ||
203 | if (!prop || len < sizeof(unsigned int)) | ||
204 | return 0; | ||
205 | return prop; | ||
206 | } | ||
207 | |||
153 | /* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa | 208 | /* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa |
154 | * info is found. | 209 | * info is found. |
155 | */ | 210 | */ |
@@ -487,14 +542,29 @@ static unsigned long __init numa_enforce_memory_limit(unsigned long start, | |||
487 | } | 542 | } |
488 | 543 | ||
489 | /* | 544 | /* |
545 | * Reads the counter for a given entry in | ||
546 | * linux,drconf-usable-memory property | ||
547 | */ | ||
548 | static inline int __init read_usm_ranges(const u32 **usm) | ||
549 | { | ||
550 | /* | ||
551 | * For each lmb in ibm,dynamic-memory a corresponding | ||
552 | * entry in linux,drconf-usable-memory property contains | ||
553 | * a counter followed by that many (base, size) duple. | ||
554 | * read the counter from linux,drconf-usable-memory | ||
555 | */ | ||
556 | return read_n_cells(n_mem_size_cells, usm); | ||
557 | } | ||
558 | |||
559 | /* | ||
490 | * Extract NUMA information from the ibm,dynamic-reconfiguration-memory | 560 | * Extract NUMA information from the ibm,dynamic-reconfiguration-memory |
491 | * node. This assumes n_mem_{addr,size}_cells have been set. | 561 | * node. This assumes n_mem_{addr,size}_cells have been set. |
492 | */ | 562 | */ |
493 | static void __init parse_drconf_memory(struct device_node *memory) | 563 | static void __init parse_drconf_memory(struct device_node *memory) |
494 | { | 564 | { |
495 | const u32 *dm; | 565 | const u32 *dm, *usm; |
496 | unsigned int n, rc; | 566 | unsigned int n, rc, ranges, is_kexec_kdump = 0; |
497 | unsigned long lmb_size, size; | 567 | unsigned long lmb_size, base, size, sz; |
498 | int nid; | 568 | int nid; |
499 | struct assoc_arrays aa; | 569 | struct assoc_arrays aa; |
500 | 570 | ||
@@ -510,6 +580,11 @@ static void __init parse_drconf_memory(struct device_node *memory) | |||
510 | if (rc) | 580 | if (rc) |
511 | return; | 581 | return; |
512 | 582 | ||
583 | /* check if this is a kexec/kdump kernel */ | ||
584 | usm = of_get_usable_memory(memory); | ||
585 | if (usm != NULL) | ||
586 | is_kexec_kdump = 1; | ||
587 | |||
513 | for (; n != 0; --n) { | 588 | for (; n != 0; --n) { |
514 | struct of_drconf_cell drmem; | 589 | struct of_drconf_cell drmem; |
515 | 590 | ||
@@ -521,21 +596,31 @@ static void __init parse_drconf_memory(struct device_node *memory) | |||
521 | || !(drmem.flags & DRCONF_MEM_ASSIGNED)) | 596 | || !(drmem.flags & DRCONF_MEM_ASSIGNED)) |
522 | continue; | 597 | continue; |
523 | 598 | ||
524 | nid = of_drconf_to_nid_single(&drmem, &aa); | 599 | base = drmem.base_addr; |
600 | size = lmb_size; | ||
601 | ranges = 1; | ||
525 | 602 | ||
526 | fake_numa_create_new_node( | 603 | if (is_kexec_kdump) { |
527 | ((drmem.base_addr + lmb_size) >> PAGE_SHIFT), | 604 | ranges = read_usm_ranges(&usm); |
605 | if (!ranges) /* there are no (base, size) duple */ | ||
606 | continue; | ||
607 | } | ||
608 | do { | ||
609 | if (is_kexec_kdump) { | ||
610 | base = read_n_cells(n_mem_addr_cells, &usm); | ||
611 | size = read_n_cells(n_mem_size_cells, &usm); | ||
612 | } | ||
613 | nid = of_drconf_to_nid_single(&drmem, &aa); | ||
614 | fake_numa_create_new_node( | ||
615 | ((base + size) >> PAGE_SHIFT), | ||
528 | &nid); | 616 | &nid); |
529 | 617 | node_set_online(nid); | |
530 | node_set_online(nid); | 618 | sz = numa_enforce_memory_limit(base, size); |
531 | 619 | if (sz) | |
532 | size = numa_enforce_memory_limit(drmem.base_addr, lmb_size); | 620 | add_active_range(nid, base >> PAGE_SHIFT, |
533 | if (!size) | 621 | (base >> PAGE_SHIFT) |
534 | continue; | 622 | + (sz >> PAGE_SHIFT)); |
535 | 623 | } while (--ranges); | |
536 | add_active_range(nid, drmem.base_addr >> PAGE_SHIFT, | ||
537 | (drmem.base_addr >> PAGE_SHIFT) | ||
538 | + (size >> PAGE_SHIFT)); | ||
539 | } | 624 | } |
540 | } | 625 | } |
541 | 626 | ||
@@ -837,38 +922,50 @@ void __init do_init_bootmem(void) | |||
837 | start_pfn, end_pfn); | 922 | start_pfn, end_pfn); |
838 | 923 | ||
839 | free_bootmem_with_active_regions(nid, end_pfn); | 924 | free_bootmem_with_active_regions(nid, end_pfn); |
925 | } | ||
840 | 926 | ||
841 | /* Mark reserved regions on this node */ | 927 | /* Mark reserved regions */ |
842 | for (i = 0; i < lmb.reserved.cnt; i++) { | 928 | for (i = 0; i < lmb.reserved.cnt; i++) { |
843 | unsigned long physbase = lmb.reserved.region[i].base; | 929 | unsigned long physbase = lmb.reserved.region[i].base; |
844 | unsigned long size = lmb.reserved.region[i].size; | 930 | unsigned long size = lmb.reserved.region[i].size; |
845 | unsigned long start_paddr = start_pfn << PAGE_SHIFT; | 931 | unsigned long start_pfn = physbase >> PAGE_SHIFT; |
846 | unsigned long end_paddr = end_pfn << PAGE_SHIFT; | 932 | unsigned long end_pfn = ((physbase + size) >> PAGE_SHIFT); |
847 | 933 | struct node_active_region node_ar; | |
848 | if (early_pfn_to_nid(physbase >> PAGE_SHIFT) != nid && | 934 | |
849 | early_pfn_to_nid((physbase+size-1) >> PAGE_SHIFT) != nid) | 935 | get_node_active_region(start_pfn, &node_ar); |
850 | continue; | 936 | while (start_pfn < end_pfn) { |
851 | 937 | /* | |
852 | if (physbase < end_paddr && | 938 | * if reserved region extends past active region |
853 | (physbase+size) > start_paddr) { | 939 | * then trim size to active region |
854 | /* overlaps */ | 940 | */ |
855 | if (physbase < start_paddr) { | 941 | if (end_pfn > node_ar.end_pfn) |
856 | size -= start_paddr - physbase; | 942 | size = (node_ar.end_pfn << PAGE_SHIFT) |
857 | physbase = start_paddr; | 943 | - (start_pfn << PAGE_SHIFT); |
858 | } | 944 | dbg("reserve_bootmem %lx %lx nid=%d\n", physbase, size, |
859 | 945 | node_ar.nid); | |
860 | if (size > end_paddr - physbase) | 946 | reserve_bootmem_node(NODE_DATA(node_ar.nid), physbase, |
861 | size = end_paddr - physbase; | 947 | size, BOOTMEM_DEFAULT); |
862 | 948 | /* | |
863 | dbg("reserve_bootmem %lx %lx\n", physbase, | 949 | * if reserved region is contained in the active region |
864 | size); | 950 | * then done. |
865 | reserve_bootmem_node(NODE_DATA(nid), physbase, | 951 | */ |
866 | size, BOOTMEM_DEFAULT); | 952 | if (end_pfn <= node_ar.end_pfn) |
867 | } | 953 | break; |
954 | |||
955 | /* | ||
956 | * reserved region extends past the active region | ||
957 | * get next active region that contains this | ||
958 | * reserved region | ||
959 | */ | ||
960 | start_pfn = node_ar.end_pfn; | ||
961 | physbase = start_pfn << PAGE_SHIFT; | ||
962 | get_node_active_region(start_pfn, &node_ar); | ||
868 | } | 963 | } |
869 | 964 | ||
870 | sparse_memory_present_with_active_regions(nid); | ||
871 | } | 965 | } |
966 | |||
967 | for_each_online_node(nid) | ||
968 | sparse_memory_present_with_active_regions(nid); | ||
872 | } | 969 | } |
873 | 970 | ||
874 | void __init paging_init(void) | 971 | void __init paging_init(void) |