blob: c7b9b9c41b780d60f6fe948d1b2a49c4300f9bea [file] [log] [blame]
Jes Sorensenf14f75b2005-06-21 17:15:02 -07001/*
2 * Basic general purpose allocator for managing special purpose memory
3 * not managed by the regular kmalloc/kfree interface.
4 * Uses for this includes on-device special memory, uncached memory
5 * etc.
6 *
Jes Sorensenf14f75b2005-06-21 17:15:02 -07007 * Copyright 2005 (C) Jes Sorensen <jes@trained-monkey.org>
8 *
9 * This source code is licensed under the GNU General Public License,
10 * Version 2. See the file COPYING for more details.
11 */
12
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090013#include <linux/slab.h>
Jes Sorensenf14f75b2005-06-21 17:15:02 -070014#include <linux/module.h>
Akinobu Mita243797f2009-12-15 16:48:31 -080015#include <linux/bitmap.h>
Jes Sorensenf14f75b2005-06-21 17:15:02 -070016#include <linux/genalloc.h>
17
Jes Sorensenf14f75b2005-06-21 17:15:02 -070018
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070019/* General purpose special memory pool descriptor. */
20struct gen_pool {
21 rwlock_t lock; /* protects chunks list */
22 struct list_head chunks; /* list of chunks in this pool */
23 unsigned order; /* minimum allocation order */
24};
25
26/* General purpose special memory pool chunk descriptor. */
27struct gen_pool_chunk {
28 spinlock_t lock; /* protects bits */
29 struct list_head next_chunk; /* next chunk in pool */
30 phys_addr_t phys_addr; /* physical starting address of memory chunk */
31 unsigned long start; /* start of memory chunk */
32 unsigned long size; /* number of bits */
33 unsigned long bits[0]; /* bitmap for allocating memory chunk */
34};
35
Dean Nelsona58cbd72006-10-02 02:17:01 -070036/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037 * gen_pool_create() - create a new special memory pool
38 * @order: Log base 2 of number of bytes each bitmap bit
39 * represents.
40 * @nid: Node id of the node the pool structure should be allocated
41 * on, or -1. This will be also used for other allocations.
Dean Nelsona58cbd72006-10-02 02:17:01 -070042 *
43 * Create a new special memory pool that can be used to manage special purpose
44 * memory not managed by the regular kmalloc/kfree interface.
Dean Nelson929f9722006-06-23 02:03:21 -070045 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046struct gen_pool *__must_check gen_pool_create(unsigned order, int nid)
Jes Sorensenf14f75b2005-06-21 17:15:02 -070047{
Dean Nelson929f9722006-06-23 02:03:21 -070048 struct gen_pool *pool;
Jes Sorensenf14f75b2005-06-21 17:15:02 -070049
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050 if (WARN_ON(order >= BITS_PER_LONG))
51 return NULL;
52
53 pool = kmalloc_node(sizeof *pool, GFP_KERNEL, nid);
54 if (pool) {
Dean Nelson929f9722006-06-23 02:03:21 -070055 rwlock_init(&pool->lock);
56 INIT_LIST_HEAD(&pool->chunks);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057 pool->order = order;
Jes Sorensenf14f75b2005-06-21 17:15:02 -070058 }
Dean Nelson929f9722006-06-23 02:03:21 -070059 return pool;
Jes Sorensenf14f75b2005-06-21 17:15:02 -070060}
61EXPORT_SYMBOL(gen_pool_create);
62
Dean Nelsona58cbd72006-10-02 02:17:01 -070063/**
Jean-Christophe PLAGNIOL-VILLARD3c8f3702011-05-24 17:13:34 -070064 * gen_pool_add_virt - add a new chunk of special memory to the pool
Dean Nelson929f9722006-06-23 02:03:21 -070065 * @pool: pool to add new memory chunk to
Jean-Christophe PLAGNIOL-VILLARD3c8f3702011-05-24 17:13:34 -070066 * @virt: virtual starting address of memory chunk to add to pool
67 * @phys: physical starting address of memory chunk to add to pool
Dean Nelson929f9722006-06-23 02:03:21 -070068 * @size: size in bytes of the memory chunk to add to pool
69 * @nid: node id of the node the chunk structure and bitmap should be
70 * allocated on, or -1
Dean Nelsona58cbd72006-10-02 02:17:01 -070071 *
72 * Add a new chunk of special memory to the specified pool.
Jean-Christophe PLAGNIOL-VILLARD3c8f3702011-05-24 17:13:34 -070073 *
74 * Returns 0 on success or a -ve errno on failure.
Jes Sorensenf14f75b2005-06-21 17:15:02 -070075 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076int __must_check gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phys,
Jean-Christophe PLAGNIOL-VILLARD3c8f3702011-05-24 17:13:34 -070077 size_t size, int nid)
Jes Sorensenf14f75b2005-06-21 17:15:02 -070078{
Dean Nelson929f9722006-06-23 02:03:21 -070079 struct gen_pool_chunk *chunk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080 size_t nbytes;
Jes Sorensenf14f75b2005-06-21 17:15:02 -070081
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082 if (WARN_ON(!virt || virt + size < virt ||
83 (virt & ((1 << pool->order) - 1))))
84 return -EINVAL;
85
86 size = size >> pool->order;
87 if (WARN_ON(!size))
88 return -EINVAL;
89
90 nbytes = sizeof *chunk + BITS_TO_LONGS(size) * sizeof *chunk->bits;
91 chunk = kzalloc_node(nbytes, GFP_KERNEL, nid);
92 if (!chunk)
Jean-Christophe PLAGNIOL-VILLARD3c8f3702011-05-24 17:13:34 -070093 return -ENOMEM;
Jes Sorensenf14f75b2005-06-21 17:15:02 -070094
Dean Nelson929f9722006-06-23 02:03:21 -070095 spin_lock_init(&chunk->lock);
Jean-Christophe PLAGNIOL-VILLARD3c8f3702011-05-24 17:13:34 -070096 chunk->phys_addr = phys;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097 chunk->start = virt >> pool->order;
98 chunk->size = size;
Dean Nelson929f9722006-06-23 02:03:21 -070099
100 write_lock(&pool->lock);
101 list_add(&chunk->next_chunk, &pool->chunks);
102 write_unlock(&pool->lock);
103
104 return 0;
105}
Jean-Christophe PLAGNIOL-VILLARD3c8f3702011-05-24 17:13:34 -0700106EXPORT_SYMBOL(gen_pool_add_virt);
107
108/**
109 * gen_pool_virt_to_phys - return the physical address of memory
110 * @pool: pool to allocate from
111 * @addr: starting address of memory
112 *
113 * Returns the physical address on success, or -1 on error.
114 */
115phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long addr)
116{
117 struct list_head *_chunk;
118 struct gen_pool_chunk *chunk;
119
120 read_lock(&pool->lock);
121 list_for_each(_chunk, &pool->chunks) {
122 chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
123
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124 if (addr >= chunk->start &&
125 addr < (chunk->start + chunk->size))
126 return chunk->phys_addr + addr - chunk->start;
Jean-Christophe PLAGNIOL-VILLARD3c8f3702011-05-24 17:13:34 -0700127 }
128 read_unlock(&pool->lock);
129
130 return -1;
131}
132EXPORT_SYMBOL(gen_pool_virt_to_phys);
Dean Nelson929f9722006-06-23 02:03:21 -0700133
Dean Nelsona58cbd72006-10-02 02:17:01 -0700134/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135 * gen_pool_destroy() - destroy a special memory pool
136 * @pool: Pool to destroy.
Dean Nelsona58cbd72006-10-02 02:17:01 -0700137 *
138 * Destroy the specified special memory pool. Verifies that there are no
139 * outstanding allocations.
Steve Wise322acc92006-10-02 02:17:00 -0700140 */
141void gen_pool_destroy(struct gen_pool *pool)
142{
Steve Wise322acc92006-10-02 02:17:00 -0700143 struct gen_pool_chunk *chunk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144 int bit;
Steve Wise322acc92006-10-02 02:17:00 -0700145
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 while (!list_empty(&pool->chunks)) {
147 chunk = list_entry(pool->chunks.next, struct gen_pool_chunk,
148 next_chunk);
Steve Wise322acc92006-10-02 02:17:00 -0700149 list_del(&chunk->next_chunk);
150
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151 bit = find_next_bit(chunk->bits, chunk->size, 0);
152 BUG_ON(bit < chunk->size);
Steve Wise322acc92006-10-02 02:17:00 -0700153
154 kfree(chunk);
155 }
156 kfree(pool);
Steve Wise322acc92006-10-02 02:17:00 -0700157}
158EXPORT_SYMBOL(gen_pool_destroy);
159
Dean Nelsona58cbd72006-10-02 02:17:01 -0700160/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 * gen_pool_alloc_aligned() - allocate special memory from the pool
162 * @pool: Pool to allocate from.
163 * @size: Number of bytes to allocate from the pool.
164 * @alignment_order: Order the allocated space should be
165 * aligned to (eg. 20 means allocated space
166 * must be aligned to 1MiB).
Dean Nelsona58cbd72006-10-02 02:17:01 -0700167 *
168 * Allocate the requested number of bytes from the specified pool.
169 * Uses a first-fit algorithm.
Dean Nelson929f9722006-06-23 02:03:21 -0700170 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171unsigned long __must_check
172gen_pool_alloc_aligned(struct gen_pool *pool, size_t size,
173 unsigned alignment_order)
Dean Nelson929f9722006-06-23 02:03:21 -0700174{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 unsigned long addr, align_mask = 0, flags, start;
Dean Nelson929f9722006-06-23 02:03:21 -0700176 struct gen_pool_chunk *chunk;
Dean Nelson929f9722006-06-23 02:03:21 -0700177
178 if (size == 0)
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700179 return 0;
180
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181 if (alignment_order > pool->order)
182 align_mask = (1 << (alignment_order - pool->order)) - 1;
183
184 size = (size + (1UL << pool->order) - 1) >> pool->order;
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700185
Dean Nelson929f9722006-06-23 02:03:21 -0700186 read_lock(&pool->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187 list_for_each_entry(chunk, &pool->chunks, next_chunk) {
188 if (chunk->size < size)
189 continue;
Dean Nelson929f9722006-06-23 02:03:21 -0700190
191 spin_lock_irqsave(&chunk->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192 start = bitmap_find_next_zero_area_off(chunk->bits, chunk->size,
193 0, size, align_mask,
194 chunk->start);
195 if (start >= chunk->size) {
Dean Nelson929f9722006-06-23 02:03:21 -0700196 spin_unlock_irqrestore(&chunk->lock, flags);
Akinobu Mita243797f2009-12-15 16:48:31 -0800197 continue;
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700198 }
Akinobu Mita243797f2009-12-15 16:48:31 -0800199
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 bitmap_set(chunk->bits, start, size);
Dean Nelson929f9722006-06-23 02:03:21 -0700201 spin_unlock_irqrestore(&chunk->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 addr = (chunk->start + start) << pool->order;
203 goto done;
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700204 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205
206 addr = 0;
207done:
Dean Nelson929f9722006-06-23 02:03:21 -0700208 read_unlock(&pool->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 return addr;
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700210}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211EXPORT_SYMBOL(gen_pool_alloc_aligned);
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700212
Dean Nelsona58cbd72006-10-02 02:17:01 -0700213/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 * gen_pool_free() - free allocated special memory back to the pool
215 * @pool: Pool to free to.
216 * @addr: Starting address of memory to free back to pool.
217 * @size: Size in bytes of memory to free.
Dean Nelsona58cbd72006-10-02 02:17:01 -0700218 *
219 * Free previously allocated special memory back to the specified pool.
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700220 */
Dean Nelson929f9722006-06-23 02:03:21 -0700221void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size)
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700222{
Dean Nelson929f9722006-06-23 02:03:21 -0700223 struct gen_pool_chunk *chunk;
224 unsigned long flags;
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700225
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 if (!size)
227 return;
228
229 addr = addr >> pool->order;
230 size = (size + (1UL << pool->order) - 1) >> pool->order;
231
232 BUG_ON(addr + size < addr);
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700233
Dean Nelson929f9722006-06-23 02:03:21 -0700234 read_lock(&pool->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 list_for_each_entry(chunk, &pool->chunks, next_chunk)
236 if (addr >= chunk->start &&
237 addr + size <= chunk->start + chunk->size) {
Dean Nelson929f9722006-06-23 02:03:21 -0700238 spin_lock_irqsave(&chunk->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239 bitmap_clear(chunk->bits, addr - chunk->start, size);
Dean Nelson929f9722006-06-23 02:03:21 -0700240 spin_unlock_irqrestore(&chunk->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700241 goto done;
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700242 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243 BUG_ON(1);
244done:
Dean Nelson929f9722006-06-23 02:03:21 -0700245 read_unlock(&pool->lock);
Jes Sorensenf14f75b2005-06-21 17:15:02 -0700246}
247EXPORT_SYMBOL(gen_pool_free);