diff options
-rw-r--r-- | arch/x86/kernel/setup_percpu.c | 78 | ||||
-rw-r--r-- | include/linux/percpu.h | 12 | ||||
-rw-r--r-- | mm/percpu.c | 85 |
3 files changed, 113 insertions, 62 deletions
diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index 14728206fb52..ab896b31e80b 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c | |||
@@ -124,6 +124,19 @@ static void * __init pcpu_alloc_bootmem(unsigned int cpu, unsigned long size, | |||
124 | } | 124 | } |
125 | 125 | ||
126 | /* | 126 | /* |
127 | * Helpers for first chunk memory allocation | ||
128 | */ | ||
129 | static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size) | ||
130 | { | ||
131 | return pcpu_alloc_bootmem(cpu, size, size); | ||
132 | } | ||
133 | |||
134 | static void __init pcpu_fc_free(void *ptr, size_t size) | ||
135 | { | ||
136 | free_bootmem(__pa(ptr), size); | ||
137 | } | ||
138 | |||
139 | /* | ||
127 | * Large page remap allocator | 140 | * Large page remap allocator |
128 | * | 141 | * |
129 | * This allocator uses PMD page as unit. A PMD page is allocated for | 142 | * This allocator uses PMD page as unit. A PMD page is allocated for |
@@ -346,22 +359,11 @@ static ssize_t __init setup_pcpu_embed(size_t static_size, bool chosen) | |||
346 | } | 359 | } |
347 | 360 | ||
348 | /* | 361 | /* |
349 | * 4k page allocator | 362 | * 4k allocator |
350 | * | 363 | * |
351 | * This is the basic allocator. Static percpu area is allocated | 364 | * Boring fallback 4k allocator. This allocator puts more pressure on |
352 | * page-by-page and most of initialization is done by the generic | 365 | * PTE TLBs but other than that behaves nicely on both UMA and NUMA. |
353 | * setup function. | ||
354 | */ | 366 | */ |
355 | static struct page **pcpu4k_pages __initdata; | ||
356 | static int pcpu4k_nr_static_pages __initdata; | ||
357 | |||
358 | static struct page * __init pcpu4k_get_page(unsigned int cpu, int pageno) | ||
359 | { | ||
360 | if (pageno < pcpu4k_nr_static_pages) | ||
361 | return pcpu4k_pages[cpu * pcpu4k_nr_static_pages + pageno]; | ||
362 | return NULL; | ||
363 | } | ||
364 | |||
365 | static void __init pcpu4k_populate_pte(unsigned long addr) | 367 | static void __init pcpu4k_populate_pte(unsigned long addr) |
366 | { | 368 | { |
367 | populate_extra_pte(addr); | 369 | populate_extra_pte(addr); |
@@ -369,51 +371,9 @@ static void __init pcpu4k_populate_pte(unsigned long addr) | |||
369 | 371 | ||
370 | static ssize_t __init setup_pcpu_4k(size_t static_size) | 372 | static ssize_t __init setup_pcpu_4k(size_t static_size) |
371 | { | 373 | { |
372 | size_t pages_size; | 374 | return pcpu_4k_first_chunk(static_size, PERCPU_FIRST_CHUNK_RESERVE, |
373 | unsigned int cpu; | 375 | pcpu_fc_alloc, pcpu_fc_free, |
374 | int i, j; | 376 | pcpu4k_populate_pte); |
375 | ssize_t ret; | ||
376 | |||
377 | pcpu4k_nr_static_pages = PFN_UP(static_size); | ||
378 | |||
379 | /* unaligned allocations can't be freed, round up to page size */ | ||
380 | pages_size = PFN_ALIGN(pcpu4k_nr_static_pages * num_possible_cpus() | ||
381 | * sizeof(pcpu4k_pages[0])); | ||
382 | pcpu4k_pages = alloc_bootmem(pages_size); | ||
383 | |||
384 | /* allocate and copy */ | ||
385 | j = 0; | ||
386 | for_each_possible_cpu(cpu) | ||
387 | for (i = 0; i < pcpu4k_nr_static_pages; i++) { | ||
388 | void *ptr; | ||
389 | |||
390 | ptr = pcpu_alloc_bootmem(cpu, PAGE_SIZE, PAGE_SIZE); | ||
391 | if (!ptr) { | ||
392 | pr_warning("PERCPU: failed to allocate " | ||
393 | "4k page for cpu%u\n", cpu); | ||
394 | goto enomem; | ||
395 | } | ||
396 | |||
397 | memcpy(ptr, __per_cpu_load + i * PAGE_SIZE, PAGE_SIZE); | ||
398 | pcpu4k_pages[j++] = virt_to_page(ptr); | ||
399 | } | ||
400 | |||
401 | /* we're ready, commit */ | ||
402 | pr_info("PERCPU: Allocated %d 4k pages, static data %zu bytes\n", | ||
403 | pcpu4k_nr_static_pages, static_size); | ||
404 | |||
405 | ret = pcpu_setup_first_chunk(pcpu4k_get_page, static_size, | ||
406 | PERCPU_FIRST_CHUNK_RESERVE, -1, | ||
407 | -1, NULL, pcpu4k_populate_pte); | ||
408 | goto out_free_ar; | ||
409 | |||
410 | enomem: | ||
411 | while (--j >= 0) | ||
412 | free_bootmem(__pa(page_address(pcpu4k_pages[j])), PAGE_SIZE); | ||
413 | ret = -ENOMEM; | ||
414 | out_free_ar: | ||
415 | free_bootmem(__pa(pcpu4k_pages), pages_size); | ||
416 | return ret; | ||
417 | } | 377 | } |
418 | 378 | ||
419 | /* for explicit first chunk allocator selection */ | 379 | /* for explicit first chunk allocator selection */ |
diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 83bff053bd1c..41b5bfab4195 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h | |||
@@ -59,18 +59,26 @@ | |||
59 | extern void *pcpu_base_addr; | 59 | extern void *pcpu_base_addr; |
60 | 60 | ||
61 | typedef struct page * (*pcpu_get_page_fn_t)(unsigned int cpu, int pageno); | 61 | typedef struct page * (*pcpu_get_page_fn_t)(unsigned int cpu, int pageno); |
62 | typedef void (*pcpu_populate_pte_fn_t)(unsigned long addr); | 62 | typedef void * (*pcpu_fc_alloc_fn_t)(unsigned int cpu, size_t size); |
63 | typedef void (*pcpu_fc_free_fn_t)(void *ptr, size_t size); | ||
64 | typedef void (*pcpu_fc_populate_pte_fn_t)(unsigned long addr); | ||
63 | 65 | ||
64 | extern size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, | 66 | extern size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, |
65 | size_t static_size, size_t reserved_size, | 67 | size_t static_size, size_t reserved_size, |
66 | ssize_t dyn_size, ssize_t unit_size, | 68 | ssize_t dyn_size, ssize_t unit_size, |
67 | void *base_addr, | 69 | void *base_addr, |
68 | pcpu_populate_pte_fn_t populate_pte_fn); | 70 | pcpu_fc_populate_pte_fn_t populate_pte_fn); |
69 | 71 | ||
70 | extern ssize_t __init pcpu_embed_first_chunk( | 72 | extern ssize_t __init pcpu_embed_first_chunk( |
71 | size_t static_size, size_t reserved_size, | 73 | size_t static_size, size_t reserved_size, |
72 | ssize_t dyn_size); | 74 | ssize_t dyn_size); |
73 | 75 | ||
76 | extern ssize_t __init pcpu_4k_first_chunk( | ||
77 | size_t static_size, size_t reserved_size, | ||
78 | pcpu_fc_alloc_fn_t alloc_fn, | ||
79 | pcpu_fc_free_fn_t free_fn, | ||
80 | pcpu_fc_populate_pte_fn_t populate_pte_fn); | ||
81 | |||
74 | /* | 82 | /* |
75 | * Use this to get to a cpu's version of the per-cpu object | 83 | * Use this to get to a cpu's version of the per-cpu object |
76 | * dynamically allocated. Non-atomic access to the current CPU's | 84 | * dynamically allocated. Non-atomic access to the current CPU's |
diff --git a/mm/percpu.c b/mm/percpu.c index fc6babe6e554..27b0f40a3ea8 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
@@ -1037,7 +1037,7 @@ size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, | |||
1037 | size_t static_size, size_t reserved_size, | 1037 | size_t static_size, size_t reserved_size, |
1038 | ssize_t dyn_size, ssize_t unit_size, | 1038 | ssize_t dyn_size, ssize_t unit_size, |
1039 | void *base_addr, | 1039 | void *base_addr, |
1040 | pcpu_populate_pte_fn_t populate_pte_fn) | 1040 | pcpu_fc_populate_pte_fn_t populate_pte_fn) |
1041 | { | 1041 | { |
1042 | static struct vm_struct first_vm; | 1042 | static struct vm_struct first_vm; |
1043 | static int smap[2], dmap[2]; | 1043 | static int smap[2], dmap[2]; |
@@ -1271,6 +1271,89 @@ ssize_t __init pcpu_embed_first_chunk(size_t static_size, size_t reserved_size, | |||
1271 | } | 1271 | } |
1272 | 1272 | ||
1273 | /* | 1273 | /* |
1274 | * 4k page first chunk setup helper. | ||
1275 | */ | ||
1276 | static struct page **pcpu4k_pages __initdata; | ||
1277 | static int pcpu4k_nr_static_pages __initdata; | ||
1278 | |||
1279 | static struct page * __init pcpu4k_get_page(unsigned int cpu, int pageno) | ||
1280 | { | ||
1281 | if (pageno < pcpu4k_nr_static_pages) | ||
1282 | return pcpu4k_pages[cpu * pcpu4k_nr_static_pages + pageno]; | ||
1283 | return NULL; | ||
1284 | } | ||
1285 | |||
1286 | /** | ||
1287 | * pcpu_4k_first_chunk - map the first chunk using PAGE_SIZE pages | ||
1288 | * @static_size: the size of static percpu area in bytes | ||
1289 | * @reserved_size: the size of reserved percpu area in bytes | ||
1290 | * @alloc_fn: function to allocate percpu page, always called with PAGE_SIZE | ||
1291 | * @free_fn: funtion to free percpu page, always called with PAGE_SIZE | ||
1292 | * @populate_pte_fn: function to populate pte | ||
1293 | * | ||
1294 | * This is a helper to ease setting up embedded first percpu chunk and | ||
1295 | * can be called where pcpu_setup_first_chunk() is expected. | ||
1296 | * | ||
1297 | * This is the basic allocator. Static percpu area is allocated | ||
1298 | * page-by-page into vmalloc area. | ||
1299 | * | ||
1300 | * RETURNS: | ||
1301 | * The determined pcpu_unit_size which can be used to initialize | ||
1302 | * percpu access on success, -errno on failure. | ||
1303 | */ | ||
1304 | ssize_t __init pcpu_4k_first_chunk(size_t static_size, size_t reserved_size, | ||
1305 | pcpu_fc_alloc_fn_t alloc_fn, | ||
1306 | pcpu_fc_free_fn_t free_fn, | ||
1307 | pcpu_fc_populate_pte_fn_t populate_pte_fn) | ||
1308 | { | ||
1309 | size_t pages_size; | ||
1310 | unsigned int cpu; | ||
1311 | int i, j; | ||
1312 | ssize_t ret; | ||
1313 | |||
1314 | pcpu4k_nr_static_pages = PFN_UP(static_size); | ||
1315 | |||
1316 | /* unaligned allocations can't be freed, round up to page size */ | ||
1317 | pages_size = PFN_ALIGN(pcpu4k_nr_static_pages * num_possible_cpus() * | ||
1318 | sizeof(pcpu4k_pages[0])); | ||
1319 | pcpu4k_pages = alloc_bootmem(pages_size); | ||
1320 | |||
1321 | /* allocate and copy */ | ||
1322 | j = 0; | ||
1323 | for_each_possible_cpu(cpu) | ||
1324 | for (i = 0; i < pcpu4k_nr_static_pages; i++) { | ||
1325 | void *ptr; | ||
1326 | |||
1327 | ptr = alloc_fn(cpu, PAGE_SIZE); | ||
1328 | if (!ptr) { | ||
1329 | pr_warning("PERCPU: failed to allocate " | ||
1330 | "4k page for cpu%u\n", cpu); | ||
1331 | goto enomem; | ||
1332 | } | ||
1333 | |||
1334 | memcpy(ptr, __per_cpu_load + i * PAGE_SIZE, PAGE_SIZE); | ||
1335 | pcpu4k_pages[j++] = virt_to_page(ptr); | ||
1336 | } | ||
1337 | |||
1338 | /* we're ready, commit */ | ||
1339 | pr_info("PERCPU: Allocated %d 4k pages, static data %zu bytes\n", | ||
1340 | pcpu4k_nr_static_pages, static_size); | ||
1341 | |||
1342 | ret = pcpu_setup_first_chunk(pcpu4k_get_page, static_size, | ||
1343 | reserved_size, -1, | ||
1344 | -1, NULL, populate_pte_fn); | ||
1345 | goto out_free_ar; | ||
1346 | |||
1347 | enomem: | ||
1348 | while (--j >= 0) | ||
1349 | free_fn(page_address(pcpu4k_pages[j]), PAGE_SIZE); | ||
1350 | ret = -ENOMEM; | ||
1351 | out_free_ar: | ||
1352 | free_bootmem(__pa(pcpu4k_pages), pages_size); | ||
1353 | return ret; | ||
1354 | } | ||
1355 | |||
1356 | /* | ||
1274 | * Generic percpu area setup. | 1357 | * Generic percpu area setup. |
1275 | * | 1358 | * |
1276 | * The embedding helper is used because its behavior closely resembles | 1359 | * The embedding helper is used because its behavior closely resembles |