diff options
Diffstat (limited to 'mm/percpu.c')
| -rw-r--r-- | mm/percpu.c | 139 |
1 files changed, 92 insertions, 47 deletions
diff --git a/mm/percpu.c b/mm/percpu.c index 6af78c1ee704..5adfc268b408 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
| @@ -153,7 +153,10 @@ static int pcpu_reserved_chunk_limit; | |||
| 153 | * | 153 | * |
| 154 | * During allocation, pcpu_alloc_mutex is kept locked all the time and | 154 | * During allocation, pcpu_alloc_mutex is kept locked all the time and |
| 155 | * pcpu_lock is grabbed and released as necessary. All actual memory | 155 | * pcpu_lock is grabbed and released as necessary. All actual memory |
| 156 | * allocations are done using GFP_KERNEL with pcpu_lock released. | 156 | * allocations are done using GFP_KERNEL with pcpu_lock released. In |
| 157 | * general, percpu memory can't be allocated with irq off but | ||
| 158 | * irqsave/restore are still used in alloc path so that it can be used | ||
| 159 | * from early init path - sched_init() specifically. | ||
| 157 | * | 160 | * |
| 158 | * Free path accesses and alters only the index data structures, so it | 161 | * Free path accesses and alters only the index data structures, so it |
| 159 | * can be safely called from atomic context. When memory needs to be | 162 | * can be safely called from atomic context. When memory needs to be |
| @@ -352,62 +355,86 @@ static struct pcpu_chunk *pcpu_chunk_addr_search(void *addr) | |||
| 352 | } | 355 | } |
| 353 | 356 | ||
| 354 | /** | 357 | /** |
| 355 | * pcpu_extend_area_map - extend area map for allocation | 358 | * pcpu_need_to_extend - determine whether chunk area map needs to be extended |
| 356 | * @chunk: target chunk | 359 | * @chunk: chunk of interest |
| 357 | * | 360 | * |
| 358 | * Extend area map of @chunk so that it can accomodate an allocation. | 361 | * Determine whether area map of @chunk needs to be extended to |
| 359 | * A single allocation can split an area into three areas, so this | 362 | * accomodate a new allocation. |
| 360 | * function makes sure that @chunk->map has at least two extra slots. | ||
| 361 | * | 363 | * |
| 362 | * CONTEXT: | 364 | * CONTEXT: |
| 363 | * pcpu_alloc_mutex, pcpu_lock. pcpu_lock is released and reacquired | 365 | * pcpu_lock. |
| 364 | * if area map is extended. | ||
| 365 | * | 366 | * |
| 366 | * RETURNS: | 367 | * RETURNS: |
| 367 | * 0 if noop, 1 if successfully extended, -errno on failure. | 368 | * New target map allocation length if extension is necessary, 0 |
| 369 | * otherwise. | ||
| 368 | */ | 370 | */ |
| 369 | static int pcpu_extend_area_map(struct pcpu_chunk *chunk) | 371 | static int pcpu_need_to_extend(struct pcpu_chunk *chunk) |
| 370 | { | 372 | { |
| 371 | int new_alloc; | 373 | int new_alloc; |
| 372 | int *new; | ||
| 373 | size_t size; | ||
| 374 | 374 | ||
| 375 | /* has enough? */ | ||
| 376 | if (chunk->map_alloc >= chunk->map_used + 2) | 375 | if (chunk->map_alloc >= chunk->map_used + 2) |
| 377 | return 0; | 376 | return 0; |
| 378 | 377 | ||
| 379 | spin_unlock_irq(&pcpu_lock); | ||
| 380 | |||
| 381 | new_alloc = PCPU_DFL_MAP_ALLOC; | 378 | new_alloc = PCPU_DFL_MAP_ALLOC; |
| 382 | while (new_alloc < chunk->map_used + 2) | 379 | while (new_alloc < chunk->map_used + 2) |
| 383 | new_alloc *= 2; | 380 | new_alloc *= 2; |
| 384 | 381 | ||
| 385 | new = pcpu_mem_alloc(new_alloc * sizeof(new[0])); | 382 | return new_alloc; |
| 386 | if (!new) { | 383 | } |
| 387 | spin_lock_irq(&pcpu_lock); | 384 | |
| 385 | /** | ||
| 386 | * pcpu_extend_area_map - extend area map of a chunk | ||
| 387 | * @chunk: chunk of interest | ||
| 388 | * @new_alloc: new target allocation length of the area map | ||
| 389 | * | ||
| 390 | * Extend area map of @chunk to have @new_alloc entries. | ||
| 391 | * | ||
| 392 | * CONTEXT: | ||
| 393 | * Does GFP_KERNEL allocation. Grabs and releases pcpu_lock. | ||
| 394 | * | ||
| 395 | * RETURNS: | ||
| 396 | * 0 on success, -errno on failure. | ||
| 397 | */ | ||
| 398 | static int pcpu_extend_area_map(struct pcpu_chunk *chunk, int new_alloc) | ||
| 399 | { | ||
| 400 | int *old = NULL, *new = NULL; | ||
| 401 | size_t old_size = 0, new_size = new_alloc * sizeof(new[0]); | ||
| 402 | unsigned long flags; | ||
| 403 | |||
| 404 | new = pcpu_mem_alloc(new_size); | ||
| 405 | if (!new) | ||
| 388 | return -ENOMEM; | 406 | return -ENOMEM; |
| 389 | } | ||
| 390 | 407 | ||
| 391 | /* | 408 | /* acquire pcpu_lock and switch to new area map */ |
| 392 | * Acquire pcpu_lock and switch to new area map. Only free | 409 | spin_lock_irqsave(&pcpu_lock, flags); |
| 393 | * could have happened inbetween, so map_used couldn't have | 410 | |
| 394 | * grown. | 411 | if (new_alloc <= chunk->map_alloc) |
| 395 | */ | 412 | goto out_unlock; |
| 396 | spin_lock_irq(&pcpu_lock); | ||
| 397 | BUG_ON(new_alloc < chunk->map_used + 2); | ||
| 398 | 413 | ||
| 399 | size = chunk->map_alloc * sizeof(chunk->map[0]); | 414 | old_size = chunk->map_alloc * sizeof(chunk->map[0]); |
| 400 | memcpy(new, chunk->map, size); | 415 | memcpy(new, chunk->map, old_size); |
| 401 | 416 | ||
| 402 | /* | 417 | /* |
| 403 | * map_alloc < PCPU_DFL_MAP_ALLOC indicates that the chunk is | 418 | * map_alloc < PCPU_DFL_MAP_ALLOC indicates that the chunk is |
| 404 | * one of the first chunks and still using static map. | 419 | * one of the first chunks and still using static map. |
| 405 | */ | 420 | */ |
| 406 | if (chunk->map_alloc >= PCPU_DFL_MAP_ALLOC) | 421 | if (chunk->map_alloc >= PCPU_DFL_MAP_ALLOC) |
| 407 | pcpu_mem_free(chunk->map, size); | 422 | old = chunk->map; |
| 408 | 423 | ||
| 409 | chunk->map_alloc = new_alloc; | 424 | chunk->map_alloc = new_alloc; |
| 410 | chunk->map = new; | 425 | chunk->map = new; |
| 426 | new = NULL; | ||
| 427 | |||
| 428 | out_unlock: | ||
| 429 | spin_unlock_irqrestore(&pcpu_lock, flags); | ||
| 430 | |||
| 431 | /* | ||
| 432 | * pcpu_mem_free() might end up calling vfree() which uses | ||
| 433 | * IRQ-unsafe lock and thus can't be called under pcpu_lock. | ||
| 434 | */ | ||
| 435 | pcpu_mem_free(old, old_size); | ||
| 436 | pcpu_mem_free(new, new_size); | ||
| 437 | |||
| 411 | return 0; | 438 | return 0; |
| 412 | } | 439 | } |
| 413 | 440 | ||
| @@ -1046,7 +1073,8 @@ static void *pcpu_alloc(size_t size, size_t align, bool reserved) | |||
| 1046 | static int warn_limit = 10; | 1073 | static int warn_limit = 10; |
| 1047 | struct pcpu_chunk *chunk; | 1074 | struct pcpu_chunk *chunk; |
| 1048 | const char *err; | 1075 | const char *err; |
| 1049 | int slot, off; | 1076 | int slot, off, new_alloc; |
| 1077 | unsigned long flags; | ||
| 1050 | 1078 | ||
| 1051 | if (unlikely(!size || size > PCPU_MIN_UNIT_SIZE || align > PAGE_SIZE)) { | 1079 | if (unlikely(!size || size > PCPU_MIN_UNIT_SIZE || align > PAGE_SIZE)) { |
| 1052 | WARN(true, "illegal size (%zu) or align (%zu) for " | 1080 | WARN(true, "illegal size (%zu) or align (%zu) for " |
| @@ -1055,19 +1083,30 @@ static void *pcpu_alloc(size_t size, size_t align, bool reserved) | |||
| 1055 | } | 1083 | } |
| 1056 | 1084 | ||
| 1057 | mutex_lock(&pcpu_alloc_mutex); | 1085 | mutex_lock(&pcpu_alloc_mutex); |
| 1058 | spin_lock_irq(&pcpu_lock); | 1086 | spin_lock_irqsave(&pcpu_lock, flags); |
| 1059 | 1087 | ||
| 1060 | /* serve reserved allocations from the reserved chunk if available */ | 1088 | /* serve reserved allocations from the reserved chunk if available */ |
| 1061 | if (reserved && pcpu_reserved_chunk) { | 1089 | if (reserved && pcpu_reserved_chunk) { |
| 1062 | chunk = pcpu_reserved_chunk; | 1090 | chunk = pcpu_reserved_chunk; |
| 1063 | if (size > chunk->contig_hint || | 1091 | |
| 1064 | pcpu_extend_area_map(chunk) < 0) { | 1092 | if (size > chunk->contig_hint) { |
| 1065 | err = "failed to extend area map of reserved chunk"; | 1093 | err = "alloc from reserved chunk failed"; |
| 1066 | goto fail_unlock; | 1094 | goto fail_unlock; |
| 1067 | } | 1095 | } |
| 1096 | |||
| 1097 | while ((new_alloc = pcpu_need_to_extend(chunk))) { | ||
| 1098 | spin_unlock_irqrestore(&pcpu_lock, flags); | ||
| 1099 | if (pcpu_extend_area_map(chunk, new_alloc) < 0) { | ||
| 1100 | err = "failed to extend area map of reserved chunk"; | ||
| 1101 | goto fail_unlock_mutex; | ||
| 1102 | } | ||
| 1103 | spin_lock_irqsave(&pcpu_lock, flags); | ||
| 1104 | } | ||
| 1105 | |||
| 1068 | off = pcpu_alloc_area(chunk, size, align); | 1106 | off = pcpu_alloc_area(chunk, size, align); |
| 1069 | if (off >= 0) | 1107 | if (off >= 0) |
| 1070 | goto area_found; | 1108 | goto area_found; |
| 1109 | |||
| 1071 | err = "alloc from reserved chunk failed"; | 1110 | err = "alloc from reserved chunk failed"; |
| 1072 | goto fail_unlock; | 1111 | goto fail_unlock; |
| 1073 | } | 1112 | } |
| @@ -1079,14 +1118,20 @@ restart: | |||
| 1079 | if (size > chunk->contig_hint) | 1118 | if (size > chunk->contig_hint) |
| 1080 | continue; | 1119 | continue; |
| 1081 | 1120 | ||
| 1082 | switch (pcpu_extend_area_map(chunk)) { | 1121 | new_alloc = pcpu_need_to_extend(chunk); |
| 1083 | case 0: | 1122 | if (new_alloc) { |
| 1084 | break; | 1123 | spin_unlock_irqrestore(&pcpu_lock, flags); |
| 1085 | case 1: | 1124 | if (pcpu_extend_area_map(chunk, |
| 1086 | goto restart; /* pcpu_lock dropped, restart */ | 1125 | new_alloc) < 0) { |
| 1087 | default: | 1126 | err = "failed to extend area map"; |
| 1088 | err = "failed to extend area map"; | 1127 | goto fail_unlock_mutex; |
| 1089 | goto fail_unlock; | 1128 | } |
| 1129 | spin_lock_irqsave(&pcpu_lock, flags); | ||
| 1130 | /* | ||
| 1131 | * pcpu_lock has been dropped, need to | ||
| 1132 | * restart cpu_slot list walking. | ||
| 1133 | */ | ||
| 1134 | goto restart; | ||
| 1090 | } | 1135 | } |
| 1091 | 1136 | ||
| 1092 | off = pcpu_alloc_area(chunk, size, align); | 1137 | off = pcpu_alloc_area(chunk, size, align); |
| @@ -1096,7 +1141,7 @@ restart: | |||
| 1096 | } | 1141 | } |
| 1097 | 1142 | ||
| 1098 | /* hmmm... no space left, create a new chunk */ | 1143 | /* hmmm... no space left, create a new chunk */ |
| 1099 | spin_unlock_irq(&pcpu_lock); | 1144 | spin_unlock_irqrestore(&pcpu_lock, flags); |
| 1100 | 1145 | ||
| 1101 | chunk = alloc_pcpu_chunk(); | 1146 | chunk = alloc_pcpu_chunk(); |
| 1102 | if (!chunk) { | 1147 | if (!chunk) { |
| @@ -1104,16 +1149,16 @@ restart: | |||
| 1104 | goto fail_unlock_mutex; | 1149 | goto fail_unlock_mutex; |
| 1105 | } | 1150 | } |
| 1106 | 1151 | ||
| 1107 | spin_lock_irq(&pcpu_lock); | 1152 | spin_lock_irqsave(&pcpu_lock, flags); |
| 1108 | pcpu_chunk_relocate(chunk, -1); | 1153 | pcpu_chunk_relocate(chunk, -1); |
| 1109 | goto restart; | 1154 | goto restart; |
| 1110 | 1155 | ||
| 1111 | area_found: | 1156 | area_found: |
| 1112 | spin_unlock_irq(&pcpu_lock); | 1157 | spin_unlock_irqrestore(&pcpu_lock, flags); |
| 1113 | 1158 | ||
| 1114 | /* populate, map and clear the area */ | 1159 | /* populate, map and clear the area */ |
| 1115 | if (pcpu_populate_chunk(chunk, off, size)) { | 1160 | if (pcpu_populate_chunk(chunk, off, size)) { |
| 1116 | spin_lock_irq(&pcpu_lock); | 1161 | spin_lock_irqsave(&pcpu_lock, flags); |
| 1117 | pcpu_free_area(chunk, off); | 1162 | pcpu_free_area(chunk, off); |
| 1118 | err = "failed to populate"; | 1163 | err = "failed to populate"; |
| 1119 | goto fail_unlock; | 1164 | goto fail_unlock; |
| @@ -1125,7 +1170,7 @@ area_found: | |||
| 1125 | return __addr_to_pcpu_ptr(chunk->base_addr + off); | 1170 | return __addr_to_pcpu_ptr(chunk->base_addr + off); |
| 1126 | 1171 | ||
| 1127 | fail_unlock: | 1172 | fail_unlock: |
| 1128 | spin_unlock_irq(&pcpu_lock); | 1173 | spin_unlock_irqrestore(&pcpu_lock, flags); |
| 1129 | fail_unlock_mutex: | 1174 | fail_unlock_mutex: |
| 1130 | mutex_unlock(&pcpu_alloc_mutex); | 1175 | mutex_unlock(&pcpu_alloc_mutex); |
| 1131 | if (warn_limit) { | 1176 | if (warn_limit) { |
