diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/setup_percpu.c | 72 |
1 files changed, 55 insertions, 17 deletions
diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index 4f2e0ac9130b..7501bb14bd51 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c | |||
@@ -149,36 +149,73 @@ static void __init pcpul_map(void *ptr, size_t size, void *addr) | |||
149 | set_pmd(pmd, pmd_v); | 149 | set_pmd(pmd, pmd_v); |
150 | } | 150 | } |
151 | 151 | ||
152 | static int pcpu_lpage_cpu_distance(unsigned int from, unsigned int to) | ||
153 | { | ||
154 | if (early_cpu_to_node(from) == early_cpu_to_node(to)) | ||
155 | return LOCAL_DISTANCE; | ||
156 | else | ||
157 | return REMOTE_DISTANCE; | ||
158 | } | ||
159 | |||
152 | static ssize_t __init setup_pcpu_lpage(size_t static_size, bool chosen) | 160 | static ssize_t __init setup_pcpu_lpage(size_t static_size, bool chosen) |
153 | { | 161 | { |
154 | size_t reserve = PERCPU_MODULE_RESERVE + PERCPU_DYNAMIC_RESERVE; | 162 | size_t reserve = PERCPU_MODULE_RESERVE + PERCPU_DYNAMIC_RESERVE; |
163 | size_t dyn_size = reserve - PERCPU_FIRST_CHUNK_RESERVE; | ||
164 | size_t unit_map_size, unit_size; | ||
165 | int *unit_map; | ||
166 | int nr_units; | ||
167 | ssize_t ret; | ||
168 | |||
169 | /* on non-NUMA, embedding is better */ | ||
170 | if (!chosen && !pcpu_need_numa()) | ||
171 | return -EINVAL; | ||
172 | |||
173 | /* need PSE */ | ||
174 | if (!cpu_has_pse) { | ||
175 | pr_warning("PERCPU: lpage allocator requires PSE\n"); | ||
176 | return -EINVAL; | ||
177 | } | ||
155 | 178 | ||
179 | /* allocate and build unit_map */ | ||
180 | unit_map_size = num_possible_cpus() * sizeof(int); | ||
181 | unit_map = alloc_bootmem_nopanic(unit_map_size); | ||
182 | if (!unit_map) { | ||
183 | pr_warning("PERCPU: failed to allocate unit_map\n"); | ||
184 | return -ENOMEM; | ||
185 | } | ||
186 | |||
187 | ret = pcpu_lpage_build_unit_map(static_size, | ||
188 | PERCPU_FIRST_CHUNK_RESERVE, | ||
189 | &dyn_size, &unit_size, PMD_SIZE, | ||
190 | unit_map, pcpu_lpage_cpu_distance); | ||
191 | if (ret < 0) { | ||
192 | pr_warning("PERCPU: failed to build unit_map\n"); | ||
193 | goto out_free; | ||
194 | } | ||
195 | nr_units = ret; | ||
196 | |||
197 | /* do the parameters look okay? */ | ||
156 | if (!chosen) { | 198 | if (!chosen) { |
157 | size_t vm_size = VMALLOC_END - VMALLOC_START; | 199 | size_t vm_size = VMALLOC_END - VMALLOC_START; |
158 | size_t tot_size = num_possible_cpus() * PMD_SIZE; | 200 | size_t tot_size = nr_units * unit_size; |
159 | |||
160 | /* on non-NUMA, embedding is better */ | ||
161 | if (!pcpu_need_numa()) | ||
162 | return -EINVAL; | ||
163 | 201 | ||
164 | /* don't consume more than 20% of vmalloc area */ | 202 | /* don't consume more than 20% of vmalloc area */ |
165 | if (tot_size > vm_size / 5) { | 203 | if (tot_size > vm_size / 5) { |
166 | pr_info("PERCPU: too large chunk size %zuMB for " | 204 | pr_info("PERCPU: too large chunk size %zuMB for " |
167 | "large page remap\n", tot_size >> 20); | 205 | "large page remap\n", tot_size >> 20); |
168 | return -EINVAL; | 206 | ret = -EINVAL; |
207 | goto out_free; | ||
169 | } | 208 | } |
170 | } | 209 | } |
171 | 210 | ||
172 | /* need PSE */ | 211 | ret = pcpu_lpage_first_chunk(static_size, PERCPU_FIRST_CHUNK_RESERVE, |
173 | if (!cpu_has_pse) { | 212 | dyn_size, unit_size, PMD_SIZE, |
174 | pr_warning("PERCPU: lpage allocator requires PSE\n"); | 213 | unit_map, nr_units, |
175 | return -EINVAL; | 214 | pcpu_fc_alloc, pcpu_fc_free, pcpul_map); |
176 | } | 215 | out_free: |
177 | 216 | if (ret < 0) | |
178 | return pcpu_lpage_first_chunk(static_size, PERCPU_FIRST_CHUNK_RESERVE, | 217 | free_bootmem(__pa(unit_map), unit_map_size); |
179 | reserve - PERCPU_FIRST_CHUNK_RESERVE, | 218 | return ret; |
180 | PMD_SIZE, | ||
181 | pcpu_fc_alloc, pcpu_fc_free, pcpul_map); | ||
182 | } | 219 | } |
183 | #else | 220 | #else |
184 | static ssize_t __init setup_pcpu_lpage(size_t static_size, bool chosen) | 221 | static ssize_t __init setup_pcpu_lpage(size_t static_size, bool chosen) |
@@ -299,7 +336,8 @@ void __init setup_per_cpu_areas(void) | |||
299 | /* alrighty, percpu areas up and running */ | 336 | /* alrighty, percpu areas up and running */ |
300 | delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start; | 337 | delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start; |
301 | for_each_possible_cpu(cpu) { | 338 | for_each_possible_cpu(cpu) { |
302 | per_cpu_offset(cpu) = delta + cpu * pcpu_unit_size; | 339 | per_cpu_offset(cpu) = |
340 | delta + pcpu_unit_map[cpu] * pcpu_unit_size; | ||
303 | per_cpu(this_cpu_off, cpu) = per_cpu_offset(cpu); | 341 | per_cpu(this_cpu_off, cpu) = per_cpu_offset(cpu); |
304 | per_cpu(cpu_number, cpu) = cpu; | 342 | per_cpu(cpu_number, cpu) = cpu; |
305 | setup_percpu_segment(cpu); | 343 | setup_percpu_segment(cpu); |