Skip to content

Commit

Permalink
percpu: use dynamic percpu allocator as the default percpu allocator
Browse files Browse the repository at this point in the history
This patch makes most !CONFIG_HAVE_SETUP_PER_CPU_AREA archs use
dynamic percpu allocator.  The first chunk is allocated using
embedding helper and 8k is reserved for modules.  This ensures that
the new allocator behaves almost identically to the original allocator
as long as static percpu variables are concerned, so it shouldn't
introduce much breakage.

s390 and alpha use custom SHIFT_PERCPU_PTR() to work around addressing
range limit the addressing model imposes.  Unfortunately, this breaks
if the address is specified using a variable, so for now, the two
archs aren't converted.

The following architectures are affected by this change.

* sh
* arm
* cris
* mips
* sparc(32)
* blackfin
* avr32
* parisc (broken, under investigation)
* m32r
* powerpc(32)

As this change makes the dynamic allocator the default one,
CONFIG_HAVE_DYNAMIC_PER_CPU_AREA is replaced with its invert -
CONFIG_HAVE_LEGACY_PER_CPU_AREA, which is added to yet-to-be converted
archs.  These archs implement their own setup_per_cpu_areas() and the
conversion is not trivial.

* powerpc(64)
* sparc(64)
* ia64
* alpha
* s390

Boot and batch alloc/free tests on x86_32 with debug code (x86_32
doesn't use default first chunk initialization).  Compile tested on
sparc(32), powerpc(32), arm and alpha.

Kyle McMartin reported that this change breaks parisc.  The problem is
still under investigation and he is okay with pushing this patch
forward and fixing parisc later.

[ Impact: use dynamic allocator for most archs w/o custom percpu setup ]

Signed-off-by: Tejun Heo <[email protected]>
Acked-by: Rusty Russell <[email protected]>
Acked-by: David S. Miller <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
Acked-by: Martin Schwidefsky <[email protected]>
Reviewed-by: Christoph Lameter <[email protected]>
Cc: Paul Mundt <[email protected]>
Cc: Russell King <[email protected]>
Cc: Mikael Starvik <[email protected]>
Cc: Ralf Baechle <[email protected]>
Cc: Bryan Wu <[email protected]>
Cc: Kyle McMartin <[email protected]>
Cc: Matthew Wilcox <[email protected]>
Cc: Grant Grundler <[email protected]>
Cc: Hirokazu Takata <[email protected]>
Cc: Richard Henderson <[email protected]>
Cc: Ivan Kokshaysky <[email protected]>
Cc: Heiko Carstens <[email protected]>
Cc: Ingo Molnar <[email protected]>
  • Loading branch information
htejun committed Jun 24, 2009
1 parent 0017c86 commit e74e396
Show file tree
Hide file tree
Showing 12 changed files with 95 additions and 35 deletions.
3 changes: 3 additions & 0 deletions arch/alpha/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ config AUTO_IRQ_AFFINITY
depends on SMP
default y

config HAVE_LEGACY_PER_CPU_AREA
def_bool y

source "init/Kconfig"
source "kernel/Kconfig.freezer"

Expand Down
3 changes: 3 additions & 0 deletions arch/ia64/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ config GENERIC_TIME_VSYSCALL
bool
default y

config HAVE_LEGACY_PER_CPU_AREA
def_bool y

config HAVE_SETUP_PER_CPU_AREA
def_bool y

Expand Down
3 changes: 3 additions & 0 deletions arch/powerpc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ config GENERIC_HARDIRQS_NO__DO_IRQ
bool
default y

config HAVE_LEGACY_PER_CPU_AREA
def_bool PPC64

config HAVE_SETUP_PER_CPU_AREA
def_bool PPC64

Expand Down
3 changes: 3 additions & 0 deletions arch/s390/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ config VIRT_CPU_ACCOUNTING
config ARCH_SUPPORTS_DEBUG_PAGEALLOC
def_bool y

config HAVE_LEGACY_PER_CPU_AREA
def_bool y

mainmenu "Linux Kernel Configuration"

config S390
Expand Down
3 changes: 3 additions & 0 deletions arch/sparc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ config AUDIT_ARCH
bool
default y

config HAVE_LEGACY_PER_CPU_AREA
def_bool y if SPARC64

config HAVE_SETUP_PER_CPU_AREA
def_bool y if SPARC64

Expand Down
3 changes: 0 additions & 3 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,6 @@ config ARCH_HAS_CACHE_LINE_SIZE
config HAVE_SETUP_PER_CPU_AREA
def_bool y

config HAVE_DYNAMIC_PER_CPU_AREA
def_bool y

config HAVE_CPUMASK_OF_CPU_MAP
def_bool X86_64_SMP

Expand Down
12 changes: 9 additions & 3 deletions include/linux/percpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

#ifdef CONFIG_SMP

#ifdef CONFIG_HAVE_DYNAMIC_PER_CPU_AREA
#ifndef CONFIG_HAVE_LEGACY_PER_CPU_AREA

/* minimum unit size, also is the maximum supported allocation size */
#define PCPU_MIN_UNIT_SIZE PFN_ALIGN(64 << 10)
Expand Down Expand Up @@ -80,7 +80,7 @@ extern ssize_t __init pcpu_embed_first_chunk(

extern void *__alloc_reserved_percpu(size_t size, size_t align);

#else /* CONFIG_HAVE_DYNAMIC_PER_CPU_AREA */
#else /* CONFIG_HAVE_LEGACY_PER_CPU_AREA */

struct percpu_data {
void *ptrs[1];
Expand All @@ -99,11 +99,15 @@ struct percpu_data {
(__typeof__(ptr))__p->ptrs[(cpu)]; \
})

#endif /* CONFIG_HAVE_DYNAMIC_PER_CPU_AREA */
#endif /* CONFIG_HAVE_LEGACY_PER_CPU_AREA */

extern void *__alloc_percpu(size_t size, size_t align);
extern void free_percpu(void *__pdata);

#ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA
extern void __init setup_per_cpu_areas(void);
#endif

#else /* CONFIG_SMP */

#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); (ptr); })
Expand All @@ -124,6 +128,8 @@ static inline void free_percpu(void *p)
kfree(p);
}

static inline void __init setup_per_cpu_areas(void) { }

#endif /* CONFIG_SMP */

#define alloc_percpu(type) (type *)__alloc_percpu(sizeof(type), \
Expand Down
24 changes: 0 additions & 24 deletions init/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,6 @@ static void __init smp_init(void)
#define smp_init() do { } while (0)
#endif

static inline void setup_per_cpu_areas(void) { }
static inline void setup_nr_cpu_ids(void) { }
static inline void smp_prepare_cpus(unsigned int maxcpus) { }

Expand All @@ -378,29 +377,6 @@ static void __init setup_nr_cpu_ids(void)
nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1;
}

#ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA
unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;

EXPORT_SYMBOL(__per_cpu_offset);

static void __init setup_per_cpu_areas(void)
{
unsigned long size, i;
char *ptr;
unsigned long nr_possible_cpus = num_possible_cpus();

/* Copy section for each CPU (we discard the original) */
size = ALIGN(PERCPU_ENOUGH_ROOM, PAGE_SIZE);
ptr = alloc_bootmem_pages(size * nr_possible_cpus);

for_each_possible_cpu(i) {
__per_cpu_offset[i] = ptr - __per_cpu_start;
memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
ptr += size;
}
}
#endif /* CONFIG_HAVE_SETUP_PER_CPU_AREA */

/* Called by boot processor to activate the rest. */
static void __init smp_init(void)
{
Expand Down
6 changes: 3 additions & 3 deletions kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ EXPORT_SYMBOL_GPL(find_module);

#ifdef CONFIG_SMP

#ifdef CONFIG_HAVE_DYNAMIC_PER_CPU_AREA
#ifndef CONFIG_HAVE_LEGACY_PER_CPU_AREA

static void *percpu_modalloc(unsigned long size, unsigned long align,
const char *name)
Expand All @@ -389,7 +389,7 @@ static void percpu_modfree(void *freeme)
free_percpu(freeme);
}

#else /* ... !CONFIG_HAVE_DYNAMIC_PER_CPU_AREA */
#else /* ... CONFIG_HAVE_LEGACY_PER_CPU_AREA */

