diff options
| -rw-r--r-- | arch/x86/kernel/setup_percpu.c | 54 | ||||
| -rw-r--r-- | include/linux/percpu.h | 4 | ||||
| -rw-r--r-- | mm/percpu.c | 86 |
3 files changed, 96 insertions, 48 deletions
diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index e41c51f6ada1..400331b50a53 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c | |||
| @@ -257,31 +257,13 @@ static ssize_t __init setup_pcpu_remap(size_t static_size) | |||
| 257 | * Embedding allocator | 257 | * Embedding allocator |
| 258 | * | 258 | * |
| 259 | * The first chunk is sized to just contain the static area plus | 259 | * The first chunk is sized to just contain the static area plus |
| 260 | * module and dynamic reserves, and allocated as a contiguous area | 260 | * module and dynamic reserves and embedded into linear physical |
| 261 | * using bootmem allocator and used as-is without being mapped into | 261 | * mapping so that it can use PMD mapping without additional TLB |
| 262 | * vmalloc area. This enables the first chunk to piggy back on the | 262 | * pressure. |
| 263 | * linear physical PMD mapping and doesn't add any additional pressure | ||
| 264 | * to TLB. Note that if the needed size is smaller than the minimum | ||
| 265 | * unit size, the leftover is returned to the bootmem allocator. | ||
| 266 | */ | 263 | */ |
| 267 | static void *pcpue_ptr __initdata; | ||
| 268 | static size_t pcpue_size __initdata; | ||
| 269 | static size_t pcpue_unit_size __initdata; | ||
| 270 | |||
| 271 | static struct page * __init pcpue_get_page(unsigned int cpu, int pageno) | ||
| 272 | { | ||
| 273 | size_t off = (size_t)pageno << PAGE_SHIFT; | ||
| 274 | |||
| 275 | if (off >= pcpue_size) | ||
| 276 | return NULL; | ||
| 277 | |||
| 278 | return virt_to_page(pcpue_ptr + cpu * pcpue_unit_size + off); | ||
| 279 | } | ||
| 280 | |||
| 281 | static ssize_t __init setup_pcpu_embed(size_t static_size) | 264 | static ssize_t __init setup_pcpu_embed(size_t static_size) |
| 282 | { | 265 | { |
| 283 | unsigned int cpu; | 266 | size_t reserve = PERCPU_MODULE_RESERVE + PERCPU_DYNAMIC_RESERVE; |
| 284 | size_t dyn_size; | ||
| 285 | 267 | ||
| 286 | /* | 268 | /* |
| 287 | * If large page isn't supported, there's no benefit in doing | 269 | * If large page isn't supported, there's no benefit in doing |
| @@ -291,32 +273,8 @@ static ssize_t __init setup_pcpu_embed(size_t static_size) | |||
| 291 | if (!cpu_has_pse || pcpu_need_numa()) | 273 | if (!cpu_has_pse || pcpu_need_numa()) |
| 292 | return -EINVAL; | 274 | return -EINVAL; |
| 293 | 275 | ||
| 294 | /* allocate and copy */ | 276 | return pcpu_embed_first_chunk(static_size, PERCPU_FIRST_CHUNK_RESERVE, |
| 295 | pcpue_size = PFN_ALIGN(static_size + PERCPU_MODULE_RESERVE + | 277 | reserve - PERCPU_FIRST_CHUNK_RESERVE, -1); |
| 296 | PERCPU_DYNAMIC_RESERVE); | ||
| 297 | pcpue_unit_size = max_t(size_t, pcpue_size, PCPU_MIN_UNIT_SIZE); | ||
| 298 | dyn_size = pcpue_size - static_size - PERCPU_FIRST_CHUNK_RESERVE; | ||
| 299 | |||
| 300 | pcpue_ptr = pcpu_alloc_bootmem(0, num_possible_cpus() * pcpue_unit_size, | ||
| 301 | PAGE_SIZE); | ||
| 302 | if (!pcpue_ptr) | ||
| 303 | return -ENOMEM; | ||
| 304 | |||
| 305 | for_each_possible_cpu(cpu) { | ||
| 306 | void *ptr = pcpue_ptr + cpu * pcpue_unit_size; | ||
| 307 | |||
| 308 | free_bootmem(__pa(ptr + pcpue_size), | ||
| 309 | pcpue_unit_size - pcpue_size); | ||
| 310 | memcpy(ptr, __per_cpu_load, static_size); | ||
| 311 | } | ||
| 312 | |||
| 313 | /* we're ready, commit */ | ||
| 314 | pr_info("PERCPU: Embedded %zu pages at %p, static data %zu bytes\n", | ||
| 315 | pcpue_size >> PAGE_SHIFT, pcpue_ptr, static_size); | ||
| 316 | |||
| 317 | return pcpu_setup_first_chunk(pcpue_get_page, static_size, | ||
| 318 | PERCPU_FIRST_CHUNK_RESERVE, dyn_size, | ||
| 319 | pcpue_unit_size, pcpue_ptr, NULL); | ||
| 320 | } | 278 | } |
| 321 | 279 | ||
| 322 | /* | 280 | /* |
diff --git a/include/linux/percpu.h b/include/linux/percpu.h index fb455dcc59c7..ee5615d65211 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h | |||
| @@ -111,6 +111,10 @@ extern size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, | |||
| 111 | void *base_addr, | 111 | void *base_addr, |
| 112 | pcpu_populate_pte_fn_t populate_pte_fn); | 112 | pcpu_populate_pte_fn_t populate_pte_fn); |
| 113 | 113 | ||
| 114 | extern ssize_t __init pcpu_embed_first_chunk( | ||
| 115 | size_t static_size, size_t reserved_size, | ||
| 116 | ssize_t dyn_size, ssize_t unit_size); | ||
| 117 | |||
| 114 | /* | 118 | /* |
| 115 | * Use this to get to a cpu's version of the per-cpu object | 119 | * Use this to get to a cpu's version of the per-cpu object |
| 116 | * dynamically allocated. Non-atomic access to the current CPU's | 120 | * dynamically allocated. Non-atomic access to the current CPU's |
diff --git a/mm/percpu.c b/mm/percpu.c index 2f94661d3e36..1aa5d8fbca12 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
| @@ -1238,3 +1238,89 @@ size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, | |||
| 1238 | pcpu_base_addr = (void *)pcpu_chunk_addr(schunk, 0, 0); | 1238 | pcpu_base_addr = (void *)pcpu_chunk_addr(schunk, 0, 0); |
| 1239 | return pcpu_unit_size; | 1239 | return pcpu_unit_size; |
| 1240 | } | 1240 | } |
| 1241 | |||
| 1242 | /* | ||
| 1243 | * Embedding first chunk setup helper. | ||
| 1244 | */ | ||
| 1245 | static void *pcpue_ptr __initdata; | ||
| 1246 | static size_t pcpue_size __initdata; | ||
| 1247 | static size_t pcpue_unit_size __initdata; | ||
| 1248 | |||
| 1249 | static struct page * __init pcpue_get_page(unsigned int cpu, int pageno) | ||
| 1250 | { | ||
| 1251 | size_t off = (size_t)pageno << PAGE_SHIFT; | ||
| 1252 | |||
| 1253 | if (off >= pcpue_size) | ||
| 1254 | return NULL; | ||
| 1255 | |||
| 1256 | return virt_to_page(pcpue_ptr + cpu * pcpue_unit_size + off); | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | /** | ||
| 1260 | * pcpu_embed_first_chunk - embed the first percpu chunk into bootmem | ||
| 1261 | * @static_size: the size of static percpu area in bytes | ||
| 1262 | * @reserved_size: the size of reserved percpu area in bytes | ||
| 1263 | * @dyn_size: free size for dynamic allocation in bytes, -1 for auto | ||
| 1264 | * @unit_size: unit size in bytes, must be multiple of PAGE_SIZE, -1 for auto | ||
| 1265 | * | ||
| 1266 | * This is a helper to ease setting up embedded first percpu chunk and | ||
| 1267 | * can be called where pcpu_setup_first_chunk() is expected. | ||
| 1268 | * | ||
| 1269 | * If this function is used to setup the first chunk, it is allocated | ||
| 1270 | * as a contiguous area using bootmem allocator and used as-is without | ||
| 1271 | * being mapped into vmalloc area. This enables the first chunk to | ||
| 1272 | * piggy back on the linear physical mapping which often uses larger | ||
| 1273 | * page size. | ||
| 1274 | * | ||
| 1275 | * When @dyn_size is positive, dynamic area might be larger than | ||
| 1276 | * specified to fill page alignment. Also, when @dyn_size is auto, | ||
| 1277 | * @dyn_size does not fill the whole first chunk but only what's | ||
| 1278 | * necessary for page alignment after static and reserved areas. | ||
| 1279 | * | ||
| 1280 | * If the needed size is smaller than the minimum or specified unit | ||
| 1281 | * size, the leftover is returned to the bootmem allocator. | ||
| 1282 | * | ||
| 1283 | * RETURNS: | ||
| 1284 | * The determined pcpu_unit_size which can be used to initialize | ||
| 1285 | * percpu access on success, -errno on failure. | ||
| 1286 | */ | ||
| 1287 | ssize_t __init pcpu_embed_first_chunk(size_t static_size, size_t reserved_size, | ||
| 1288 | ssize_t dyn_size, ssize_t unit_size) | ||
| 1289 | { | ||
| 1290 | unsigned int cpu; | ||
| 1291 | |||
| 1292 | /* determine parameters and allocate */ | ||
| 1293 | pcpue_size = PFN_ALIGN(static_size + reserved_size + | ||
| 1294 | (dyn_size >= 0 ? dyn_size : 0)); | ||
| 1295 | if (dyn_size != 0) | ||
| 1296 | dyn_size = pcpue_size - static_size - reserved_size; | ||
| 1297 | |||
| 1298 | if (unit_size >= 0) { | ||
| 1299 | BUG_ON(unit_size < pcpue_size); | ||
| 1300 | pcpue_unit_size = unit_size; | ||
| 1301 | } else | ||
| 1302 | pcpue_unit_size = max_t(size_t, pcpue_size, PCPU_MIN_UNIT_SIZE); | ||
| 1303 | |||
| 1304 | pcpue_ptr = __alloc_bootmem_nopanic( | ||
| 1305 | num_possible_cpus() * pcpue_unit_size, | ||
| 1306 | PAGE_SIZE, __pa(MAX_DMA_ADDRESS)); | ||
| 1307 | if (!pcpue_ptr) | ||
| 1308 | return -ENOMEM; | ||
| 1309 | |||
| 1310 | /* return the leftover and copy */ | ||
| 1311 | for_each_possible_cpu(cpu) { | ||
| 1312 | void *ptr = pcpue_ptr + cpu * pcpue_unit_size; | ||
| 1313 | |||
| 1314 | free_bootmem(__pa(ptr + pcpue_size), | ||
| 1315 | pcpue_unit_size - pcpue_size); | ||
| 1316 | memcpy(ptr, __per_cpu_load, static_size); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | /* we're ready, commit */ | ||
| 1320 | pr_info("PERCPU: Embedded %zu pages at %p, static data %zu bytes\n", | ||
| 1321 | pcpue_size >> PAGE_SHIFT, pcpue_ptr, static_size); | ||
| 1322 | |||
| 1323 | return pcpu_setup_first_chunk(pcpue_get_page, static_size, | ||
| 1324 | reserved_size, dyn_size, | ||
| 1325 | pcpue_unit_size, pcpue_ptr, NULL); | ||
| 1326 | } | ||
