e74e396204
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 <tj@kernel.org> Acked-by: Rusty Russell <rusty@rustcorp.com.au> Acked-by: David S. Miller <davem@davemloft.net> Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Reviewed-by: Christoph Lameter <cl@linux.com> Cc: Paul Mundt <lethal@linux-sh.org> Cc: Russell King <rmk@arm.linux.org.uk> Cc: Mikael Starvik <starvik@axis.com> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: Bryan Wu <cooloney@kernel.org> Cc: Kyle McMartin <kyle@mcmartin.ca> Cc: Matthew Wilcox <matthew@wil.cx> Cc: Grant Grundler <grundler@parisc-linux.org> Cc: Hirokazu Takata <takata@linux-m32r.org> Cc: Richard Henderson <rth@twiddle.net> Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru> Cc: Heiko Carstens <heiko.carstens@de.ibm.com> Cc: Ingo Molnar <mingo@elte.hu>
191 lines
5.2 KiB
C
191 lines
5.2 KiB
C
#ifndef __LINUX_PERCPU_H
|
|
#define __LINUX_PERCPU_H
|
|
|
|
#include <linux/preempt.h>
|
|
#include <linux/slab.h> /* For kmalloc() */
|
|
#include <linux/smp.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/pfn.h>
|
|
|
|
#include <asm/percpu.h>
|
|
|
|
/* enough to cover all DEFINE_PER_CPUs in modules */
|
|
#ifdef CONFIG_MODULES
|
|
#define PERCPU_MODULE_RESERVE (8 << 10)
|
|
#else
|
|
#define PERCPU_MODULE_RESERVE 0
|
|
#endif
|
|
|
|
#ifndef PERCPU_ENOUGH_ROOM
|
|
#define PERCPU_ENOUGH_ROOM \
|
|
(ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES) + \
|
|
PERCPU_MODULE_RESERVE)
|
|
#endif
|
|
|
|
/*
|
|
* Must be an lvalue. Since @var must be a simple identifier,
|
|
* we force a syntax error here if it isn't.
|
|
*/
|
|
#define get_cpu_var(var) (*({ \
|
|
extern int simple_identifier_##var(void); \
|
|
preempt_disable(); \
|
|
&__get_cpu_var(var); }))
|
|
#define put_cpu_var(var) preempt_enable()
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
#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)
|
|
|
|
/*
|
|
* PERCPU_DYNAMIC_RESERVE indicates the amount of free area to piggy
|
|
* back on the first chunk for dynamic percpu allocation if arch is
|
|
* manually allocating and mapping it for faster access (as a part of
|
|
* large page mapping for example).
|
|
*
|
|
* The following values give between one and two pages of free space
|
|
* after typical minimal boot (2-way SMP, single disk and NIC) with
|
|
* both defconfig and a distro config on x86_64 and 32. More
|
|
* intelligent way to determine this would be nice.
|
|
*/
|
|
#if BITS_PER_LONG > 32
|
|
#define PERCPU_DYNAMIC_RESERVE (20 << 10)
|
|
#else
|
|
#define PERCPU_DYNAMIC_RESERVE (12 << 10)
|
|
#endif
|
|
|
|
extern void *pcpu_base_addr;
|
|
|
|
typedef struct page * (*pcpu_get_page_fn_t)(unsigned int cpu, int pageno);
|
|
typedef void (*pcpu_populate_pte_fn_t)(unsigned long addr);
|
|
|
|
extern size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn,
|
|
size_t static_size, size_t reserved_size,
|
|
ssize_t dyn_size, ssize_t unit_size,
|
|
void *base_addr,
|
|
pcpu_populate_pte_fn_t populate_pte_fn);
|
|
|
|
extern ssize_t __init pcpu_embed_first_chunk(
|
|
size_t static_size, size_t reserved_size,
|
|
ssize_t dyn_size, ssize_t unit_size);
|
|
|
|
/*
|
|
* Use this to get to a cpu's version of the per-cpu object
|
|
* dynamically allocated. Non-atomic access to the current CPU's
|
|
* version should probably be combined with get_cpu()/put_cpu().
|
|
*/
|
|
#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu)))
|
|
|
|
extern void *__alloc_reserved_percpu(size_t size, size_t align);
|
|
|
|
#else /* CONFIG_HAVE_LEGACY_PER_CPU_AREA */
|
|
|
|
struct percpu_data {
|
|
void *ptrs[1];
|
|
};
|
|
|
|
/* pointer disguising messes up the kmemleak objects tracking */
|
|
#ifndef CONFIG_DEBUG_KMEMLEAK
|
|
#define __percpu_disguise(pdata) (struct percpu_data *)~(unsigned long)(pdata)
|
|
#else
|
|
#define __percpu_disguise(pdata) (struct percpu_data *)(pdata)
|
|
#endif
|
|
|
|
#define per_cpu_ptr(ptr, cpu) \
|
|
({ \
|
|
struct percpu_data *__p = __percpu_disguise(ptr); \
|
|
(__typeof__(ptr))__p->ptrs[(cpu)]; \
|
|
})
|
|
|
|
#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); })
|
|
|
|
static inline void *__alloc_percpu(size_t size, size_t align)
|
|
{
|
|
/*
|
|
* Can't easily make larger alignment work with kmalloc. WARN
|
|
* on it. Larger alignment should only be used for module
|
|
* percpu sections on SMP for which this path isn't used.
|
|
*/
|
|
WARN_ON_ONCE(align > SMP_CACHE_BYTES);
|
|
return kzalloc(size, GFP_KERNEL);
|
|
}
|
|
|
|
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), \
|
|
__alignof__(type))
|
|
|
|
/*
|
|
* Optional methods for optimized non-lvalue per-cpu variable access.
|
|
*
|
|
* @var can be a percpu variable or a field of it and its size should
|
|
* equal char, int or long. percpu_read() evaluates to a lvalue and
|
|
* all others to void.
|
|
*
|
|
* These operations are guaranteed to be atomic w.r.t. preemption.
|
|
* The generic versions use plain get/put_cpu_var(). Archs are
|
|
* encouraged to implement single-instruction alternatives which don't
|
|
* require preemption protection.
|
|
*/
|
|
#ifndef percpu_read
|
|
# define percpu_read(var) \
|
|
({ \
|
|
typeof(per_cpu_var(var)) __tmp_var__; \
|
|
__tmp_var__ = get_cpu_var(var); \
|
|
put_cpu_var(var); \
|
|
__tmp_var__; \
|
|
})
|
|
#endif
|
|
|
|
#define __percpu_generic_to_op(var, val, op) \
|
|
do { \
|
|
get_cpu_var(var) op val; \
|
|
put_cpu_var(var); \
|
|
} while (0)
|
|
|
|
#ifndef percpu_write
|
|
# define percpu_write(var, val) __percpu_generic_to_op(var, (val), =)
|
|
#endif
|
|
|
|
#ifndef percpu_add
|
|
# define percpu_add(var, val) __percpu_generic_to_op(var, (val), +=)
|
|
#endif
|
|
|
|
#ifndef percpu_sub
|
|
# define percpu_sub(var, val) __percpu_generic_to_op(var, (val), -=)
|
|
#endif
|
|
|
|
#ifndef percpu_and
|
|
# define percpu_and(var, val) __percpu_generic_to_op(var, (val), &=)
|
|
#endif
|
|
|
|
#ifndef percpu_or
|
|
# define percpu_or(var, val) __percpu_generic_to_op(var, (val), |=)
|
|
#endif
|
|
|
|
#ifndef percpu_xor
|
|
# define percpu_xor(var, val) __percpu_generic_to_op(var, (val), ^=)
|
|
#endif
|
|
|
|
#endif /* __LINUX_PERCPU_H */
|