aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2011-11-18 13:55:35 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2011-12-21 15:57:37 -0500
commit6400c8a382ded237df5dc1605f0d06e7939ff4da (patch)
treeb1c13ae760a87d9d758e0741ab0e418d8a870915
parent5341e68f9b6c25c0a062de6d730034640988a605 (diff)
percpu: fix chunk range calculation
commit a855b84c3d8c73220d4d3cd392a7bee7c83de70e upstream. Percpu allocator recorded the cpus which map to the first and last units in pcpu_first/last_unit_cpu respectively and used them to determine the address range of a chunk - e.g. it assumed that the first unit has the lowest address in a chunk while the last unit has the highest address. This simply isn't true. Groups in a chunk can have arbitrary positive or negative offsets from the previous one and there is no guarantee that the first unit occupies the lowest offset while the last one the highest. Fix it by actually comparing unit offsets to determine cpus occupying the lowest and highest offsets. Also, rename pcu_first/last_unit_cpu to pcpu_low/high_unit_cpu to avoid confusion. The chunk address range is used to flush cache on vmalloc area map/unmap and decide whether a given address is in the first chunk by per_cpu_ptr_to_phys() and the bug was discovered by invalid per_cpu_ptr_to_phys() translation for crash_note. Kudos to Dave Young for tracking down the problem. Signed-off-by: Tejun Heo <tj@kernel.org> Reported-by: WANG Cong <xiyou.wangcong@gmail.com> Reported-by: Dave Young <dyoung@redhat.com> Tested-by: Dave Young <dyoung@redhat.com> LKML-Reference: <4EC21F67.10905@redhat.com> Signed-off-by: Thomas Renninger <trenn@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--mm/percpu-vm.c12
-rw-r--r--mm/percpu.c34
2 files changed, 26 insertions, 20 deletions
diff --git a/mm/percpu-vm.c b/mm/percpu-vm.c
index ea534960a04..bfad7246665 100644
--- a/mm/percpu-vm.c
+++ b/mm/percpu-vm.c
@@ -143,8 +143,8 @@ static void pcpu_pre_unmap_flush(struct pcpu_chunk *chunk,
143 int page_start, int page_end) 143 int page_start, int page_end)
144{ 144{
145 flush_cache_vunmap( 145 flush_cache_vunmap(
146 pcpu_chunk_addr(chunk, pcpu_first_unit_cpu, page_start), 146 pcpu_chunk_addr(chunk, pcpu_low_unit_cpu, page_start),
147 pcpu_chunk_addr(chunk, pcpu_last_unit_cpu, page_end)); 147 pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
148} 148}
149 149
150static void __pcpu_unmap_pages(unsigned long addr, int nr_pages) 150static void __pcpu_unmap_pages(unsigned long addr, int nr_pages)
@@ -206,8 +206,8 @@ static void pcpu_post_unmap_tlb_flush(struct pcpu_chunk *chunk,
206 int page_start, int page_end) 206 int page_start, int page_end)
207{ 207{
208 flush_tlb_kernel_range( 208 flush_tlb_kernel_range(
209 pcpu_chunk_addr(chunk, pcpu_first_unit_cpu, page_start), 209 pcpu_chunk_addr(chunk, pcpu_low_unit_cpu, page_start),
210 pcpu_chunk_addr(chunk, pcpu_last_unit_cpu, page_end)); 210 pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
211} 211}
212 212
213static int __pcpu_map_pages(unsigned long addr, struct page **pages, 213static int __pcpu_map_pages(unsigned long addr, struct page **pages,
@@ -284,8 +284,8 @@ static void pcpu_post_map_flush(struct pcpu_chunk *chunk,
284 int page_start, int page_end) 284 int page_start, int page_end)
285{ 285{
286 flush_cache_vmap( 286 flush_cache_vmap(
287 pcpu_chunk_addr(chunk, pcpu_first_unit_cpu, page_start), 287 pcpu_chunk_addr(chunk, pcpu_low_unit_cpu, page_start),
288 pcpu_chunk_addr(chunk, pcpu_last_unit_cpu, page_end)); 288 pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
289} 289}
290 290
291/** 291/**
diff --git a/mm/percpu.c b/mm/percpu.c
index bf80e55dbed..93b5a7c96a7 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -116,9 +116,9 @@ static int pcpu_atom_size __read_mostly;
116static int pcpu_nr_slots __read_mostly; 116static int pcpu_nr_slots __read_mostly;
117static size_t pcpu_chunk_struct_size __read_mostly; 117static size_t pcpu_chunk_struct_size __read_mostly;
118 118
119/* cpus with the lowest and highest unit numbers */ 119/* cpus with the lowest and highest unit addresses */
120static unsigned int pcpu_first_unit_cpu __read_mostly; 120static unsigned int pcpu_low_unit_cpu __read_mostly;
121static unsigned int pcpu_last_unit_cpu __read_mostly; 121static unsigned int pcpu_high_unit_cpu __read_mostly;
122 122
123/* the address of the first chunk which starts with the kernel static area */ 123/* the address of the first chunk which starts with the kernel static area */
124void *pcpu_base_addr __read_mostly; 124void *pcpu_base_addr __read_mostly;
@@ -984,19 +984,19 @@ phys_addr_t per_cpu_ptr_to_phys(void *addr)
984{ 984{
985 void __percpu *base = __addr_to_pcpu_ptr(pcpu_base_addr); 985 void __percpu *base = __addr_to_pcpu_ptr(pcpu_base_addr);
986 bool in_first_chunk = false; 986 bool in_first_chunk = false;
987 unsigned long first_start, first_end; 987 unsigned long first_low, first_high;
988 unsigned int cpu; 988 unsigned int cpu;
989 989
990 /* 990 /*
991 * The following test on first_start/end isn't strictly 991 * The following test on unit_low/high isn't strictly
992 * necessary but will speed up lookups of addresses which 992 * necessary but will speed up lookups of addresses which
993 * aren't in the first chunk. 993 * aren't in the first chunk.
994 */ 994 */
995 first_start = pcpu_chunk_addr(pcpu_first_chunk, pcpu_first_unit_cpu, 0); 995 first_low = pcpu_chunk_addr(pcpu_first_chunk, pcpu_low_unit_cpu, 0);
996 first_end = pcpu_chunk_addr(pcpu_first_chunk, pcpu_last_unit_cpu, 996 first_high = pcpu_chunk_addr(pcpu_first_chunk, pcpu_high_unit_cpu,
997 pcpu_unit_pages); 997 pcpu_unit_pages);
998 if ((unsigned long)addr >= first_start && 998 if ((unsigned long)addr >= first_low &&
999 (unsigned long)addr < first_end) { 999 (unsigned long)addr < first_high) {
1000 for_each_possible_cpu(cpu) { 1000 for_each_possible_cpu(cpu) {
1001 void *start = per_cpu_ptr(base, cpu); 1001 void *start = per_cpu_ptr(base, cpu);
1002 1002
@@ -1233,7 +1233,9 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
1233 1233
1234 for (cpu = 0; cpu < nr_cpu_ids; cpu++) 1234 for (cpu = 0; cpu < nr_cpu_ids; cpu++)
1235 unit_map[cpu] = UINT_MAX; 1235 unit_map[cpu] = UINT_MAX;
1236 pcpu_first_unit_cpu = NR_CPUS; 1236
1237 pcpu_low_unit_cpu = NR_CPUS;
1238 pcpu_high_unit_cpu = NR_CPUS;
1237 1239
1238 for (group = 0, unit = 0; group < ai->nr_groups; group++, unit += i) { 1240 for (group = 0, unit = 0; group < ai->nr_groups; group++, unit += i) {
1239 const struct pcpu_group_info *gi = &ai->groups[group]; 1241 const struct pcpu_group_info *gi = &ai->groups[group];
@@ -1253,9 +1255,13 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
1253 unit_map[cpu] = unit + i; 1255 unit_map[cpu] = unit + i;
1254 unit_off[cpu] = gi->base_offset + i * ai->unit_size; 1256 unit_off[cpu] = gi->base_offset + i * ai->unit_size;
1255 1257
1256 if (pcpu_first_unit_cpu == NR_CPUS) 1258 /* determine low/high unit_cpu */
1257 pcpu_first_unit_cpu = cpu; 1259 if (pcpu_low_unit_cpu == NR_CPUS ||
1258 pcpu_last_unit_cpu = cpu; 1260 unit_off[cpu] < unit_off[pcpu_low_unit_cpu])
1261 pcpu_low_unit_cpu = cpu;
1262 if (pcpu_high_unit_cpu == NR_CPUS ||
1263 unit_off[cpu] > unit_off[pcpu_high_unit_cpu])
1264 pcpu_high_unit_cpu = cpu;
1259 } 1265 }
1260 } 1266 }
1261 pcpu_nr_units = unit; 1267 pcpu_nr_units = unit;