aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
Diffstat (limited to 'mm')
-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 */