diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/percpu.c | 136 |
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) | |||
539 | static void pcpu_free_area(struct pcpu_chunk *chunk, int freeme) | 537 | static 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 */ |