/* Number of blocks used and allocated. */
static unsigned int pcpu_num_used, pcpu_num_allocated;
Expand Down Expand Up @@ -535,7 +535,7 @@ static int percpu_modinit(void)
}
__initcall(percpu_modinit);

#endif /* CONFIG_HAVE_DYNAMIC_PER_CPU_AREA */
#endif /* CONFIG_HAVE_LEGACY_PER_CPU_AREA */

static unsigned int find_pcpusec(Elf_Ehdr *hdr,
Elf_Shdr *sechdrs,
Expand Down
2 changes: 1 addition & 1 deletion mm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ obj-$(CONFIG_FAILSLAB) += failslab.o
obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o
obj-$(CONFIG_FS_XIP) += filemap_xip.o
obj-$(CONFIG_MIGRATION) += migrate.o
ifdef CONFIG_HAVE_DYNAMIC_PER_CPU_AREA
ifndef CONFIG_HAVE_LEGACY_PER_CPU_AREA
obj-$(CONFIG_SMP) += percpu.o
else
obj-$(CONFIG_SMP) += allocpercpu.o
Expand Down
28 changes: 28 additions & 0 deletions mm/allocpercpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*/
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/bootmem.h>
#include <asm/sections.h>

#ifndef cache_line_size
#define cache_line_size() L1_CACHE_BYTES
Expand Down Expand Up @@ -147,3 +149,29 @@ void free_percpu(void *__pdata)
kfree(__percpu_disguise(__pdata));
}
EXPORT_SYMBOL_GPL(free_percpu);

/*
* Generic percpu area setup.
*/
#ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA
unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;

EXPORT_SYMBOL(__per_cpu_offset);

void __init setup_per_cpu_areas(void)
{
unsigned long size, i;
char *ptr;
unsigned long nr_possible_cpus = num_possible_cpus();

/* Copy section for each CPU (we discard the original) */
size = ALIGN(PERCPU_ENOUGH_ROOM, PAGE_SIZE);
ptr = alloc_bootmem_pages(size * nr_possible_cpus);

for_each_possible_cpu(i) {
__per_cpu_offset[i] = ptr - __per_cpu_start;
memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
ptr += size;
}
}
#endif /* CONFIG_HAVE_SETUP_PER_CPU_AREA */
40 changes: 39 additions & 1 deletion mm/percpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
*
* To use this allocator, arch code should do the followings.
*
* - define CONFIG_HAVE_DYNAMIC_PER_CPU_AREA
* - drop CONFIG_HAVE_LEGACY_PER_CPU_AREA
*
* - define __addr_to_pcpu_ptr() and __pcpu_ptr_to_addr() to translate
* regular address to percpu pointer and back if they need to be
Expand Down Expand Up @@ -1275,3 +1275,41 @@ ssize_t __init pcpu_embed_first_chunk(size_t static_size, size_t reserved_size,
reserved_size, dyn_size,
pcpue_unit_size, pcpue_ptr, NULL);
}

/*
* Generic percpu area setup.
*
* The embedding helper is used because its behavior closely resembles
* the original non-dynamic generic percpu area setup. This is
* important because many archs have addressing restrictions and might
* fail if the percpu area is located far away from the previous
* location. As an added bonus, in non-NUMA cases, embedding is
* generally a good idea TLB-wise because percpu area can piggy back
* on the physical linear memory mapping which uses large page
* mappings on applicable archs.
*/
#ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA
unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
EXPORT_SYMBOL(__per_cpu_offset);

void __init setup_per_cpu_areas(void)
{
size_t static_size = __per_cpu_end - __per_cpu_start;
ssize_t unit_size;
unsigned long delta;
unsigned int cpu;

/*
* Always reserve area for module percpu variables. That's
* what the legacy allocator did.
*/
unit_size = pcpu_embed_first_chunk(static_size, PERCPU_MODULE_RESERVE,
PERCPU_DYNAMIC_RESERVE, -1);
if (unit_size < 0)
panic("Failed to initialized percpu areas.");

delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
for_each_possible_cpu(cpu)
__per_cpu_offset[cpu] = delta + cpu * unit_size;
}
#endif /* CONFIG_HAVE_SETUP_PER_CPU_AREA */

0 comments on commit e74e396

Please sign in to comment.