Commit 5835d96e authored by Tejun Heo's avatar Tejun Heo

percpu: implement [__]alloc_percpu_gfp()

Now that pcpu_alloc_area() can allocate only from populated areas,
it's easy to add atomic allocation support to [__]alloc_percpu().
Update pcpu_alloc() so that it accepts @gfp and skips all the blocking
operations and allocates only from the populated areas if @gfp doesn't
contain GFP_KERNEL.  New interface functions [__]alloc_percpu_gfp()
are added.

While this means that atomic allocations are possible, this isn't
complete yet as there's no mechanism to ensure that certain amount of
populated areas is kept available and atomic allocations may keep
failing under certain conditions.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
parent e04d3208
...@@ -122,11 +122,16 @@ extern void __init setup_per_cpu_areas(void); ...@@ -122,11 +122,16 @@ extern void __init setup_per_cpu_areas(void);
#endif #endif
extern void __init percpu_init_late(void); extern void __init percpu_init_late(void);
extern void __percpu *__alloc_percpu_gfp(size_t size, size_t align, gfp_t gfp);
extern void __percpu *__alloc_percpu(size_t size, size_t align); extern void __percpu *__alloc_percpu(size_t size, size_t align);
extern void free_percpu(void __percpu *__pdata); extern void free_percpu(void __percpu *__pdata);
extern phys_addr_t per_cpu_ptr_to_phys(void *addr); extern phys_addr_t per_cpu_ptr_to_phys(void *addr);
#define alloc_percpu_gfp(type, gfp) \
(typeof(type) __percpu *)__alloc_percpu_gfp(sizeof(type), \
__alignof__(type), gfp)
#define alloc_percpu(type) \ #define alloc_percpu(type) \
(typeof(type) __percpu *)__alloc_percpu(sizeof(type), __alignof__(type)) (typeof(type) __percpu *)__alloc_percpu(sizeof(type), \
__alignof__(type))
#endif /* __LINUX_PERCPU_H */ #endif /* __LINUX_PERCPU_H */
...@@ -151,11 +151,6 @@ static struct pcpu_chunk *pcpu_first_chunk; ...@@ -151,11 +151,6 @@ static struct pcpu_chunk *pcpu_first_chunk;
static struct pcpu_chunk *pcpu_reserved_chunk; static struct pcpu_chunk *pcpu_reserved_chunk;
static int pcpu_reserved_chunk_limit; static int pcpu_reserved_chunk_limit;
/*
* Free path accesses and alters only the index data structures and can be
* safely called from atomic context. When memory needs to be returned to
* the system, free path schedules reclaim_work.
*/
static DEFINE_SPINLOCK(pcpu_lock); /* all internal data structures */ static DEFINE_SPINLOCK(pcpu_lock); /* all internal data structures */
static DEFINE_MUTEX(pcpu_alloc_mutex); /* chunk create/destroy, [de]pop */ static DEFINE_MUTEX(pcpu_alloc_mutex); /* chunk create/destroy, [de]pop */
...@@ -727,20 +722,21 @@ static struct pcpu_chunk *pcpu_chunk_addr_search(void *addr) ...@@ -727,20 +722,21 @@ static struct pcpu_chunk *pcpu_chunk_addr_search(void *addr)
* @size: size of area to allocate in bytes * @size: size of area to allocate in bytes
* @align: alignment of area (max PAGE_SIZE) * @align: alignment of area (max PAGE_SIZE)
* @reserved: allocate from the reserved chunk if available * @reserved: allocate from the reserved chunk if available
* @gfp: allocation flags
* *
* Allocate percpu area of @size bytes aligned at @align. * Allocate percpu area of @size bytes aligned at @align. If @gfp doesn't
* * contain %GFP_KERNEL, the allocation is atomic.
* CONTEXT:
* Does GFP_KERNEL allocation.
* *
* RETURNS: * RETURNS:
* Percpu pointer to the allocated area on success, NULL on failure. * Percpu pointer to the allocated area on success, NULL on failure.
*/ */
static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved) static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved,
gfp_t gfp)
{ {
static int warn_limit = 10; static int warn_limit = 10;
struct pcpu_chunk *chunk; struct pcpu_chunk *chunk;
const char *err; const char *err;
bool is_atomic = !(gfp & GFP_KERNEL);
int slot, off, new_alloc, cpu, ret; int slot, off, new_alloc, cpu, ret;
unsigned long flags; unsigned long flags;
void __percpu *ptr; void __percpu *ptr;
...@@ -773,14 +769,15 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved) ...@@ -773,14 +769,15 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
while ((new_alloc = pcpu_need_to_extend(chunk))) { while ((new_alloc = pcpu_need_to_extend(chunk))) {
spin_unlock_irqrestore(&pcpu_lock, flags); spin_unlock_irqrestore(&pcpu_lock, flags);
if (pcpu_extend_area_map(chunk, new_alloc) < 0) { if (is_atomic ||
pcpu_extend_area_map(chunk, new_alloc) < 0) {
err = "failed to extend area map of reserved chunk"; err = "failed to extend area map of reserved chunk";
goto fail; goto fail;
} }
spin_lock_irqsave(&pcpu_lock, flags); spin_lock_irqsave(&pcpu_lock, flags);
} }
off = pcpu_alloc_area(chunk, size, align, false); off = pcpu_alloc_area(chunk, size, align, is_atomic);
if (off >= 0) if (off >= 0)
goto area_found; goto area_found;
...@@ -797,6 +794,8 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved) ...@@ -797,6 +794,8 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
new_alloc = pcpu_need_to_extend(chunk); new_alloc = pcpu_need_to_extend(chunk);
if (new_alloc) { if (new_alloc) {
if (is_atomic)
continue;
spin_unlock_irqrestore(&pcpu_lock, flags); spin_unlock_irqrestore(&pcpu_lock, flags);
if (pcpu_extend_area_map(chunk, if (pcpu_extend_area_map(chunk,
new_alloc) < 0) { new_alloc) < 0) {
...@@ -811,7 +810,7 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved) ...@@ -811,7 +810,7 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
goto restart; goto restart;
} }
off = pcpu_alloc_area(chunk, size, align, false); off = pcpu_alloc_area(chunk, size, align, is_atomic);
if (off >= 0) if (off >= 0)
goto area_found; goto area_found;
} }
...@@ -824,6 +823,9 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved) ...@@ -824,6 +823,9 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
* tasks to create chunks simultaneously. Serialize and create iff * tasks to create chunks simultaneously. Serialize and create iff
* there's still no empty chunk after grabbing the mutex. * there's still no empty chunk after grabbing the mutex.
*/ */
if (is_atomic)
goto fail;
mutex_lock(&pcpu_alloc_mutex); mutex_lock(&pcpu_alloc_mutex);
if (list_empty(&pcpu_slot[pcpu_nr_slots - 1])) { if (list_empty(&pcpu_slot[pcpu_nr_slots - 1])) {
...@@ -846,7 +848,7 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved) ...@@ -846,7 +848,7 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
spin_unlock_irqrestore(&pcpu_lock, flags); spin_unlock_irqrestore(&pcpu_lock, flags);
/* populate if not all pages are already there */ /* populate if not all pages are already there */
if (true) { if (!is_atomic) {
int page_start, page_end, rs, re; int page_start, page_end, rs, re;
mutex_lock(&pcpu_alloc_mutex); mutex_lock(&pcpu_alloc_mutex);
...@@ -884,9 +886,9 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved) ...@@ -884,9 +886,9 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
fail_unlock: fail_unlock:
spin_unlock_irqrestore(&pcpu_lock, flags); spin_unlock_irqrestore(&pcpu_lock, flags);
fail: fail:
if (warn_limit) { if (!is_atomic && warn_limit) {
pr_warning("PERCPU: allocation failed, size=%zu align=%zu, " pr_warning("PERCPU: allocation failed, size=%zu align=%zu atomic=%d, %s\n",
"%s\n", size, align, err); size, align, is_atomic, err);
dump_stack(); dump_stack();
if (!--warn_limit) if (!--warn_limit)
pr_info("PERCPU: limit reached, disable warning\n"); pr_info("PERCPU: limit reached, disable warning\n");
...@@ -895,22 +897,34 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved) ...@@ -895,22 +897,34 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
} }
/** /**
* __alloc_percpu - allocate dynamic percpu area * __alloc_percpu_gfp - allocate dynamic percpu area
* @size: size of area to allocate in bytes * @size: size of area to allocate in bytes
* @align: alignment of area (max PAGE_SIZE) * @align: alignment of area (max PAGE_SIZE)
* @gfp: allocation flags
* *
* Allocate zero-filled percpu area of @size bytes aligned at @align. * Allocate zero-filled percpu area of @size bytes aligned at @align. If
* Might sleep. Might trigger writeouts. * @gfp doesn't contain %GFP_KERNEL, the allocation doesn't block and can
* * be called from any context but is a lot more likely to fail.
* CONTEXT:
* Does GFP_KERNEL allocation.
* *
* RETURNS: * RETURNS:
* Percpu pointer to the allocated area on success, NULL on failure. * Percpu pointer to the allocated area on success, NULL on failure.
*/ */
void __percpu *__alloc_percpu_gfp(size_t size, size_t align, gfp_t gfp)
{
return pcpu_alloc(size, align, false, gfp);
}
EXPORT_SYMBOL_GPL(__alloc_percpu_gfp);
/**
* __alloc_percpu - allocate dynamic percpu area
* @size: size of area to allocate in bytes
* @align: alignment of area (max PAGE_SIZE)
*
* Equivalent to __alloc_percpu_gfp(size, align, %GFP_KERNEL).
*/
void __percpu *__alloc_percpu(size_t size, size_t align) void __percpu *__alloc_percpu(size_t size, size_t align)
{ {
return pcpu_alloc(size, align, false); return pcpu_alloc(size, align, false, GFP_KERNEL);
} }
EXPORT_SYMBOL_GPL(__alloc_percpu); EXPORT_SYMBOL_GPL(__alloc_percpu);
...@@ -932,7 +946,7 @@ EXPORT_SYMBOL_GPL(__alloc_percpu); ...@@ -932,7 +946,7 @@ EXPORT_SYMBOL_GPL(__alloc_percpu);
*/ */
void __percpu *__alloc_reserved_percpu(size_t size, size_t align) void __percpu *__alloc_reserved_percpu(size_t size, size_t align)
{ {
return pcpu_alloc(size, align, true); return pcpu_alloc(size, align, true, GFP_KERNEL);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment