diff options
-rw-r--r-- | include/linux/genalloc.h | 12 | ||||
-rw-r--r-- | lib/genalloc.c | 23 |
2 files changed, 31 insertions, 4 deletions
diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h index 661d374aeb2d..98bead69d488 100644 --- a/include/linux/genalloc.h +++ b/include/linux/genalloc.h @@ -93,7 +93,17 @@ static inline int gen_pool_add(struct gen_pool *pool, unsigned long addr, return gen_pool_add_virt(pool, addr, -1, size, nid); } extern void gen_pool_destroy(struct gen_pool *); -extern unsigned long gen_pool_alloc(struct gen_pool *, size_t); +extern unsigned long gen_pool_alloc_addr(struct gen_pool *, + size_t, unsigned long); +/** + * gen_pool_alloc - allocate special memory from the pool + * @pool: pool to allocate from + * @size: number of bytes to allocate from the pool + */ +static inline unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) +{ + return gen_pool_alloc_addr(pool, size); +} extern void gen_pool_free(struct gen_pool *, unsigned long, size_t); extern void gen_pool_for_each_chunk(struct gen_pool *, void (*)(struct gen_pool *, struct gen_pool_chunk *, void *), void *); diff --git a/lib/genalloc.c b/lib/genalloc.c index b35cfa9bc3d4..30d3d8e24af9 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -254,21 +254,24 @@ void gen_pool_destroy(struct gen_pool *pool) EXPORT_SYMBOL(gen_pool_destroy); /** - * gen_pool_alloc - allocate special memory from the pool + * gen_pool_alloc_addr - allocate special memory from the pool * @pool: pool to allocate from * @size: number of bytes to allocate from the pool + * @alloc_addr: if non-zero, allocate starting at alloc_addr. * * Allocate the requested number of bytes from the specified pool. * Uses the pool allocation function (with first-fit algorithm by default). * Can not be used in NMI handler on architectures without * NMI-safe cmpxchg implementation. */ -unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) +unsigned long gen_pool_alloc_addr(struct gen_pool *pool, size_t size, + unsigned long alloc_addr) { struct gen_pool_chunk *chunk; unsigned long addr = 0; int order = pool->min_alloc_order; int nbits, start_bit = 0, end_bit, remain; + int alloc_bit_needed = 0; #ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG BUG_ON(in_nmi()); @@ -277,6 +280,9 @@ unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) if (size == 0) return 0; + if (alloc_addr & (1 << order) - 1) + return 0; + nbits = (size + (1UL << order) - 1) >> order; rcu_read_lock(); list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) { @@ -284,9 +290,20 @@ unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) continue; end_bit = (chunk->end_addr - chunk->start_addr) >> order; + if (alloc_addr) { + if (alloc_addr < chunk->start_addr || + alloc_addr >= chunk->end_addr) + continue; + if (alloc_addr + size > chunk->end_addr) + return 0; + alloc_bit_needed = start_bit = + (alloc_addr - chunk->start_addr) >> order; + } retry: start_bit = pool->algo(chunk->bits, end_bit, start_bit, nbits, pool->data); + if (alloc_addr && alloc_bit_needed != start_bit) + return 0; if (start_bit >= end_bit) continue; remain = bitmap_set_ll(chunk->bits, start_bit, nbits); @@ -305,7 +322,7 @@ retry: rcu_read_unlock(); return addr; } -EXPORT_SYMBOL(gen_pool_alloc); +EXPORT_SYMBOL(gen_pool_alloc_addr); /** * gen_pool_free - free allocated special memory back to the pool |