aboutsummaryrefslogtreecommitdiffstats
path: root/mm/percpu.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2014-03-06 21:13:18 -0500
committerTejun Heo <tj@kernel.org>2014-03-07 07:52:26 -0500
commit723ad1d90b5663ab623bb3bfba3e4ee7101795d7 (patch)
treed655c99efcba7240179869faa8151e1be746c23a /mm/percpu.c
parent706c16f2372316a0a8af3be6e2bd6e391c073ca0 (diff)
percpu: store offsets instead of lengths in ->map[]
Current code keeps +-length for each area in chunk->map[]. It has several unpleasant consequences: * even if we know that first 50 areas are all in use, allocation still needs to go through all those areas just to sum their sizes, just to get the offset of free one. * freeing needs to find the array entry refering to the area in question; again, the need to sum the sizes until we reach the offset we are interested in. Note that offsets are monotonous, so simple binary search would do here. New data representation: array of <offset,in-use flag> pairs. Each pair is represented by one int - we use offset|1 for <offset, in use> and offset for <offset, free> (we make sure that all offsets are even). In the end we put a sentry entry - <total size, in use>. The first entry is <0, flag>; it would be possible to store together the flag for Nth area and offset for N+1st, but that leads to much hairier code. In other words, where the old variant would have 4, -8, -4, 4, -12, 100 (4 bytes free, 8 in use, 4 in use, 4 free, 12 in use, 100 free) we store <0,0>, <4,1>, <12,1>, <16,0>, <20,1>, <32,0>, <132,1> i.e. 0, 5, 13, 16, 21, 32, 133 This commit switches to new data representation and takes care of a couple of low-hanging fruits in free_pcpu_area() - one is the switch to binary search, another is not doing two memmove() when one would do. Speeding the alloc side up (by keeping track of how many areas in the beginning are known to be all in use) also becomes possible - that'll be done in the next commit. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'mm/percpu.c')
-rw-r--r--mm/percpu.c136
1 files changed, 81 insertions, 55 deletions
diff --git a/mm/percpu.c b/mm/percpu.c
index 592f289819b7..49dfccf9169c 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -102,7 +102,7 @@ struct pcpu_chunk {
102 int free_size; /* free bytes in the chunk */ 102 int free_size; /* free bytes in the chunk */
103 int contig_hint; /* max contiguous size hint */ 103 int contig_hint; /* max contiguous size hint */
104 void *base_addr; /* base address of this chunk */ 104 void *base_addr; /* base address of this chunk */
105 int map_used; /* # of map entries used */ 105 int map_used; /* # of map entries used before the sentry */
106 int map_alloc; /* # of map entries allocated */ 106 int map_alloc; /* # of map entries allocated */
107 int *map; /* allocation map */ 107 int *map; /* allocation map */
108 void *data; /* chunk data */ 108 void *data; /* chunk data */
@@ -356,11 +356,11 @@ static int pcpu_need_to_extend(struct pcpu_chunk *chunk)
356{ 356{
357 int new_alloc; 357 int new_alloc;
358 358
359 if (chunk->map_alloc >= chunk->map_used + 2) 359 if (chunk->map_alloc >= chunk->map_used + 3)
360 return 0; 360 return 0;
361 361
362 new_alloc = PCPU_DFL_MAP_ALLOC; 362 new_alloc = PCPU_DFL_MAP_ALLOC;
363 while (new_alloc < chunk->map_used + 2) 363 while (new_alloc < chunk->map_used + 3)
364 new_alloc *= 2; 364 new_alloc *= 2;
365 365
366 return new_alloc; 366 return new_alloc;
@@ -441,19 +441,22 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align)
441 int oslot = pcpu_chunk_slot(chunk); 441 int oslot = pcpu_chunk_slot(chunk);
442 int max_contig = 0; 442 int max_contig = 0;
443 int i, off; 443 int i, off;
444 int *p;
444 445
445 for (i = 0, off = 0; i < chunk->map_used; off += abs(chunk->map[i++])) { 446 for (i = 0, p = chunk->map; i < chunk->map_used; i++, p++) {
446 bool is_last = i + 1 == chunk->map_used;
447 int head, tail; 447 int head, tail;
448 int this_size;
449
450 off = *p;
451 if (off & 1)
452 continue;
448 453
449 /* extra for alignment requirement */ 454 /* extra for alignment requirement */
450 head = ALIGN(off, align) - off; 455 head = ALIGN(off, align) - off;
451 BUG_ON(i == 0 && head != 0);
452 456
453 if (chunk->map[i] < 0) 457 this_size = (p[1] & ~1) - off;
454 continue; 458 if (this_size < head + size) {
455 if (chunk->map[i] < head + size) { 459 max_contig = max(this_size, max_contig);
456 max_contig = max(chunk->map[i], max_contig);
457 continue; 460 continue;
458 } 461 }
459 462
@@ -463,55 +466,50 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align)
463 * than sizeof(int), which is very small but isn't too 466 * than sizeof(int), which is very small but isn't too
464 * uncommon for percpu allocations. 467 * uncommon for percpu allocations.
465 */ 468 */
466 if (head && (head < sizeof(int) || chunk->map[i - 1] > 0)) { 469 if (head && (head < sizeof(int) || !(p[-1] & 1))) {
467 if (chunk->map[i - 1] > 0) 470 if (p[-1] & 1)
468 chunk->map[i - 1] += head;
469 else {
470 chunk->map[i - 1] -= head;
471 chunk->free_size -= head; 471 chunk->free_size -= head;
472 } 472 *p = off += head;
473 chunk->map[i] -= head; 473 this_size -= head;
474 off += head;
475 head = 0; 474 head = 0;
476 } 475 }
477 476
478 /* if tail is small, just keep it around */ 477 /* if tail is small, just keep it around */
479 tail = chunk->map[i] - head - size; 478 tail = this_size - head - size;
480 if (tail < sizeof(int)) 479 if (tail < sizeof(int)) {
481 tail = 0; 480 tail = 0;
481 size = this_size - head;
482 }
482 483
483 /* split if warranted */ 484 /* split if warranted */
484 if (head || tail) { 485 if (head || tail) {
485 int nr_extra = !!head + !!tail; 486 int nr_extra = !!head + !!tail;
486 487
487 /* insert new subblocks */ 488 /* insert new subblocks */
488 memmove(&chunk->map[i + nr_extra], &chunk->map[i], 489 memmove(p + nr_extra + 1, p + 1,
489 sizeof(chunk->map[0]) * (chunk->map_used - i)); 490 sizeof(chunk->map[0]) * (chunk->map_used - i));
490 chunk->map_used += nr_extra; 491 chunk->map_used += nr_extra;
491 492
492 if (head) { 493 if (head) {
493 chunk->map[i + 1] = chunk->map[i] - head; 494 *++p = off += head;
494 chunk->map[i] = head; 495 ++i;
495 off += head;
496 i++;
497 max_contig = max(head, max_contig); 496 max_contig = max(head, max_contig);
498 } 497 }
499 if (tail) { 498 if (tail) {
500 chunk->map[i] -= tail; 499 p[1] = off + size;
501 chunk->map[i + 1] = tail;
502 max_contig = max(tail, max_contig); 500 max_contig = max(tail, max_contig);
503 } 501 }
504 } 502 }
505 503
506 /* update hint and mark allocated */ 504 /* update hint and mark allocated */
507 if (is_last) 505 if (i + 1 == chunk->map_used)
508 chunk->contig_hint = max_contig; /* fully scanned */ 506 chunk->contig_hint = max_contig; /* fully scanned */
509 else 507 else
510 chunk->contig_hint = max(chunk->contig_hint, 508 chunk->contig_hint = max(chunk->contig_hint,
511 max_contig); 509 max_contig);
512 510
513 chunk->free_size -= chunk->map[i]; 511 chunk->free_size -= size;
514 chunk->map[i] = -chunk->map[i]; 512 *p |= 1;
515 513
516 pcpu_chunk_relocate(chunk, oslot); 514 pcpu_chunk_relocate(chunk, oslot);
517 return off; 515 return off;
@@ -539,34 +537,47 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align)
539static void pcpu_free_area(struct pcpu_chunk *chunk, int freeme) 537static void pcpu_free_area(struct pcpu_chunk *chunk, int freeme)
540{ 538{
541 int oslot = pcpu_chunk_slot(chunk); 539 int oslot = pcpu_chunk_slot(chunk);
542 int i, off; 540 int off = 0;
543 541 unsigned i, j;
544 for (i = 0, off = 0; i < chunk->map_used; off += abs(chunk->map[i++])) 542 int to_free = 0;
545 if (off == freeme) 543 int *p;
546 break; 544
545 freeme |= 1; /* we are searching for <given offset, in use> pair */
546
547 i = 0;
548 j = chunk->map_used;
549 while (i != j) {
550 unsigned k = (i + j) / 2;
551 off = chunk->map[k];
552 if (off < freeme)
553 i = k + 1;
554 else if (off > freeme)
555 j = k;
556 else
557 i = j = k;
558 }
547 BUG_ON(off != freeme); 559 BUG_ON(off != freeme);
548 BUG_ON(chunk->map[i] > 0);
549 560
550 chunk->map[i] = -chunk->map[i]; 561 p = chunk->map + i;
551 chunk->free_size += chunk->map[i]; 562 *p = off &= ~1;
563 chunk->free_size += (p[1] & ~1) - off;
552 564
565 /* merge with next? */
566 if (!(p[1] & 1))
567 to_free++;
553 /* merge with previous? */ 568 /* merge with previous? */
554 if (i > 0 && chunk->map[i - 1] >= 0) { 569 if (i > 0 && !(p[-1] & 1)) {
555 chunk->map[i - 1] += chunk->map[i]; 570 to_free++;
556 chunk->map_used--;
557 memmove(&chunk->map[i], &chunk->map[i + 1],
558 (chunk->map_used - i) * sizeof(chunk->map[0]));
559 i--; 571 i--;
572 p--;
560 } 573 }
561 /* merge with next? */ 574 if (to_free) {
562 if (i + 1 < chunk->map_used && chunk->map[i + 1] >= 0) { 575 chunk->map_used -= to_free;
563 chunk->map[i] += chunk->map[i + 1]; 576 memmove(p + 1, p + 1 + to_free,
564 chunk->map_used--; 577 (chunk->map_used - i) * sizeof(chunk->map[0]));
565 memmove(&chunk->map[i + 1], &chunk->map[i + 2],
566 (chunk->map_used - (i + 1)) * sizeof(chunk->map[0]));
567 } 578 }
568 579
569 chunk->contig_hint = max(chunk->map[i], chunk->contig_hint); 580 chunk->contig_hint = max(chunk->map[i + 1] - chunk->map[i] - 1, chunk->contig_hint);
570 pcpu_chunk_relocate(chunk, oslot); 581 pcpu_chunk_relocate(chunk, oslot);
571} 582}
572 583
@@ -586,7 +597,9 @@ static struct pcpu_chunk *pcpu_alloc_chunk(void)
586 } 597 }
587 598
588 chunk->map_alloc = PCPU_DFL_MAP_ALLOC; 599 chunk->map_alloc = PCPU_DFL_MAP_ALLOC;
589 chunk->map[chunk->map_used++] = pcpu_unit_size; 600 chunk->map[0] = 0;
601 chunk->map[1] = pcpu_unit_size | 1;
602 chunk->map_used = 1;
590 603
591 INIT_LIST_HEAD(&chunk->list); 604 INIT_LIST_HEAD(&chunk->list);
592 chunk->free_size = pcpu_unit_size; 605 chunk->free_size = pcpu_unit_size;
@@ -682,6 +695,13 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
682 unsigned long flags; 695 unsigned long flags;
683 void __percpu *ptr; 696 void __percpu *ptr;
684 697
698 /*
699 * We want the lowest bit of offset available for in-use/free
700 * indicator.
701 */
702 if (unlikely(align < 2))
703 align = 2;
704
685 if (unlikely(!size || size > PCPU_MIN_UNIT_SIZE || align > PAGE_SIZE)) { 705 if (unlikely(!size || size > PCPU_MIN_UNIT_SIZE || align > PAGE_SIZE)) {
686 WARN(true, "illegal size (%zu) or align (%zu) for " 706 WARN(true, "illegal size (%zu) or align (%zu) for "
687 "percpu allocation\n", size, align); 707 "percpu allocation\n", size, align);
@@ -1312,9 +1332,13 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
1312 } 1332 }
1313 schunk->contig_hint = schunk->free_size; 1333 schunk->contig_hint = schunk->free_size;
1314 1334
1315 schunk->map[schunk->map_used++] = -ai->static_size; 1335 schunk->map[0] = 1;
1336 schunk->map[1] = ai->static_size;
1337 schunk->map_used = 1;
1316 if (schunk->free_size) 1338 if (schunk->free_size)
1317 schunk->map[schunk->map_used++] = schunk->free_size; 1339 schunk->map[++schunk->map_used] = 1 | (ai->static_size + schunk->free_size);
1340 else
1341 schunk->map[1] |= 1;
1318 1342
1319 /* init dynamic chunk if necessary */ 1343 /* init dynamic chunk if necessary */
1320 if (dyn_size) { 1344 if (dyn_size) {
@@ -1327,8 +1351,10 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
1327 bitmap_fill(dchunk->populated, pcpu_unit_pages); 1351 bitmap_fill(dchunk->populated, pcpu_unit_pages);
1328 1352
1329 dchunk->contig_hint = dchunk->free_size = dyn_size; 1353 dchunk->contig_hint = dchunk->free_size = dyn_size;
1330 dchunk->map[dchunk->map_used++] = -pcpu_reserved_chunk_limit; 1354 dchunk->map[0] = 1;
1331 dchunk->map[dchunk->map_used++] = dchunk->free_size; 1355 dchunk->map[1] = pcpu_reserved_chunk_limit;
1356 dchunk->map[2] = (pcpu_reserved_chunk_limit + dchunk->free_size) | 1;
1357 dchunk->map_used = 2;
1332 } 1358 }
1333 1359
1334 /* link the first chunk in */ 1360 /* link the first chunk in */