| From 73fbedb2b7f6fc9806a57a94ea02e341a9f108ca Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 3 Jan 2019 15:26:44 -0800 |
| Subject: lib/genalloc.c: fix allocation of aligned buffer from non-aligned |
| chunk |
| |
| From: Alexey Skidanov <alexey.skidanov@intel.com> |
| |
| [ Upstream commit 52fbf1134d479234d7e64ba9dcbaea23405f229e ] |
| |
| gen_pool_alloc_algo() uses different allocation functions implementing |
| different allocation algorithms. With gen_pool_first_fit_align() |
| allocation function, the returned address should be aligned on the |
| requested boundary. |
| |
| If chunk start address isn't aligned on the requested boundary, the |
| returned address isn't aligned too. The only way to get properly |
| aligned address is to initialize the pool with chunks aligned on the |
| requested boundary. If want to have an ability to allocate buffers |
| aligned on different boundaries (for example, 4K, 1MB, ...), the chunk |
| start address should be aligned on the max possible alignment. |
| |
| This happens because gen_pool_first_fit_align() looks for properly |
| aligned memory block without taking into account the chunk start address |
| alignment. |
| |
| To fix this, we provide chunk start address to |
| gen_pool_first_fit_align() and change its implementation such that it |
| starts looking for properly aligned block with appropriate offset |
| (exactly as is done in CMA). |
| |
| Link: https://lkml.kernel.org/lkml/a170cf65-6884-3592-1de9-4c235888cc8a@intel.com |
| Link: http://lkml.kernel.org/r/1541690953-4623-1-git-send-email-alexey.skidanov@intel.com |
| Signed-off-by: Alexey Skidanov <alexey.skidanov@intel.com> |
| Reviewed-by: Andrew Morton <akpm@linux-foundation.org> |
| Cc: Logan Gunthorpe <logang@deltatee.com> |
| Cc: Daniel Mentz <danielmentz@google.com> |
| Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> |
| Cc: Laura Abbott <labbott@redhat.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| include/linux/genalloc.h | 13 +++++++------ |
| lib/genalloc.c | 20 ++++++++++++-------- |
| 2 files changed, 19 insertions(+), 14 deletions(-) |
| |
| diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h |
| index 206fe3bcccccd..575f2dee8cf71 100644 |
| --- a/include/linux/genalloc.h |
| +++ b/include/linux/genalloc.h |
| @@ -50,7 +50,8 @@ typedef unsigned long (*genpool_algo_t)(unsigned long *map, |
| unsigned long size, |
| unsigned long start, |
| unsigned int nr, |
| - void *data, struct gen_pool *pool); |
| + void *data, struct gen_pool *pool, |
| + unsigned long start_addr); |
| |
| /* |
| * General purpose special memory pool descriptor. |
| @@ -130,24 +131,24 @@ extern void gen_pool_set_algo(struct gen_pool *pool, genpool_algo_t algo, |
| |
| extern unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size, |
| unsigned long start, unsigned int nr, void *data, |
| - struct gen_pool *pool); |
| + struct gen_pool *pool, unsigned long start_addr); |
| |
| extern unsigned long gen_pool_fixed_alloc(unsigned long *map, |
| unsigned long size, unsigned long start, unsigned int nr, |
| - void *data, struct gen_pool *pool); |
| + void *data, struct gen_pool *pool, unsigned long start_addr); |
| |
| extern unsigned long gen_pool_first_fit_align(unsigned long *map, |
| unsigned long size, unsigned long start, unsigned int nr, |
| - void *data, struct gen_pool *pool); |
| + void *data, struct gen_pool *pool, unsigned long start_addr); |
| |
| |
| extern unsigned long gen_pool_first_fit_order_align(unsigned long *map, |
| unsigned long size, unsigned long start, unsigned int nr, |
| - void *data, struct gen_pool *pool); |
| + void *data, struct gen_pool *pool, unsigned long start_addr); |
| |
| extern unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size, |
| unsigned long start, unsigned int nr, void *data, |
| - struct gen_pool *pool); |
| + struct gen_pool *pool, unsigned long start_addr); |
| |
| |
| extern struct gen_pool *devm_gen_pool_create(struct device *dev, |
| diff --git a/lib/genalloc.c b/lib/genalloc.c |
| index ca06adc4f4451..5deb25c40a5a1 100644 |
| --- a/lib/genalloc.c |
| +++ b/lib/genalloc.c |
| @@ -311,7 +311,7 @@ unsigned long gen_pool_alloc_algo(struct gen_pool *pool, size_t size, |
| end_bit = chunk_size(chunk) >> order; |
| retry: |
| start_bit = algo(chunk->bits, end_bit, start_bit, |
| - nbits, data, pool); |
| + nbits, data, pool, chunk->start_addr); |
| if (start_bit >= end_bit) |
| continue; |
| remain = bitmap_set_ll(chunk->bits, start_bit, nbits); |
| @@ -525,7 +525,7 @@ EXPORT_SYMBOL(gen_pool_set_algo); |
| */ |
| unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size, |
| unsigned long start, unsigned int nr, void *data, |
| - struct gen_pool *pool) |
| + struct gen_pool *pool, unsigned long start_addr) |
| { |
| return bitmap_find_next_zero_area(map, size, start, nr, 0); |
| } |
| @@ -543,16 +543,19 @@ EXPORT_SYMBOL(gen_pool_first_fit); |
| */ |
| unsigned long gen_pool_first_fit_align(unsigned long *map, unsigned long size, |
| unsigned long start, unsigned int nr, void *data, |
| - struct gen_pool *pool) |
| + struct gen_pool *pool, unsigned long start_addr) |
| { |
| struct genpool_data_align *alignment; |
| - unsigned long align_mask; |
| + unsigned long align_mask, align_off; |
| int order; |
| |
| alignment = data; |
| order = pool->min_alloc_order; |
| align_mask = ((alignment->align + (1UL << order) - 1) >> order) - 1; |
| - return bitmap_find_next_zero_area(map, size, start, nr, align_mask); |
| + align_off = (start_addr & (alignment->align - 1)) >> order; |
| + |
| + return bitmap_find_next_zero_area_off(map, size, start, nr, |
| + align_mask, align_off); |
| } |
| EXPORT_SYMBOL(gen_pool_first_fit_align); |
| |
| @@ -567,7 +570,7 @@ EXPORT_SYMBOL(gen_pool_first_fit_align); |
| */ |
| unsigned long gen_pool_fixed_alloc(unsigned long *map, unsigned long size, |
| unsigned long start, unsigned int nr, void *data, |
| - struct gen_pool *pool) |
| + struct gen_pool *pool, unsigned long start_addr) |
| { |
| struct genpool_data_fixed *fixed_data; |
| int order; |
| @@ -601,7 +604,8 @@ EXPORT_SYMBOL(gen_pool_fixed_alloc); |
| */ |
| unsigned long gen_pool_first_fit_order_align(unsigned long *map, |
| unsigned long size, unsigned long start, |
| - unsigned int nr, void *data, struct gen_pool *pool) |
| + unsigned int nr, void *data, struct gen_pool *pool, |
| + unsigned long start_addr) |
| { |
| unsigned long align_mask = roundup_pow_of_two(nr) - 1; |
| |
| @@ -624,7 +628,7 @@ EXPORT_SYMBOL(gen_pool_first_fit_order_align); |
| */ |
| unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size, |
| unsigned long start, unsigned int nr, void *data, |
| - struct gen_pool *pool) |
| + struct gen_pool *pool, unsigned long start_addr) |
| { |
| unsigned long start_bit = size; |
| unsigned long len = size + 1; |
| -- |
| 2.20.1 |
| |