lib/scatterlist: Introduce sgl_alloc() and sgl_free()
Many kernel drivers contain code that allocates and frees both a scatterlist and the pages that populate that scatterlist. Introduce functions in lib/scatterlist.c that perform these tasks instead of duplicating this functionality in multiple drivers. Only include these functions in the build if CONFIG_SGL_ALLOC=y to avoid that the kernel size increases if this functionality is not used. Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com> Reviewed-by: Hannes Reinecke <hare@suse.com> Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
		
							parent
							
								
									bbbc3c1cfa
								
							
						
					
					
						commit
						e80a0af475
					
				| @ -276,6 +276,16 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, | ||||
| 			      unsigned int n_pages, unsigned int offset, | ||||
| 			      unsigned long size, gfp_t gfp_mask); | ||||
| 
 | ||||
| #ifdef CONFIG_SGL_ALLOC | ||||
| struct scatterlist *sgl_alloc_order(unsigned long long length, | ||||
| 				    unsigned int order, bool chainable, | ||||
| 				    gfp_t gfp, unsigned int *nent_p); | ||||
| struct scatterlist *sgl_alloc(unsigned long long length, gfp_t gfp, | ||||
| 			      unsigned int *nent_p); | ||||
| void sgl_free_order(struct scatterlist *sgl, int order); | ||||
| void sgl_free(struct scatterlist *sgl); | ||||
| #endif /* CONFIG_SGL_ALLOC */ | ||||
| 
 | ||||
| size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, | ||||
| 		      size_t buflen, off_t skip, bool to_buffer); | ||||
| 
 | ||||
|  | ||||
| @ -409,6 +409,10 @@ config HAS_DMA | ||||
| 	depends on !NO_DMA | ||||
| 	default y | ||||
| 
 | ||||
| config SGL_ALLOC | ||||
| 	bool | ||||
| 	default n | ||||
| 
 | ||||
| config DMA_NOOP_OPS | ||||
| 	bool | ||||
| 	depends on HAS_DMA && (!64BIT || ARCH_DMA_ADDR_T_64BIT) | ||||
|  | ||||
| @ -474,6 +474,111 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, | ||||
| } | ||||
| EXPORT_SYMBOL(sg_alloc_table_from_pages); | ||||
| 
 | ||||
| #ifdef CONFIG_SGL_ALLOC | ||||
| 
 | ||||
| /**
 | ||||
|  * sgl_alloc_order - allocate a scatterlist and its pages | ||||
|  * @length: Length in bytes of the scatterlist. Must be at least one | ||||
|  * @order: Second argument for alloc_pages() | ||||
|  * @chainable: Whether or not to allocate an extra element in the scatterlist | ||||
|  *	for scatterlist chaining purposes | ||||
|  * @gfp: Memory allocation flags | ||||
|  * @nent_p: [out] Number of entries in the scatterlist that have pages | ||||
|  * | ||||
|  * Returns: A pointer to an initialized scatterlist or %NULL upon failure. | ||||
|  */ | ||||
| struct scatterlist *sgl_alloc_order(unsigned long long length, | ||||
| 				    unsigned int order, bool chainable, | ||||
| 				    gfp_t gfp, unsigned int *nent_p) | ||||
| { | ||||
| 	struct scatterlist *sgl, *sg; | ||||
| 	struct page *page; | ||||
| 	unsigned int nent, nalloc; | ||||
| 	u32 elem_len; | ||||
| 
 | ||||
| 	nent = round_up(length, PAGE_SIZE << order) >> (PAGE_SHIFT + order); | ||||
| 	/* Check for integer overflow */ | ||||
| 	if (length > (nent << (PAGE_SHIFT + order))) | ||||
| 		return NULL; | ||||
| 	nalloc = nent; | ||||
| 	if (chainable) { | ||||
| 		/* Check for integer overflow */ | ||||
| 		if (nalloc + 1 < nalloc) | ||||
| 			return NULL; | ||||
| 		nalloc++; | ||||
| 	} | ||||
| 	sgl = kmalloc_array(nalloc, sizeof(struct scatterlist), | ||||
| 			    (gfp & ~GFP_DMA) | __GFP_ZERO); | ||||
| 	if (!sgl) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	sg_init_table(sgl, nent); | ||||
| 	sg = sgl; | ||||
| 	while (length) { | ||||
| 		elem_len = min_t(u64, length, PAGE_SIZE << order); | ||||
| 		page = alloc_pages(gfp, order); | ||||
| 		if (!page) { | ||||
| 			sgl_free(sgl); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 
 | ||||
| 		sg_set_page(sg, page, elem_len, 0); | ||||
| 		length -= elem_len; | ||||
| 		sg = sg_next(sg); | ||||
| 	} | ||||
| 	WARN_ON_ONCE(sg); | ||||
| 	if (nent_p) | ||||
| 		*nent_p = nent; | ||||
| 	return sgl; | ||||
| } | ||||
| EXPORT_SYMBOL(sgl_alloc_order); | ||||
| 
 | ||||
| /**
 | ||||
|  * sgl_alloc - allocate a scatterlist and its pages | ||||
|  * @length: Length in bytes of the scatterlist | ||||
|  * @gfp: Memory allocation flags | ||||
|  * @nent_p: [out] Number of entries in the scatterlist | ||||
|  * | ||||
|  * Returns: A pointer to an initialized scatterlist or %NULL upon failure. | ||||
|  */ | ||||
| struct scatterlist *sgl_alloc(unsigned long long length, gfp_t gfp, | ||||
| 			      unsigned int *nent_p) | ||||
| { | ||||
| 	return sgl_alloc_order(length, 0, false, gfp, nent_p); | ||||
| } | ||||
| EXPORT_SYMBOL(sgl_alloc); | ||||
| 
 | ||||
| /**
 | ||||
|  * sgl_free_order - free a scatterlist and its pages | ||||
|  * @sgl: Scatterlist with one or more elements | ||||
|  * @order: Second argument for __free_pages() | ||||
|  */ | ||||
| void sgl_free_order(struct scatterlist *sgl, int order) | ||||
| { | ||||
| 	struct scatterlist *sg; | ||||
| 	struct page *page; | ||||
| 
 | ||||
| 	for (sg = sgl; sg; sg = sg_next(sg)) { | ||||
| 		page = sg_page(sg); | ||||
| 		if (page) | ||||
| 			__free_pages(page, order); | ||||
| 	} | ||||
| 	kfree(sgl); | ||||
| } | ||||
| EXPORT_SYMBOL(sgl_free_order); | ||||
| 
 | ||||
| /**
 | ||||
|  * sgl_free - free a scatterlist and its pages | ||||
|  * @sgl: Scatterlist with one or more elements | ||||
|  */ | ||||
| void sgl_free(struct scatterlist *sgl) | ||||
| { | ||||
| 	sgl_free_order(sgl, 0); | ||||
| } | ||||
| EXPORT_SYMBOL(sgl_free); | ||||
| 
 | ||||
| #endif /* CONFIG_SGL_ALLOC */ | ||||
| 
 | ||||
| void __sg_page_iter_start(struct sg_page_iter *piter, | ||||
| 			  struct scatterlist *sglist, unsigned int nents, | ||||
| 			  unsigned long pgoffset) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user