Skip to content

Commit 099a19d

Browse files
committed
percpu: allow limited allocation before slab is online
This patch updates percpu allocator such that it can serve limited amount of allocation before slab comes online. This is primarily to allow slab to depend on working percpu allocator. Two parameters, PERCPU_DYNAMIC_EARLY_SIZE and SLOTS, determine how much memory space and allocation map slots are reserved. If this reserved area is exhausted, WARN_ON_ONCE() will trigger and allocation will fail till slab comes online. The following changes are made to implement early alloc. * pcpu_mem_alloc() now checks slab_is_available() * Chunks are allocated using pcpu_mem_alloc() * Init paths make sure ai->dyn_size is at least as large as PERCPU_DYNAMIC_EARLY_SIZE. * Initial alloc maps are allocated in __initdata and copied to kmalloc'd areas once slab is online. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Christoph Lameter <cl@linux-foundation.org>
1 parent 4ba6ce2 commit 099a19d

File tree

3 files changed

+54
-12
lines changed

3 files changed

+54
-12
lines changed

include/linux/percpu.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@
4444
/* minimum unit size, also is the maximum supported allocation size */
4545
#define PCPU_MIN_UNIT_SIZE PFN_ALIGN(64 << 10)
4646

47+
/*
48+
* Percpu allocator can serve percpu allocations before slab is
49+
* initialized which allows slab to depend on the percpu allocator.
50+
* The following two parameters decide how much resource to
51+
* preallocate for this. Keep PERCPU_DYNAMIC_RESERVE equal to or
52+
* larger than PERCPU_DYNAMIC_EARLY_SIZE.
53+
*/
54+
#define PERCPU_DYNAMIC_EARLY_SLOTS 128
55+
#define PERCPU_DYNAMIC_EARLY_SIZE (12 << 10)
56+
4757
/*
4858
* PERCPU_DYNAMIC_RESERVE indicates the amount of free area to piggy
4959
* back on the first chunk for dynamic percpu allocation if arch is
@@ -135,6 +145,7 @@ extern bool is_kernel_percpu_address(unsigned long addr);
135145
#ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA
136146
extern void __init setup_per_cpu_areas(void);
137147
#endif
148+
extern void __init percpu_init_late(void);
138149

139150
#else /* CONFIG_SMP */
140151

@@ -148,6 +159,8 @@ static inline bool is_kernel_percpu_address(unsigned long addr)
148159

149160
static inline void __init setup_per_cpu_areas(void) { }
150161

162+
static inline void __init percpu_init_late(void) { }
163+
151164
static inline void *pcpu_lpage_remapped(void *kaddr)
152165
{
153166
return NULL;

init/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ static void __init mm_init(void)
522522
page_cgroup_init_flatmem();
523523
mem_init();
524524
kmem_cache_init();
525+
percpu_init_late();
525526
pgtable_cache_init();
526527
vmalloc_init();
527528
}

