aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2014-09-02 14:46:05 -0400
committerTejun Heo <tj@kernel.org>2014-09-02 14:46:05 -0400
commit9c824b6a172c8d44a6b037946bae90127c969b1b (patch)
tree32a8eb3ce9e3866ff8194d7fdfc5203bb5184e0e /mm
parent5835d96e9ce4efdba8c6cefffc2f1575925456de (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.c53
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 */
332static int pcpu_need_to_extend(struct pcpu_chunk *chunk) 342static 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
417static 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);