aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-06-27 12:50:00 -0400
committerTejun Heo <tj@kernel.org>2010-06-27 12:50:00 -0400
commit099a19d91ca429944743d51bef8fee240e94d8e3 (patch)
tree55bdc0f25ecbf38240782fb1d9a80d33c0100eb6
parent4ba6ce250e406b20bcd6f0f3aed6b3d80965e6c2 (diff)
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>
-rw-r--r--include/linux/percpu.h13
-rw-r--r--init/main.c1
-rw-r--r--mm/percpu.c52
3 files changed, 54 insertions, 12 deletions
diff --git a/include/linux/percpu.h b/include/linux/percpu.h
index 3ffd05e550d..b8b9084527b 100644
--- a/include/linux/percpu.h
+++ b/include/linux/percpu.h
@@ -45,6 +45,16 @@
45#define PCPU_MIN_UNIT_SIZE PFN_ALIGN(64 << 10) 45#define PCPU_MIN_UNIT_SIZE PFN_ALIGN(64 << 10)
46 46
47/* 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
57/*
48 * PERCPU_DYNAMIC_RESERVE indicates the amount of free area to piggy 58 * PERCPU_DYNAMIC_RESERVE indicates the amount of free area to piggy
49 * back on the first chunk for dynamic percpu allocation if arch is 59 * back on the first chunk for dynamic percpu allocation if arch is
50 * manually allocating and mapping it for faster access (as a part of 60 * manually allocating and mapping it for faster access (as a part of
@@ -135,6 +145,7 @@ extern bool is_kernel_percpu_address(unsigned long addr);
135#ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA 145#ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA
136extern void __init setup_per_cpu_areas(void); 146extern void __init setup_per_cpu_areas(void);
137#endif 147#endif
148extern void __init percpu_init_late(void);
138 149
139#else /* CONFIG_SMP */ 150#else /* CONFIG_SMP */
140 151
@@ -148,6 +159,8 @@ static inline bool is_kernel_percpu_address(unsigned long addr)
148 159
149static inline void __init setup_per_cpu_areas(void) { } 160static inline void __init setup_per_cpu_areas(void) { }
150 161
162static inline void __init percpu_init_late(void) { }
163
151static inline void *pcpu_lpage_remapped(void *kaddr) 164static inline void *pcpu_lpage_remapped(void *kaddr)
152{ 165{
153 return NULL; 166 return NULL;
diff --git a/init/main.c b/init/main.c
index 3bdb152f412..3ff8dd0fb51 100644
--- a/init/main.c
+++ b/init/main.c
@@ -522,6 +522,7 @@ static void __init mm_init(void)
522 page_cgroup_init_flatmem(); 522 page_cgroup_init_flatmem();
523 mem_init(); 523 mem_init();
524 kmem_cache_init(); 524 kmem_cache_init();
525 percpu_init_late();
525 pgtable_cache_init(); 526 pgtable_cache_init();
526 vmalloc_init(); 527 vmalloc_init();
527} 528}
diff --git a/mm/percpu.c b/mm/percpu.c
index c3e7010c6d7..e61dc2cc587 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -282,6 +282,9 @@ static void __maybe_unused pcpu_next_pop(struct pcpu_chunk *chunk,
282 */ 282 */
283static void *pcpu_mem_alloc(size_t size) 283static void *pcpu_mem_alloc(size_t size)
284{ 284{
285 if (WARN_ON_ONCE(!slab_is_available()))
286 return NULL;
287
285 if (size <= PAGE_SIZE) 288 if (size <= PAGE_SIZE)
286 return kzalloc(size, GFP_KERNEL); 289 return kzalloc(size, GFP_KERNEL);
287 else { 290 else {
@@ -392,13 +395,6 @@ static int pcpu_extend_area_map(struct pcpu_chunk *chunk, int new_alloc)
392 old_size = chunk->map_alloc * sizeof(chunk->map[0]); 395 old_size = chunk->map_alloc * sizeof(chunk->map[0]);
393 memcpy(new, chunk->map, old_size); 396 memcpy(new, chunk->map, old_size);
394 397
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
402 chunk->map_alloc = new_alloc; 398 chunk->map_alloc = new_alloc;
403 chunk->map = new; 399 chunk->map = new;
404 new = NULL; 400 new = NULL;
@@ -604,7 +600,7 @@ static struct pcpu_chunk *pcpu_alloc_chunk(void)
604{ 600{
605 struct pcpu_chunk *chunk; 601 struct pcpu_chunk *chunk;
606 602
607 chunk = kzalloc(pcpu_chunk_struct_size, GFP_KERNEL); 603 chunk = pcpu_mem_alloc(pcpu_chunk_struct_size);
608 if (!chunk) 604 if (!chunk)
609 return NULL; 605 return NULL;
610 606
@@ -1109,7 +1105,9 @@ static struct pcpu_alloc_info * __init pcpu_build_alloc_info(
1109 memset(group_map, 0, sizeof(group_map)); 1105 memset(group_map, 0, sizeof(group_map));
1110 memset(group_cnt, 0, sizeof(group_cnt)); 1106 memset(group_cnt, 0, sizeof(group_cnt));
1111 1107
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));
1113 dyn_size = size_sum - static_size - reserved_size; 1111 dyn_size = size_sum - static_size - reserved_size;
1114 1112
1115 /* 1113 /*
@@ -1338,7 +1336,8 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
1338 void *base_addr) 1336 void *base_addr)
1339{ 1337{
1340 static char cpus_buf[4096] __initdata; 1338 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;
1342 size_t dyn_size = ai->dyn_size; 1341 size_t dyn_size = ai->dyn_size;
1343 size_t size_sum = ai->static_size + ai->reserved_size + dyn_size; 1342 size_t size_sum = ai->static_size + ai->reserved_size + dyn_size;
1344 struct pcpu_chunk *schunk, *dchunk = NULL; 1343 struct pcpu_chunk *schunk, *dchunk = NULL;
@@ -1361,14 +1360,13 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
1361} while (0) 1360} while (0)
1362 1361
1363 /* sanity checks */ 1362 /* sanity checks */
1364 BUILD_BUG_ON(ARRAY_SIZE(smap) >= PCPU_DFL_MAP_ALLOC ||
1365 ARRAY_SIZE(dmap) >= PCPU_DFL_MAP_ALLOC);
1366 PCPU_SETUP_BUG_ON(ai->nr_groups <= 0); 1363 PCPU_SETUP_BUG_ON(ai->nr_groups <= 0);
1367 PCPU_SETUP_BUG_ON(!ai->static_size); 1364 PCPU_SETUP_BUG_ON(!ai->static_size);
1368 PCPU_SETUP_BUG_ON(!base_addr); 1365 PCPU_SETUP_BUG_ON(!base_addr);
1369 PCPU_SETUP_BUG_ON(ai->unit_size < size_sum); 1366 PCPU_SETUP_BUG_ON(ai->unit_size < size_sum);
1370 PCPU_SETUP_BUG_ON(ai->unit_size & ~PAGE_MASK); 1367 PCPU_SETUP_BUG_ON(ai->unit_size & ~PAGE_MASK);
1371 PCPU_SETUP_BUG_ON(ai->unit_size < PCPU_MIN_UNIT_SIZE); 1368 PCPU_SETUP_BUG_ON(ai->unit_size < PCPU_MIN_UNIT_SIZE);
1369 PCPU_SETUP_BUG_ON(ai->dyn_size < PERCPU_DYNAMIC_EARLY_SIZE);
1372 PCPU_SETUP_BUG_ON(pcpu_verify_alloc_info(ai) < 0); 1370 PCPU_SETUP_BUG_ON(pcpu_verify_alloc_info(ai) < 0);
1373 1371
1374 /* process group information and build config tables accordingly */ 1372 /* process group information and build config tables accordingly */
@@ -1806,3 +1804,33 @@ void __init setup_per_cpu_areas(void)
1806 __per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu]; 1804 __per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
1807} 1805}
1808#endif /* CONFIG_HAVE_SETUP_PER_CPU_AREA */ 1806#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 */
1814void __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}