diff options
author | Tejun Heo <tj@kernel.org> | 2009-07-21 04:11:50 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2009-08-14 00:20:53 -0400 |
commit | 74d46d6b2d23d44d72c37df4c6a5d2e782f7b088 (patch) | |
tree | bcc468968e738394b9c4a44b8936a0e6037be163 /mm | |
parent | d6647bdf98a0de19963de8d5d9698d469ed72097 (diff) |
percpu, sparc64: fix sparse possible cpu map handling
percpu code has been assuming num_possible_cpus() == nr_cpu_ids which
is incorrect if cpu_possible_map contains holes. This causes percpu
code to access beyond allocated memories and vmalloc areas. On a
sparc64 machine with cpus 0 and 2 (u60), this triggers the following
warning or fails boot.
WARNING: at /devel/tj/os/work/mm/vmalloc.c:106 vmap_page_range_noflush+0x1f0/0x240()
Modules linked in:
Call Trace:
[00000000004b17d0] vmap_page_range_noflush+0x1f0/0x240
[00000000004b1840] map_vm_area+0x20/0x60
[00000000004b1950] __vmalloc_area_node+0xd0/0x160
[0000000000593434] deflate_init+0x14/0xe0
[0000000000583b94] __crypto_alloc_tfm+0xd4/0x1e0
[00000000005844f0] crypto_alloc_base+0x50/0xa0
[000000000058b898] alg_test_comp+0x18/0x80
[000000000058dad4] alg_test+0x54/0x180
[000000000058af00] cryptomgr_test+0x40/0x60
[0000000000473098] kthread+0x58/0x80
[000000000042b590] kernel_thread+0x30/0x60
[0000000000472fd0] kthreadd+0xf0/0x160
---[ end trace 429b268a213317ba ]---
This patch fixes generic percpu functions and sparc64
setup_per_cpu_areas() so that they handle sparse cpu_possible_map
properly.
Please note that on x86, cpu_possible_map() doesn't contain holes and
thus num_possible_cpus() == nr_cpu_ids and this patch doesn't cause
any behavior difference.
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: David S. Miller <davem@davemloft.net>
Cc: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/percpu.c | 33 |
1 files changed, 18 insertions, 15 deletions
diff --git a/mm/percpu.c b/mm/percpu.c index b70f2acd8853..e0be1146f617 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
@@ -8,12 +8,12 @@ | |||
8 | * | 8 | * |
9 | * This is percpu allocator which can handle both static and dynamic | 9 | * This is percpu allocator which can handle both static and dynamic |
10 | * areas. Percpu areas are allocated in chunks in vmalloc area. Each | 10 | * areas. Percpu areas are allocated in chunks in vmalloc area. Each |
11 | * chunk is consisted of num_possible_cpus() units and the first chunk | 11 | * chunk is consisted of nr_cpu_ids units and the first chunk is used |
12 | * is used for static percpu variables in the kernel image (special | 12 | * for static percpu variables in the kernel image (special boot time |
13 | * boot time alloc/init handling necessary as these areas need to be | 13 | * alloc/init handling necessary as these areas need to be brought up |
14 | * brought up before allocation services are running). Unit grows as | 14 | * before allocation services are running). Unit grows as necessary |
15 | * necessary and all units grow or shrink in unison. When a chunk is | 15 | * and all units grow or shrink in unison. When a chunk is filled up, |
16 | * filled up, another chunk is allocated. ie. in vmalloc area | 16 | * another chunk is allocated. ie. in vmalloc area |
17 | * | 17 | * |
18 | * c0 c1 c2 | 18 | * c0 c1 c2 |
19 | * ------------------- ------------------- ------------ | 19 | * ------------------- ------------------- ------------ |
@@ -558,7 +558,7 @@ static void pcpu_free_area(struct pcpu_chunk *chunk, int freeme) | |||
558 | static void pcpu_unmap(struct pcpu_chunk *chunk, int page_start, int page_end, | 558 | static void pcpu_unmap(struct pcpu_chunk *chunk, int page_start, int page_end, |
559 | bool flush_tlb) | 559 | bool flush_tlb) |
560 | { | 560 | { |
561 | unsigned int last = num_possible_cpus() - 1; | 561 | unsigned int last = nr_cpu_ids - 1; |
562 | unsigned int cpu; | 562 | unsigned int cpu; |
563 | 563 | ||
564 | /* unmap must not be done on immutable chunk */ | 564 | /* unmap must not be done on immutable chunk */ |
@@ -643,7 +643,7 @@ static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk, int off, int size, | |||
643 | */ | 643 | */ |
644 | static int pcpu_map(struct pcpu_chunk *chunk, int page_start, int page_end) | 644 | static int pcpu_map(struct pcpu_chunk *chunk, int page_start, int page_end) |
645 | { | 645 | { |
646 | unsigned int last = num_possible_cpus() - 1; | 646 | unsigned int last = nr_cpu_ids - 1; |
647 | unsigned int cpu; | 647 | unsigned int cpu; |
648 | int err; | 648 | int err; |
649 | 649 | ||
@@ -1067,9 +1067,9 @@ size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, | |||
1067 | PFN_UP(size_sum)); | 1067 | PFN_UP(size_sum)); |
1068 | 1068 | ||
1069 | pcpu_unit_size = pcpu_unit_pages << PAGE_SHIFT; | 1069 | pcpu_unit_size = pcpu_unit_pages << PAGE_SHIFT; |
1070 | pcpu_chunk_size = num_possible_cpus() * pcpu_unit_size; | 1070 | pcpu_chunk_size = nr_cpu_ids * pcpu_unit_size; |
1071 | pcpu_chunk_struct_size = sizeof(struct pcpu_chunk) | 1071 | pcpu_chunk_struct_size = sizeof(struct pcpu_chunk) |
1072 | + num_possible_cpus() * pcpu_unit_pages * sizeof(struct page *); | 1072 | + nr_cpu_ids * pcpu_unit_pages * sizeof(struct page *); |
1073 | 1073 | ||
1074 | if (dyn_size < 0) | 1074 | if (dyn_size < 0) |
1075 | dyn_size = pcpu_unit_size - static_size - reserved_size; | 1075 | dyn_size = pcpu_unit_size - static_size - reserved_size; |
@@ -1248,7 +1248,7 @@ ssize_t __init pcpu_embed_first_chunk(size_t static_size, size_t reserved_size, | |||
1248 | } else | 1248 | } else |
1249 | pcpue_unit_size = max_t(size_t, pcpue_size, PCPU_MIN_UNIT_SIZE); | 1249 | pcpue_unit_size = max_t(size_t, pcpue_size, PCPU_MIN_UNIT_SIZE); |
1250 | 1250 | ||
1251 | chunk_size = pcpue_unit_size * num_possible_cpus(); | 1251 | chunk_size = pcpue_unit_size * nr_cpu_ids; |
1252 | 1252 | ||
1253 | pcpue_ptr = __alloc_bootmem_nopanic(chunk_size, PAGE_SIZE, | 1253 | pcpue_ptr = __alloc_bootmem_nopanic(chunk_size, PAGE_SIZE, |
1254 | __pa(MAX_DMA_ADDRESS)); | 1254 | __pa(MAX_DMA_ADDRESS)); |
@@ -1259,12 +1259,15 @@ ssize_t __init pcpu_embed_first_chunk(size_t static_size, size_t reserved_size, | |||
1259 | } | 1259 | } |
1260 | 1260 | ||
1261 | /* return the leftover and copy */ | 1261 | /* return the leftover and copy */ |
1262 | for_each_possible_cpu(cpu) { | 1262 | for (cpu = 0; cpu < nr_cpu_ids; cpu++) { |
1263 | void *ptr = pcpue_ptr + cpu * pcpue_unit_size; | 1263 | void *ptr = pcpue_ptr + cpu * pcpue_unit_size; |
1264 | 1264 | ||
1265 | free_bootmem(__pa(ptr + pcpue_size), | 1265 | if (cpu_possible(cpu)) { |
1266 | pcpue_unit_size - pcpue_size); | 1266 | free_bootmem(__pa(ptr + pcpue_size), |
1267 | memcpy(ptr, __per_cpu_load, static_size); | 1267 | pcpue_unit_size - pcpue_size); |
1268 | memcpy(ptr, __per_cpu_load, static_size); | ||
1269 | } else | ||
1270 | free_bootmem(__pa(ptr), pcpue_unit_size); | ||
1268 | } | 1271 | } |
1269 | 1272 | ||
1270 | /* we're ready, commit */ | 1273 | /* we're ready, commit */ |