mm/percpu.c

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ static void __maybe_unused pcpu_next_pop(struct pcpu_chunk *chunk,
282282
*/
283283
static void *pcpu_mem_alloc(size_t size)
284284
{
285+
if (WARN_ON_ONCE(!slab_is_available()))
286+
return NULL;
287+
285288
if (size <= PAGE_SIZE)
286289
return kzalloc(size, GFP_KERNEL);
287290
else {
@@ -392,13 +395,6 @@ static int pcpu_extend_area_map(struct pcpu_chunk *chunk, int new_alloc)
392395
old_size = chunk->map_alloc * sizeof(chunk->map[0]);
393396
memcpy(new, chunk->map, old_size);
394397

395-
/*
396-
* map_alloc < PCPU_DFL_MAP_ALLOC indicates that the chunk is
397-
* one of the first chunks and still using static map.
398-
*/
399-
if (chunk->map_alloc >= PCPU_DFL_MAP_ALLOC)
400-
old = chunk->map;
401-
402398
chunk->map_alloc = new_alloc;
403399
chunk->map = new;
404400
new = NULL;
@@ -604,7 +600,7 @@ static struct pcpu_chunk *pcpu_alloc_chunk(void)
604600
{
605601
struct pcpu_chunk *chunk;
606602

607-
chunk = kzalloc(pcpu_chunk_struct_size, GFP_KERNEL);
603+
chunk = pcpu_mem_alloc(pcpu_chunk_struct_size);
608604
if (!chunk)
609605
return NULL;
610606

@@ -1109,7 +1105,9 @@ static struct pcpu_alloc_info * __init pcpu_build_alloc_info(
11091105
memset(group_map, 0, sizeof(group_map));
11101106
memset(group_cnt, 0, sizeof(group_cnt));
11111107

1112-
size_sum = PFN_ALIGN(static_size + reserved_size + dyn_size);
1108+
/* calculate size_sum and ensure dyn_size is enough for early alloc */
1109+
size_sum = PFN_ALIGN(static_size + reserved_size +
1110+
max_t(size_t, dyn_size, PERCPU_DYNAMIC_EARLY_SIZE));
11131111
dyn_size = size_sum - static_size - reserved_size;
11141112

11151113
/*
@@ -1338,7 +1336,8 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
13381336
void *base_addr)
13391337
{
13401338
static char cpus_buf[4096] __initdata;
1341-
static int smap[2], dmap[2];
1339+
static int smap[PERCPU_DYNAMIC_EARLY_SLOTS] __initdata;
1340+
static int dmap[PERCPU_DYNAMIC_EARLY_SLOTS] __initdata;
13421341
size_t dyn_size = ai->dyn_size;
13431342
size_t size_sum = ai->static_size + ai->reserved_size + dyn_size;
13441343
struct pcpu_chunk *schunk, *dchunk = NULL;
@@ -1361,14 +1360,13 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
13611360
} while (0)
13621361

13631362
/* sanity checks */
1364-
BUILD_BUG_ON(ARRAY_SIZE(smap) >= PCPU_DFL_MAP_ALLOC ||
1365-
ARRAY_SIZE(dmap) >= PCPU_DFL_MAP_ALLOC);
13661363
PCPU_SETUP_BUG_ON(ai->nr_groups <= 0);
13671364
PCPU_SETUP_BUG_ON(!ai->static_size);
13681365
PCPU_SETUP_BUG_ON(!base_addr);
13691366
PCPU_SETUP_BUG_ON(ai->unit_size < size_sum);
13701367
PCPU_SETUP_BUG_ON(ai->unit_size & ~PAGE_MASK);
13711368
PCPU_SETUP_BUG_ON(ai->unit_size < PCPU_MIN_UNIT_SIZE);
1369+
PCPU_SETUP_BUG_ON(ai->dyn_size < PERCPU_DYNAMIC_EARLY_SIZE);
13721370
PCPU_SETUP_BUG_ON(pcpu_verify_alloc_info(ai) < 0);
13731371

13741372
/* process group information and build config tables accordingly */
@@ -1806,3 +1804,33 @@ void __init setup_per_cpu_areas(void)
18061804
__per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
18071805
}
18081806
#endif /* CONFIG_HAVE_SETUP_PER_CPU_AREA */
1807+
1808+
/*
1809+
* First and reserved chunks are initialized with temporary allocation
1810+
* map in initdata so that they can be used before slab is online.
1811+
* This function is called after slab is brought up and replaces those
1812+
* with properly allocated maps.
1813+
*/
1814+
void __init percpu_init_late(void)
1815+
{
1816+
struct pcpu_chunk *target_chunks[] =
1817+
{ pcpu_first_chunk, pcpu_reserved_chunk, NULL };
1818+
struct pcpu_chunk *chunk;
1819+
unsigned long flags;
1820+
int i;
1821+
1822+
for (i = 0; (chunk = target_chunks[i]); i++) {
1823+
int *map;
1824+
const size_t size = PERCPU_DYNAMIC_EARLY_SLOTS * sizeof(map[0]);
1825+
1826+
BUILD_BUG_ON(size > PAGE_SIZE);
1827+
1828+
map = pcpu_mem_alloc(size);
1829+
BUG_ON(!map);
1830+
1831+
spin_lock_irqsave(&pcpu_lock, flags);
1832+
memcpy(map, chunk->map, size);
1833+
chunk->map = map;
1834+
spin_unlock_irqrestore(&pcpu_lock, flags);
1835+
}
1836+
}

0 commit comments

Comments
 (0)