diff options
Diffstat (limited to 'mm/percpu.c')
-rw-r--r-- | mm/percpu.c | 108 |
1 files changed, 60 insertions, 48 deletions
diff --git a/mm/percpu.c b/mm/percpu.c index f1d0e905850c..7d9bc35e8ed2 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
@@ -307,6 +307,50 @@ static void pcpu_chunk_addr_insert(struct pcpu_chunk *new) | |||
307 | } | 307 | } |
308 | 308 | ||
309 | /** | 309 | /** |
310 | * pcpu_extend_area_map - extend area map for allocation | ||
311 | * @chunk: target chunk | ||
312 | * | ||
313 | * Extend area map of @chunk so that it can accomodate an allocation. | ||
314 | * A single allocation can split an area into three areas, so this | ||
315 | * function makes sure that @chunk->map has at least two extra slots. | ||
316 | * | ||
317 | * RETURNS: | ||
318 | * 0 if noop, 1 if successfully extended, -errno on failure. | ||
319 | */ | ||
320 | static int pcpu_extend_area_map(struct pcpu_chunk *chunk) | ||
321 | { | ||
322 | int new_alloc; | ||
323 | int *new; | ||
324 | size_t size; | ||
325 | |||
326 | /* has enough? */ | ||
327 | if (chunk->map_alloc >= chunk->map_used + 2) | ||
328 | return 0; | ||
329 | |||
330 | new_alloc = PCPU_DFL_MAP_ALLOC; | ||
331 | while (new_alloc < chunk->map_used + 2) | ||
332 | new_alloc *= 2; | ||
333 | |||
334 | new = pcpu_mem_alloc(new_alloc * sizeof(new[0])); | ||
335 | if (!new) | ||
336 | return -ENOMEM; | ||
337 | |||
338 | size = chunk->map_alloc * sizeof(chunk->map[0]); | ||
339 | memcpy(new, chunk->map, size); | ||
340 | |||
341 | /* | ||
342 | * map_alloc < PCPU_DFL_MAP_ALLOC indicates that the chunk is | ||
343 | * one of the first chunks and still using static map. | ||
344 | */ | ||
345 | if (chunk->map_alloc >= PCPU_DFL_MAP_ALLOC) | ||
346 | pcpu_mem_free(chunk->map, size); | ||
347 | |||
348 | chunk->map_alloc = new_alloc; | ||
349 | chunk->map = new; | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | /** | ||
310 | * pcpu_split_block - split a map block | 354 | * pcpu_split_block - split a map block |
311 | * @chunk: chunk of interest | 355 | * @chunk: chunk of interest |
312 | * @i: index of map block to split | 356 | * @i: index of map block to split |
@@ -321,44 +365,16 @@ static void pcpu_chunk_addr_insert(struct pcpu_chunk *new) | |||
321 | * depending on @head, is reduced by @tail bytes and @tail byte block | 365 | * depending on @head, is reduced by @tail bytes and @tail byte block |
322 | * is inserted after the target block. | 366 | * is inserted after the target block. |
323 | * | 367 | * |
324 | * RETURNS: | 368 | * @chunk->map must have enough free slots to accomodate the split. |
325 | * 0 on success, -errno on failure. | ||
326 | */ | 369 | */ |
327 | static int pcpu_split_block(struct pcpu_chunk *chunk, int i, int head, int tail) | 370 | static void pcpu_split_block(struct pcpu_chunk *chunk, int i, |
371 | int head, int tail) | ||
328 | { | 372 | { |
329 | int nr_extra = !!head + !!tail; | 373 | int nr_extra = !!head + !!tail; |
330 | int target = chunk->map_used + nr_extra; | ||
331 | |||
332 | /* reallocation required? */ | ||
333 | if (chunk->map_alloc < target) { | ||
334 | int new_alloc; | ||
335 | int *new; | ||
336 | size_t size; | ||
337 | |||
338 | new_alloc = PCPU_DFL_MAP_ALLOC; | ||
339 | while (new_alloc < target) | ||
340 | new_alloc *= 2; | ||
341 | |||
342 | new = pcpu_mem_alloc(new_alloc * sizeof(new[0])); | ||
343 | if (!new) | ||
344 | return -ENOMEM; | ||
345 | |||
346 | size = chunk->map_alloc * sizeof(chunk->map[0]); | ||
347 | memcpy(new, chunk->map, size); | ||
348 | |||
349 | /* | ||
350 | * map_alloc < PCPU_DFL_MAP_ALLOC indicates that the | ||
351 | * chunk is one of the first chunks and still using | ||
352 | * static map. | ||
353 | */ | ||
354 | if (chunk->map_alloc >= PCPU_DFL_MAP_ALLOC) | ||
355 | pcpu_mem_free(chunk->map, size); | ||
356 | 374 | ||
357 | chunk->map_alloc = new_alloc; | 375 | BUG_ON(chunk->map_alloc < chunk->map_used + nr_extra); |
358 | chunk->map = new; | ||
359 | } | ||
360 | 376 | ||
361 | /* insert a new subblock */ | 377 | /* insert new subblocks */ |
362 | memmove(&chunk->map[i + nr_extra], &chunk->map[i], | 378 | memmove(&chunk->map[i + nr_extra], &chunk->map[i], |
363 | sizeof(chunk->map[0]) * (chunk->map_used - i)); | 379 | sizeof(chunk->map[0]) * (chunk->map_used - i)); |
364 | chunk->map_used += nr_extra; | 380 | chunk->map_used += nr_extra; |
@@ -371,7 +387,6 @@ static int pcpu_split_block(struct pcpu_chunk *chunk, int i, int head, int tail) | |||
371 | chunk->map[i++] -= tail; | 387 | chunk->map[i++] -= tail; |
372 | chunk->map[i] = tail; | 388 | chunk->map[i] = tail; |
373 | } | 389 | } |
374 | return 0; | ||
375 | } | 390 | } |
376 | 391 | ||
377 | /** | 392 | /** |
@@ -384,8 +399,11 @@ static int pcpu_split_block(struct pcpu_chunk *chunk, int i, int head, int tail) | |||
384 | * Note that this function only allocates the offset. It doesn't | 399 | * Note that this function only allocates the offset. It doesn't |
385 | * populate or map the area. | 400 | * populate or map the area. |
386 | * | 401 | * |
402 | * @chunk->map must have at least two free slots. | ||
403 | * | ||
387 | * RETURNS: | 404 | * RETURNS: |
388 | * Allocated offset in @chunk on success, -errno on failure. | 405 | * Allocated offset in @chunk on success, -1 if no matching area is |
406 | * found. | ||
389 | */ | 407 | */ |
390 | static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align) | 408 | static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align) |
391 | { | 409 | { |
@@ -433,8 +451,7 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align) | |||
433 | 451 | ||
434 | /* split if warranted */ | 452 | /* split if warranted */ |
435 | if (head || tail) { | 453 | if (head || tail) { |
436 | if (pcpu_split_block(chunk, i, head, tail)) | 454 | pcpu_split_block(chunk, i, head, tail); |
437 | return -ENOMEM; | ||
438 | if (head) { | 455 | if (head) { |
439 | i++; | 456 | i++; |
440 | off += head; | 457 | off += head; |
@@ -461,14 +478,8 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align) | |||
461 | chunk->contig_hint = max_contig; /* fully scanned */ | 478 | chunk->contig_hint = max_contig; /* fully scanned */ |
462 | pcpu_chunk_relocate(chunk, oslot); | 479 | pcpu_chunk_relocate(chunk, oslot); |
463 | 480 | ||
464 | /* | 481 | /* tell the upper layer that this chunk has no matching area */ |
465 | * Tell the upper layer that this chunk has no area left. | 482 | return -1; |
466 | * Note that this is not an error condition but a notification | ||
467 | * to upper layer that it needs to look at other chunks. | ||
468 | * -ENOSPC is chosen as it isn't used in memory subsystem and | ||
469 | * matches the meaning in a way. | ||
470 | */ | ||
471 | return -ENOSPC; | ||
472 | } | 483 | } |
473 | 484 | ||
474 | /** | 485 | /** |
@@ -755,7 +766,8 @@ static void *pcpu_alloc(size_t size, size_t align, bool reserved) | |||
755 | /* serve reserved allocations from the reserved chunk if available */ | 766 | /* serve reserved allocations from the reserved chunk if available */ |
756 | if (reserved && pcpu_reserved_chunk) { | 767 | if (reserved && pcpu_reserved_chunk) { |
757 | chunk = pcpu_reserved_chunk; | 768 | chunk = pcpu_reserved_chunk; |
758 | if (size > chunk->contig_hint) | 769 | if (size > chunk->contig_hint || |
770 | pcpu_extend_area_map(chunk) < 0) | ||
759 | goto out_unlock; | 771 | goto out_unlock; |
760 | off = pcpu_alloc_area(chunk, size, align); | 772 | off = pcpu_alloc_area(chunk, size, align); |
761 | if (off >= 0) | 773 | if (off >= 0) |
@@ -768,11 +780,11 @@ static void *pcpu_alloc(size_t size, size_t align, bool reserved) | |||
768 | list_for_each_entry(chunk, &pcpu_slot[slot], list) { | 780 | list_for_each_entry(chunk, &pcpu_slot[slot], list) { |
769 | if (size > chunk->contig_hint) | 781 | if (size > chunk->contig_hint) |
770 | continue; | 782 | continue; |
783 | if (pcpu_extend_area_map(chunk) < 0) | ||
784 | goto out_unlock; | ||
771 | off = pcpu_alloc_area(chunk, size, align); | 785 | off = pcpu_alloc_area(chunk, size, align); |
772 | if (off >= 0) | 786 | if (off >= 0) |
773 | goto area_found; | 787 | goto area_found; |
774 | if (off != -ENOSPC) | ||
775 | goto out_unlock; | ||
776 | } | 788 | } |
777 | } | 789 | } |
778 | 790 | ||