xref: /openbmc/linux/lib/sg_pool.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2*f7f04d19SMasahiro Yamada #include <linux/init.h>
39b1d6c89SMing Lin #include <linux/scatterlist.h>
49b1d6c89SMing Lin #include <linux/mempool.h>
59b1d6c89SMing Lin #include <linux/slab.h>
69b1d6c89SMing Lin 
79b1d6c89SMing Lin #define SG_MEMPOOL_NR		ARRAY_SIZE(sg_pools)
89b1d6c89SMing Lin #define SG_MEMPOOL_SIZE		2
99b1d6c89SMing Lin 
109b1d6c89SMing Lin struct sg_pool {
119b1d6c89SMing Lin 	size_t		size;
129b1d6c89SMing Lin 	char		*name;
139b1d6c89SMing Lin 	struct kmem_cache	*slab;
149b1d6c89SMing Lin 	mempool_t	*pool;
159b1d6c89SMing Lin };
169b1d6c89SMing Lin 
179b1d6c89SMing Lin #define SP(x) { .size = x, "sgpool-" __stringify(x) }
189b1d6c89SMing Lin #if (SG_CHUNK_SIZE < 32)
199b1d6c89SMing Lin #error SG_CHUNK_SIZE is too small (must be 32 or greater)
209b1d6c89SMing Lin #endif
219b1d6c89SMing Lin static struct sg_pool sg_pools[] = {
229b1d6c89SMing Lin 	SP(8),
239b1d6c89SMing Lin 	SP(16),
249b1d6c89SMing Lin #if (SG_CHUNK_SIZE > 32)
259b1d6c89SMing Lin 	SP(32),
269b1d6c89SMing Lin #if (SG_CHUNK_SIZE > 64)
279b1d6c89SMing Lin 	SP(64),
289b1d6c89SMing Lin #if (SG_CHUNK_SIZE > 128)
299b1d6c89SMing Lin 	SP(128),
309b1d6c89SMing Lin #if (SG_CHUNK_SIZE > 256)
319b1d6c89SMing Lin #error SG_CHUNK_SIZE is too large (256 MAX)
329b1d6c89SMing Lin #endif
339b1d6c89SMing Lin #endif
349b1d6c89SMing Lin #endif
359b1d6c89SMing Lin #endif
369b1d6c89SMing Lin 	SP(SG_CHUNK_SIZE)
379b1d6c89SMing Lin };
389b1d6c89SMing Lin #undef SP
399b1d6c89SMing Lin 
sg_pool_index(unsigned short nents)409b1d6c89SMing Lin static inline unsigned int sg_pool_index(unsigned short nents)
419b1d6c89SMing Lin {
429b1d6c89SMing Lin 	unsigned int index;
439b1d6c89SMing Lin 
449b1d6c89SMing Lin 	BUG_ON(nents > SG_CHUNK_SIZE);
459b1d6c89SMing Lin 
469b1d6c89SMing Lin 	if (nents <= 8)
479b1d6c89SMing Lin 		index = 0;
489b1d6c89SMing Lin 	else
499b1d6c89SMing Lin 		index = get_count_order(nents) - 3;
509b1d6c89SMing Lin 
519b1d6c89SMing Lin 	return index;
529b1d6c89SMing Lin }
539b1d6c89SMing Lin 
sg_pool_free(struct scatterlist * sgl,unsigned int nents)549b1d6c89SMing Lin static void sg_pool_free(struct scatterlist *sgl, unsigned int nents)
559b1d6c89SMing Lin {
569b1d6c89SMing Lin 	struct sg_pool *sgp;
579b1d6c89SMing Lin 
589b1d6c89SMing Lin 	sgp = sg_pools + sg_pool_index(nents);
599b1d6c89SMing Lin 	mempool_free(sgl, sgp->pool);
609b1d6c89SMing Lin }
619b1d6c89SMing Lin 
sg_pool_alloc(unsigned int nents,gfp_t gfp_mask)629b1d6c89SMing Lin static struct scatterlist *sg_pool_alloc(unsigned int nents, gfp_t gfp_mask)
639b1d6c89SMing Lin {
649b1d6c89SMing Lin 	struct sg_pool *sgp;
659b1d6c89SMing Lin 
669b1d6c89SMing Lin 	sgp = sg_pools + sg_pool_index(nents);
679b1d6c89SMing Lin 	return mempool_alloc(sgp->pool, gfp_mask);
689b1d6c89SMing Lin }
699b1d6c89SMing Lin 
709b1d6c89SMing Lin /**
719b1d6c89SMing Lin  * sg_free_table_chained - Free a previously mapped sg table
729b1d6c89SMing Lin  * @table:	The sg table header to use
734635873cSMing Lei  * @nents_first_chunk: size of the first_chunk SGL passed to
744635873cSMing Lei  *		sg_alloc_table_chained
759b1d6c89SMing Lin  *
769b1d6c89SMing Lin  *  Description:
779b1d6c89SMing Lin  *    Free an sg table previously allocated and setup with
789b1d6c89SMing Lin  *    sg_alloc_table_chained().
799b1d6c89SMing Lin  *
804635873cSMing Lei  *    @nents_first_chunk has to be same with that same parameter passed
814635873cSMing Lei  *    to sg_alloc_table_chained().
824635873cSMing Lei  *
839b1d6c89SMing Lin  **/
sg_free_table_chained(struct sg_table * table,unsigned nents_first_chunk)844635873cSMing Lei void sg_free_table_chained(struct sg_table *table,
854635873cSMing Lei 		unsigned nents_first_chunk)
869b1d6c89SMing Lin {
874635873cSMing Lei 	if (table->orig_nents <= nents_first_chunk)
889b1d6c89SMing Lin 		return;
894635873cSMing Lei 
904635873cSMing Lei 	if (nents_first_chunk == 1)
914635873cSMing Lei 		nents_first_chunk = 0;
924635873cSMing Lei 
933e302dbcSMaor Gottlieb 	__sg_free_table(table, SG_CHUNK_SIZE, nents_first_chunk, sg_pool_free,
943e302dbcSMaor Gottlieb 			table->orig_nents);
959b1d6c89SMing Lin }
969b1d6c89SMing Lin EXPORT_SYMBOL_GPL(sg_free_table_chained);
979b1d6c89SMing Lin 
989b1d6c89SMing Lin /**
999b1d6c89SMing Lin  * sg_alloc_table_chained - Allocate and chain SGLs in an sg table
1009b1d6c89SMing Lin  * @table:	The sg table header to use
1019b1d6c89SMing Lin  * @nents:	Number of entries in sg list
1029b1d6c89SMing Lin  * @first_chunk: first SGL
1034635873cSMing Lei  * @nents_first_chunk: number of the SGL of @first_chunk
1049b1d6c89SMing Lin  *
1059b1d6c89SMing Lin  *  Description:
1069b1d6c89SMing Lin  *    Allocate and chain SGLs in an sg table. If @nents@ is larger than
107b79d9a09SMing Lei  *    @nents_first_chunk a chained sg table will be setup. @first_chunk is
108b79d9a09SMing Lei  *    ignored if nents_first_chunk <= 1 because user expects the SGL points
109b79d9a09SMing Lei  *    non-chain SGL.
1109b1d6c89SMing Lin  *
1119b1d6c89SMing Lin  **/
sg_alloc_table_chained(struct sg_table * table,int nents,struct scatterlist * first_chunk,unsigned nents_first_chunk)1129b1d6c89SMing Lin int sg_alloc_table_chained(struct sg_table *table, int nents,
1134635873cSMing Lei 		struct scatterlist *first_chunk, unsigned nents_first_chunk)
1149b1d6c89SMing Lin {
1159b1d6c89SMing Lin 	int ret;
1169b1d6c89SMing Lin 
1179b1d6c89SMing Lin 	BUG_ON(!nents);
1189b1d6c89SMing Lin 
1194635873cSMing Lei 	if (first_chunk && nents_first_chunk) {
1204635873cSMing Lei 		if (nents <= nents_first_chunk) {
1219b1d6c89SMing Lin 			table->nents = table->orig_nents = nents;
1229b1d6c89SMing Lin 			sg_init_table(table->sgl, nents);
1239b1d6c89SMing Lin 			return 0;
1249b1d6c89SMing Lin 		}
1259b1d6c89SMing Lin 	}
1269b1d6c89SMing Lin 
1274635873cSMing Lei 	/* User supposes that the 1st SGL includes real entry */
128b79d9a09SMing Lei 	if (nents_first_chunk <= 1) {
1294635873cSMing Lei 		first_chunk = NULL;
1304635873cSMing Lei 		nents_first_chunk = 0;
1314635873cSMing Lei 	}
1324635873cSMing Lei 
1339b1d6c89SMing Lin 	ret = __sg_alloc_table(table, nents, SG_CHUNK_SIZE,
1344635873cSMing Lei 			       first_chunk, nents_first_chunk,
1354635873cSMing Lei 			       GFP_ATOMIC, sg_pool_alloc);
1369b1d6c89SMing Lin 	if (unlikely(ret))
1374635873cSMing Lei 		sg_free_table_chained(table, nents_first_chunk);
1389b1d6c89SMing Lin 	return ret;
1399b1d6c89SMing Lin }
1409b1d6c89SMing Lin EXPORT_SYMBOL_GPL(sg_alloc_table_chained);
1419b1d6c89SMing Lin 
sg_pool_init(void)1429b1d6c89SMing Lin static __init int sg_pool_init(void)
1439b1d6c89SMing Lin {
1449b1d6c89SMing Lin 	int i;
1459b1d6c89SMing Lin 
1469b1d6c89SMing Lin 	for (i = 0; i < SG_MEMPOOL_NR; i++) {
1479b1d6c89SMing Lin 		struct sg_pool *sgp = sg_pools + i;
1489b1d6c89SMing Lin 		int size = sgp->size * sizeof(struct scatterlist);
1499b1d6c89SMing Lin 
1509b1d6c89SMing Lin 		sgp->slab = kmem_cache_create(sgp->name, size, 0,
1519b1d6c89SMing Lin 				SLAB_HWCACHE_ALIGN, NULL);
1529b1d6c89SMing Lin 		if (!sgp->slab) {
1539b1d6c89SMing Lin 			printk(KERN_ERR "SG_POOL: can't init sg slab %s\n",
1549b1d6c89SMing Lin 					sgp->name);
1559b1d6c89SMing Lin 			goto cleanup_sdb;
1569b1d6c89SMing Lin 		}
1579b1d6c89SMing Lin 
1589b1d6c89SMing Lin 		sgp->pool = mempool_create_slab_pool(SG_MEMPOOL_SIZE,
1599b1d6c89SMing Lin 						     sgp->slab);
1609b1d6c89SMing Lin 		if (!sgp->pool) {
1619b1d6c89SMing Lin 			printk(KERN_ERR "SG_POOL: can't init sg mempool %s\n",
1629b1d6c89SMing Lin 					sgp->name);
1639b1d6c89SMing Lin 			goto cleanup_sdb;
1649b1d6c89SMing Lin 		}
1659b1d6c89SMing Lin 	}
1669b1d6c89SMing Lin 
1679b1d6c89SMing Lin 	return 0;
1689b1d6c89SMing Lin 
1699b1d6c89SMing Lin cleanup_sdb:
1709b1d6c89SMing Lin 	for (i = 0; i < SG_MEMPOOL_NR; i++) {
1719b1d6c89SMing Lin 		struct sg_pool *sgp = sg_pools + i;
1727f476715Szhong jiang 
1739b1d6c89SMing Lin 		mempool_destroy(sgp->pool);
1749b1d6c89SMing Lin 		kmem_cache_destroy(sgp->slab);
1759b1d6c89SMing Lin 	}
1769b1d6c89SMing Lin 
1779b1d6c89SMing Lin 	return -ENOMEM;
1789b1d6c89SMing Lin }
1799b1d6c89SMing Lin 
180*f7f04d19SMasahiro Yamada subsys_initcall(sg_pool_init);
181