xref: /openbmc/linux/include/linux/genalloc.h (revision 89f60a5d)
140b0b3f8SThomas Gleixner /* SPDX-License-Identifier: GPL-2.0-only */
2f14f75b8SJes Sorensen /*
37f184275SHuang Ying  * Basic general purpose allocator for managing special purpose
47f184275SHuang Ying  * memory, for example, memory that is not managed by the regular
57f184275SHuang Ying  * kmalloc/kfree interface.  Uses for this includes on-device special
67f184275SHuang Ying  * memory, uncached memory etc.
77f184275SHuang Ying  *
87f184275SHuang Ying  * It is safe to use the allocator in NMI handlers and other special
97f184275SHuang Ying  * unblockable contexts that could otherwise deadlock on locks.  This
107f184275SHuang Ying  * is implemented by using atomic operations and retries on any
117f184275SHuang Ying  * conflicts.  The disadvantage is that there may be livelocks in
127f184275SHuang Ying  * extreme cases.  For better scalability, one allocator can be used
137f184275SHuang Ying  * for each CPU.
147f184275SHuang Ying  *
157f184275SHuang Ying  * The lockless operation only works if there is enough memory
167f184275SHuang Ying  * available.  If new memory is added to the pool a lock has to be
177f184275SHuang Ying  * still taken.  So any user relying on locklessness has to ensure
187f184275SHuang Ying  * that sufficient memory is preallocated.
197f184275SHuang Ying  *
207f184275SHuang Ying  * The basic atomic operation of this allocator is cmpxchg on long.
217f184275SHuang Ying  * On architectures that don't have NMI-safe cmpxchg implementation,
227f184275SHuang Ying  * the allocator can NOT be used in NMI handler.  So code uses the
237f184275SHuang Ying  * allocator in NMI handler should depend on
247f184275SHuang Ying  * CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG.
25f14f75b8SJes Sorensen  */
26f14f75b8SJes Sorensen 
27f14f75b8SJes Sorensen 
286aae6e03SJean-Christophe PLAGNIOL-VILLARD #ifndef __GENALLOC_H__
296aae6e03SJean-Christophe PLAGNIOL-VILLARD #define __GENALLOC_H__
309375db07SPhilipp Zabel 
31de2dd0ebSZhao Qiang #include <linux/types.h>
32b30afea0SShawn Guo #include <linux/spinlock_types.h>
3336a3d1ddSStephen Bates #include <linux/atomic.h>
34b30afea0SShawn Guo 
359375db07SPhilipp Zabel struct device;
369375db07SPhilipp Zabel struct device_node;
37de2dd0ebSZhao Qiang struct gen_pool;
389375db07SPhilipp Zabel 
39ca279cf1SBenjamin Gaignard /**
40a27bfcabSJonathan Corbet  * typedef genpool_algo_t: Allocation callback function type definition
41ca279cf1SBenjamin Gaignard  * @map: Pointer to bitmap
42ca279cf1SBenjamin Gaignard  * @size: The bitmap size in bits
43ca279cf1SBenjamin Gaignard  * @start: The bitnumber to start searching at
44ca279cf1SBenjamin Gaignard  * @nr: The number of zeroed bits we're looking for
45a27bfcabSJonathan Corbet  * @data: optional additional data used by the callback
46a27bfcabSJonathan Corbet  * @pool: the pool being allocated from
47ca279cf1SBenjamin Gaignard  */
48ca279cf1SBenjamin Gaignard typedef unsigned long (*genpool_algo_t)(unsigned long *map,
49ca279cf1SBenjamin Gaignard 			unsigned long size,
50ca279cf1SBenjamin Gaignard 			unsigned long start,
51ca279cf1SBenjamin Gaignard 			unsigned int nr,
5252fbf113SAlexey Skidanov 			void *data, struct gen_pool *pool,
5352fbf113SAlexey Skidanov 			unsigned long start_addr);
54ca279cf1SBenjamin Gaignard 
55f14f75b8SJes Sorensen /*
56929f9727SDean Nelson  *  General purpose special memory pool descriptor.
57f14f75b8SJes Sorensen  */
58f14f75b8SJes Sorensen struct gen_pool {
597f184275SHuang Ying 	spinlock_t lock;
60929f9727SDean Nelson 	struct list_head chunks;	/* list of chunks in this pool */
61929f9727SDean Nelson 	int min_alloc_order;		/* minimum allocation order */
62ca279cf1SBenjamin Gaignard 
63ca279cf1SBenjamin Gaignard 	genpool_algo_t algo;		/* allocation function */
64ca279cf1SBenjamin Gaignard 	void *data;
65c98c3635SVladimir Zapolskiy 
66c98c3635SVladimir Zapolskiy 	const char *name;
67f14f75b8SJes Sorensen };
68f14f75b8SJes Sorensen 
69929f9727SDean Nelson /*
70929f9727SDean Nelson  *  General purpose special memory pool chunk descriptor.
71929f9727SDean Nelson  */
72929f9727SDean Nelson struct gen_pool_chunk {
73929f9727SDean Nelson 	struct list_head next_chunk;	/* next chunk in pool */
7436a3d1ddSStephen Bates 	atomic_long_t avail;
753c8f370dSJean-Christophe PLAGNIOL-VILLARD 	phys_addr_t phys_addr;		/* physical starting address of memory chunk */
76795ee306SDan Williams 	void *owner;			/* private data to retrieve at alloc time */
77674470d9SJoonyoung Shim 	unsigned long start_addr;	/* start address of memory chunk */
78674470d9SJoonyoung Shim 	unsigned long end_addr;		/* end address of memory chunk (inclusive) */
7989f60a5dSGustavo A. R. Silva 	unsigned long bits[];		/* bitmap for allocating memory chunk */
80929f9727SDean Nelson };
81929f9727SDean Nelson 
82de2dd0ebSZhao Qiang /*
83de2dd0ebSZhao Qiang  *  gen_pool data descriptor for gen_pool_first_fit_align.
84de2dd0ebSZhao Qiang  */
85de2dd0ebSZhao Qiang struct genpool_data_align {
86de2dd0ebSZhao Qiang 	int align;		/* alignment by bytes for starting address */
87de2dd0ebSZhao Qiang };
88de2dd0ebSZhao Qiang 
89b26981c8SZhao Qiang /*
90b26981c8SZhao Qiang  *  gen_pool data descriptor for gen_pool_fixed_alloc.
91b26981c8SZhao Qiang  */
92b26981c8SZhao Qiang struct genpool_data_fixed {
93b26981c8SZhao Qiang 	unsigned long offset;		/* The offset of the specific region */
94b26981c8SZhao Qiang };
95b26981c8SZhao Qiang 
96929f9727SDean Nelson extern struct gen_pool *gen_pool_create(int, int);
973c8f370dSJean-Christophe PLAGNIOL-VILLARD extern phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long);
98795ee306SDan Williams extern int gen_pool_add_owner(struct gen_pool *, unsigned long, phys_addr_t,
99795ee306SDan Williams 			     size_t, int, void *);
100795ee306SDan Williams 
gen_pool_add_virt(struct gen_pool * pool,unsigned long addr,phys_addr_t phys,size_t size,int nid)101795ee306SDan Williams static inline int gen_pool_add_virt(struct gen_pool *pool, unsigned long addr,
102795ee306SDan Williams 		phys_addr_t phys, size_t size, int nid)
103795ee306SDan Williams {
104795ee306SDan Williams 	return gen_pool_add_owner(pool, addr, phys, size, nid, NULL);
105795ee306SDan Williams }
106795ee306SDan Williams 
1073c8f370dSJean-Christophe PLAGNIOL-VILLARD /**
1083c8f370dSJean-Christophe PLAGNIOL-VILLARD  * gen_pool_add - add a new chunk of special memory to the pool
1093c8f370dSJean-Christophe PLAGNIOL-VILLARD  * @pool: pool to add new memory chunk to
1103c8f370dSJean-Christophe PLAGNIOL-VILLARD  * @addr: starting address of memory chunk to add to pool
1113c8f370dSJean-Christophe PLAGNIOL-VILLARD  * @size: size in bytes of the memory chunk to add to pool
1123c8f370dSJean-Christophe PLAGNIOL-VILLARD  * @nid: node id of the node the chunk structure and bitmap should be
1133c8f370dSJean-Christophe PLAGNIOL-VILLARD  *       allocated on, or -1
1143c8f370dSJean-Christophe PLAGNIOL-VILLARD  *
1153c8f370dSJean-Christophe PLAGNIOL-VILLARD  * Add a new chunk of special memory to the specified pool.
1163c8f370dSJean-Christophe PLAGNIOL-VILLARD  *
1173c8f370dSJean-Christophe PLAGNIOL-VILLARD  * Returns 0 on success or a -ve errno on failure.
1183c8f370dSJean-Christophe PLAGNIOL-VILLARD  */
gen_pool_add(struct gen_pool * pool,unsigned long addr,size_t size,int nid)1193c8f370dSJean-Christophe PLAGNIOL-VILLARD static inline int gen_pool_add(struct gen_pool *pool, unsigned long addr,
1203c8f370dSJean-Christophe PLAGNIOL-VILLARD 			       size_t size, int nid)
1213c8f370dSJean-Christophe PLAGNIOL-VILLARD {
1223c8f370dSJean-Christophe PLAGNIOL-VILLARD 	return gen_pool_add_virt(pool, addr, -1, size, nid);
1233c8f370dSJean-Christophe PLAGNIOL-VILLARD }
124322acc96SSteve Wise extern void gen_pool_destroy(struct gen_pool *);
125795ee306SDan Williams unsigned long gen_pool_alloc_algo_owner(struct gen_pool *pool, size_t size,
126795ee306SDan Williams 		genpool_algo_t algo, void *data, void **owner);
127795ee306SDan Williams 
gen_pool_alloc_owner(struct gen_pool * pool,size_t size,void ** owner)128795ee306SDan Williams static inline unsigned long gen_pool_alloc_owner(struct gen_pool *pool,
129795ee306SDan Williams 		size_t size, void **owner)
130795ee306SDan Williams {
131795ee306SDan Williams 	return gen_pool_alloc_algo_owner(pool, size, pool->algo, pool->data,
132795ee306SDan Williams 			owner);
133795ee306SDan Williams }
134795ee306SDan Williams 
gen_pool_alloc_algo(struct gen_pool * pool,size_t size,genpool_algo_t algo,void * data)135795ee306SDan Williams static inline unsigned long gen_pool_alloc_algo(struct gen_pool *pool,
136795ee306SDan Williams 		size_t size, genpool_algo_t algo, void *data)
137795ee306SDan Williams {
138795ee306SDan Williams 	return gen_pool_alloc_algo_owner(pool, size, algo, data, NULL);
139795ee306SDan Williams }
140795ee306SDan Williams 
141795ee306SDan Williams /**
142795ee306SDan Williams  * gen_pool_alloc - allocate special memory from the pool
143795ee306SDan Williams  * @pool: pool to allocate from
144795ee306SDan Williams  * @size: number of bytes to allocate from the pool
145795ee306SDan Williams  *
146795ee306SDan Williams  * Allocate the requested number of bytes from the specified pool.
147795ee306SDan Williams  * Uses the pool allocation function (with first-fit algorithm by default).
148795ee306SDan Williams  * Can not be used in NMI handler on architectures without
149795ee306SDan Williams  * NMI-safe cmpxchg implementation.
150795ee306SDan Williams  */
gen_pool_alloc(struct gen_pool * pool,size_t size)151795ee306SDan Williams static inline unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
152795ee306SDan Williams {
153795ee306SDan Williams 	return gen_pool_alloc_algo(pool, size, pool->algo, pool->data);
154795ee306SDan Williams }
155795ee306SDan Williams 
156684f0d3dSNicolin Chen extern void *gen_pool_dma_alloc(struct gen_pool *pool, size_t size,
157684f0d3dSNicolin Chen 		dma_addr_t *dma);
158cf394fc5SFredrik Noring extern void *gen_pool_dma_alloc_algo(struct gen_pool *pool, size_t size,
159cf394fc5SFredrik Noring 		dma_addr_t *dma, genpool_algo_t algo, void *data);
160cf394fc5SFredrik Noring extern void *gen_pool_dma_alloc_align(struct gen_pool *pool, size_t size,
161cf394fc5SFredrik Noring 		dma_addr_t *dma, int align);
162cf394fc5SFredrik Noring extern void *gen_pool_dma_zalloc(struct gen_pool *pool, size_t size, dma_addr_t *dma);
163cf394fc5SFredrik Noring extern void *gen_pool_dma_zalloc_algo(struct gen_pool *pool, size_t size,
164cf394fc5SFredrik Noring 		dma_addr_t *dma, genpool_algo_t algo, void *data);
165cf394fc5SFredrik Noring extern void *gen_pool_dma_zalloc_align(struct gen_pool *pool, size_t size,
166cf394fc5SFredrik Noring 		dma_addr_t *dma, int align);
167795ee306SDan Williams extern void gen_pool_free_owner(struct gen_pool *pool, unsigned long addr,
168795ee306SDan Williams 		size_t size, void **owner);
gen_pool_free(struct gen_pool * pool,unsigned long addr,size_t size)169795ee306SDan Williams static inline void gen_pool_free(struct gen_pool *pool, unsigned long addr,
170795ee306SDan Williams                 size_t size)
171795ee306SDan Williams {
172795ee306SDan Williams 	gen_pool_free_owner(pool, addr, size, NULL);
173795ee306SDan Williams }
174795ee306SDan Williams 
1757f184275SHuang Ying extern void gen_pool_for_each_chunk(struct gen_pool *,
1767f184275SHuang Ying 	void (*)(struct gen_pool *, struct gen_pool_chunk *, void *), void *);
1777f184275SHuang Ying extern size_t gen_pool_avail(struct gen_pool *);
1787f184275SHuang Ying extern size_t gen_pool_size(struct gen_pool *);
179ca279cf1SBenjamin Gaignard 
180ca279cf1SBenjamin Gaignard extern void gen_pool_set_algo(struct gen_pool *pool, genpool_algo_t algo,
181ca279cf1SBenjamin Gaignard 		void *data);
182ca279cf1SBenjamin Gaignard 
183ca279cf1SBenjamin Gaignard extern unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size,
184de2dd0ebSZhao Qiang 		unsigned long start, unsigned int nr, void *data,
18552fbf113SAlexey Skidanov 		struct gen_pool *pool, unsigned long start_addr);
186de2dd0ebSZhao Qiang 
187b26981c8SZhao Qiang extern unsigned long gen_pool_fixed_alloc(unsigned long *map,
188b26981c8SZhao Qiang 		unsigned long size, unsigned long start, unsigned int nr,
18952fbf113SAlexey Skidanov 		void *data, struct gen_pool *pool, unsigned long start_addr);
190b26981c8SZhao Qiang 
191de2dd0ebSZhao Qiang extern unsigned long gen_pool_first_fit_align(unsigned long *map,
192de2dd0ebSZhao Qiang 		unsigned long size, unsigned long start, unsigned int nr,
19352fbf113SAlexey Skidanov 		void *data, struct gen_pool *pool, unsigned long start_addr);
194de2dd0ebSZhao Qiang 
195ca279cf1SBenjamin Gaignard 
196505e3be6SLaura Abbott extern unsigned long gen_pool_first_fit_order_align(unsigned long *map,
197505e3be6SLaura Abbott 		unsigned long size, unsigned long start, unsigned int nr,
19852fbf113SAlexey Skidanov 		void *data, struct gen_pool *pool, unsigned long start_addr);
199505e3be6SLaura Abbott 
200ca279cf1SBenjamin Gaignard extern unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size,
201de2dd0ebSZhao Qiang 		unsigned long start, unsigned int nr, void *data,
20252fbf113SAlexey Skidanov 		struct gen_pool *pool, unsigned long start_addr);
203de2dd0ebSZhao Qiang 
204ca279cf1SBenjamin Gaignard 
2059375db07SPhilipp Zabel extern struct gen_pool *devm_gen_pool_create(struct device *dev,
20673858173SVladimir Zapolskiy 		int min_alloc_order, int nid, const char *name);
20773858173SVladimir Zapolskiy extern struct gen_pool *gen_pool_get(struct device *dev, const char *name);
2089375db07SPhilipp Zabel 
209964975acSHuang Shijie extern bool gen_pool_has_addr(struct gen_pool *pool, unsigned long start,
2109efb3a42SLaura Abbott 			size_t size);
2119efb3a42SLaura Abbott 
2129375db07SPhilipp Zabel #ifdef CONFIG_OF
213abdd4a70SVladimir Zapolskiy extern struct gen_pool *of_gen_pool_get(struct device_node *np,
2149375db07SPhilipp Zabel 	const char *propname, int index);
2159375db07SPhilipp Zabel #else
of_gen_pool_get(struct device_node * np,const char * propname,int index)216abdd4a70SVladimir Zapolskiy static inline struct gen_pool *of_gen_pool_get(struct device_node *np,
2179375db07SPhilipp Zabel 	const char *propname, int index)
2189375db07SPhilipp Zabel {
2199375db07SPhilipp Zabel 	return NULL;
2209375db07SPhilipp Zabel }
2219375db07SPhilipp Zabel #endif
2226aae6e03SJean-Christophe PLAGNIOL-VILLARD #endif /* __GENALLOC_H__ */
223