diff options
author | Tejun Heo <tj@kernel.org> | 2014-09-02 14:46:05 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2014-09-02 14:46:05 -0400 |
commit | 9c824b6a172c8d44a6b037946bae90127c969b1b (patch) | |
tree | 32a8eb3ce9e3866ff8194d7fdfc5203bb5184e0e /mm | |
parent | 5835d96e9ce4efdba8c6cefffc2f1575925456de (diff) |
percpu: make sure chunk->map array has available space
An allocation attempt may require extending chunk->map array which
requires GFP_KERNEL context which isn't available for atomic
allocations. This patch ensures that chunk->map array usually keeps
some amount of available space by directly allocating buffer space
during GFP_KERNEL allocations and scheduling async extension during
atomic ones. This should make atomic allocation failures from map
space exhaustion rare.
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/percpu.c | 53 |
1 files changed, 45 insertions, 8 deletions
diff --git a/mm/percpu.c b/mm/percpu.c index c52b93117dc2..546ced05cf33 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
@@ -76,6 +76,8 @@ | |||
76 | 76 | ||
77 | #define PCPU_SLOT_BASE_SHIFT 5 /* 1-31 shares the same slot */ | 77 | #define PCPU_SLOT_BASE_SHIFT 5 /* 1-31 shares the same slot */ |
78 | #define PCPU_DFL_MAP_ALLOC 16 /* start a map with 16 ents */ | 78 | #define PCPU_DFL_MAP_ALLOC 16 /* start a map with 16 ents */ |
79 | #define PCPU_ATOMIC_MAP_MARGIN_LOW 32 | ||
80 | #define PCPU_ATOMIC_MAP_MARGIN_HIGH 64 | ||
79 | 81 | ||
80 | #ifdef CONFIG_SMP | 82 | #ifdef CONFIG_SMP |
81 | /* default addr <-> pcpu_ptr mapping, override in asm/percpu.h if necessary */ | 83 | /* default addr <-> pcpu_ptr mapping, override in asm/percpu.h if necessary */ |
@@ -102,9 +104,12 @@ struct pcpu_chunk { | |||
102 | int free_size; /* free bytes in the chunk */ | 104 | int free_size; /* free bytes in the chunk */ |
103 | int contig_hint; /* max contiguous size hint */ | 105 | int contig_hint; /* max contiguous size hint */ |
104 | void *base_addr; /* base address of this chunk */ | 106 | void *base_addr; /* base address of this chunk */ |
107 | |||
105 | int map_used; /* # of map entries used before the sentry */ | 108 | int map_used; /* # of map entries used before the sentry */ |
106 | int map_alloc; /* # of map entries allocated */ | 109 | int map_alloc; /* # of map entries allocated */ |
107 | int *map; /* allocation map */ | 110 | int *map; /* allocation map */ |
111 | struct work_struct map_extend_work;/* async ->map[] extension */ | ||
112 | |||
108 | void *data; /* chunk data */ | 113 | void *data; /* chunk data */ |
109 | int first_free; /* no free below this */ | 114 | int first_free; /* no free below this */ |
110 | bool immutable; /* no [de]population allowed */ | 115 | bool immutable; /* no [de]population allowed */ |
@@ -318,9 +323,14 @@ static void pcpu_chunk_relocate(struct pcpu_chunk *chunk, int oslot) | |||
318 | /** | 323 | /** |
319 | * pcpu_need_to_extend - determine whether chunk area map needs to be extended | 324 | * pcpu_need_to_extend - determine whether chunk area map needs to be extended |
320 | * @chunk: chunk of interest | 325 | * @chunk: chunk of interest |
326 | * @is_atomic: the allocation context | ||
321 | * | 327 | * |
322 | * Determine whether area map of @chunk needs to be extended to | 328 | * Determine whether area map of @chunk needs to be extended. If |
323 | * accommodate a new allocation. | 329 | * @is_atomic, only the amount necessary for a new allocation is |
330 | * considered; however, async extension is scheduled if the left amount is | ||
331 | * low. If !@is_atomic, it aims for more empty space. Combined, this | ||
332 | * ensures that the map is likely to have enough available space to | ||
333 | * accomodate atomic allocations which can't extend maps directly. | ||
324 | * | 334 | * |
325 | * CONTEXT: | 335 | * CONTEXT: |
326 | * pcpu_lock. | 336 | * pcpu_lock. |
@@ -329,15 +339,25 @@ static void pcpu_chunk_relocate(struct pcpu_chunk *chunk, int oslot) | |||
329 | * New target map allocation length if extension is necessary, 0 | 339 | * New target map allocation length if extension is necessary, 0 |
330 | * otherwise. | 340 | * otherwise. |
331 | */ | 341 | */ |
332 | static int pcpu_need_to_extend(struct pcpu_chunk *chunk) | 342 | static int pcpu_need_to_extend(struct pcpu_chunk *chunk, bool is_atomic) |
333 | { | 343 | { |
334 | int new_alloc; | 344 | int margin, new_alloc; |
345 | |||
346 | if (is_atomic) { | ||
347 | margin = 3; | ||
335 | 348 | ||
336 | if (chunk->map_alloc >= chunk->map_used + 3) | 349 | if (chunk->map_alloc < |
350 | chunk->map_used + PCPU_ATOMIC_MAP_MARGIN_LOW) | ||
351 | schedule_work(&chunk->map_extend_work); | ||
352 | } else { | ||
353 | margin = PCPU_ATOMIC_MAP_MARGIN_HIGH; | ||
354 | } | ||
355 | |||
356 | if (chunk->map_alloc >= chunk->map_used + margin) | ||
337 | return 0; | 357 | return 0; |
338 | 358 | ||
339 | new_alloc = PCPU_DFL_MAP_ALLOC; | 359 | new_alloc = PCPU_DFL_MAP_ALLOC; |
340 | while (new_alloc < chunk->map_used + 3) | 360 | while (new_alloc < chunk->map_used + margin) |
341 | new_alloc *= 2; | 361 | new_alloc *= 2; |
342 | 362 | ||
343 | return new_alloc; | 363 | return new_alloc; |
@@ -394,6 +414,20 @@ out_unlock: | |||
394 | return 0; | 414 | return 0; |
395 | } | 415 | } |
396 | 416 | ||
417 | static void pcpu_map_extend_workfn(struct work_struct *work) | ||
418 | { | ||
419 | struct pcpu_chunk *chunk = container_of(work, struct pcpu_chunk, | ||
420 | map_extend_work); | ||
421 | int new_alloc; | ||
422 | |||
423 | spin_lock_irq(&pcpu_lock); | ||
424 | new_alloc = pcpu_need_to_extend(chunk, false); | ||
425 | spin_unlock_irq(&pcpu_lock); | ||
426 | |||
427 | if (new_alloc) | ||
428 | pcpu_extend_area_map(chunk, new_alloc); | ||
429 | } | ||
430 | |||
397 | /** | 431 | /** |
398 | * pcpu_fit_in_area - try to fit the requested allocation in a candidate area | 432 | * pcpu_fit_in_area - try to fit the requested allocation in a candidate area |
399 | * @chunk: chunk the candidate area belongs to | 433 | * @chunk: chunk the candidate area belongs to |
@@ -647,6 +681,7 @@ static struct pcpu_chunk *pcpu_alloc_chunk(void) | |||
647 | chunk->map_used = 1; | 681 | chunk->map_used = 1; |
648 | 682 | ||
649 | INIT_LIST_HEAD(&chunk->list); | 683 | INIT_LIST_HEAD(&chunk->list); |
684 | INIT_WORK(&chunk->map_extend_work, pcpu_map_extend_workfn); | ||
650 | chunk->free_size = pcpu_unit_size; | 685 | chunk->free_size = pcpu_unit_size; |
651 | chunk->contig_hint = pcpu_unit_size; | 686 | chunk->contig_hint = pcpu_unit_size; |
652 | 687 | ||
@@ -767,7 +802,7 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved, | |||
767 | goto fail_unlock; | 802 | goto fail_unlock; |
768 | } | 803 | } |
769 | 804 | ||
770 | while ((new_alloc = pcpu_need_to_extend(chunk))) { | 805 | while ((new_alloc = pcpu_need_to_extend(chunk, is_atomic))) { |
771 | spin_unlock_irqrestore(&pcpu_lock, flags); | 806 | spin_unlock_irqrestore(&pcpu_lock, flags); |
772 | if (is_atomic || | 807 | if (is_atomic || |
773 | pcpu_extend_area_map(chunk, new_alloc) < 0) { | 808 | pcpu_extend_area_map(chunk, new_alloc) < 0) { |
@@ -792,7 +827,7 @@ restart: | |||
792 | if (size > chunk->contig_hint) | 827 | if (size > chunk->contig_hint) |
793 | continue; | 828 | continue; |
794 | 829 | ||
795 | new_alloc = pcpu_need_to_extend(chunk); | 830 | new_alloc = pcpu_need_to_extend(chunk, is_atomic); |
796 | if (new_alloc) { | 831 | if (new_alloc) { |
797 | if (is_atomic) | 832 | if (is_atomic) |
798 | continue; | 833 | continue; |
@@ -1418,6 +1453,7 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, | |||
1418 | */ | 1453 | */ |
1419 | schunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0); | 1454 | schunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0); |
1420 | INIT_LIST_HEAD(&schunk->list); | 1455 | INIT_LIST_HEAD(&schunk->list); |
1456 | INIT_WORK(&schunk->map_extend_work, pcpu_map_extend_workfn); | ||
1421 | schunk->base_addr = base_addr; | 1457 | schunk->base_addr = base_addr; |
1422 | schunk->map = smap; | 1458 | schunk->map = smap; |
1423 | schunk->map_alloc = ARRAY_SIZE(smap); | 1459 | schunk->map_alloc = ARRAY_SIZE(smap); |
@@ -1446,6 +1482,7 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, | |||
1446 | if (dyn_size) { | 1482 | if (dyn_size) { |
1447 | dchunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0); | 1483 | dchunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0); |
1448 | INIT_LIST_HEAD(&dchunk->list); | 1484 | INIT_LIST_HEAD(&dchunk->list); |
1485 | INIT_WORK(&dchunk->map_extend_work, pcpu_map_extend_workfn); | ||
1449 | dchunk->base_addr = base_addr; | 1486 | dchunk->base_addr = base_addr; |
1450 | dchunk->map = dmap; | 1487 | dchunk->map = dmap; |
1451 | dchunk->map_alloc = ARRAY_SIZE(dmap); | 1488 | dchunk->map_alloc = ARRAY_SIZE(dmap); |