forked from mirrors/linux
		
	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
					
				
					 3 changed files with 119 additions and 0 deletions
				
			
		|  | @ -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 int n_pages, unsigned int offset, | ||||||
| 			      unsigned long size, gfp_t gfp_mask); | 			      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 sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, | ||||||
| 		      size_t buflen, off_t skip, bool to_buffer); | 		      size_t buflen, off_t skip, bool to_buffer); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -409,6 +409,10 @@ config HAS_DMA | ||||||
| 	depends on !NO_DMA | 	depends on !NO_DMA | ||||||
| 	default y | 	default y | ||||||
| 
 | 
 | ||||||
|  | config SGL_ALLOC | ||||||
|  | 	bool | ||||||
|  | 	default n | ||||||
|  | 
 | ||||||
| config DMA_NOOP_OPS | config DMA_NOOP_OPS | ||||||
| 	bool | 	bool | ||||||
| 	depends on HAS_DMA && (!64BIT || ARCH_DMA_ADDR_T_64BIT) | 	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); | 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, | void __sg_page_iter_start(struct sg_page_iter *piter, | ||||||
| 			  struct scatterlist *sglist, unsigned int nents, | 			  struct scatterlist *sglist, unsigned int nents, | ||||||
| 			  unsigned long pgoffset) | 			  unsigned long pgoffset) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Bart Van Assche
						Bart Van Assche