From: Maor Gottlieb <maorg@xxxxxxxxxx> Currently, sg_alloc_table_from_pages doesn't support dynamic chaining of SG entries. Therefore it requires from user to allocate all the pages in advance and hold them in a large buffer. Such a buffer consumes a lot of temporary memory in HPC systems which do a very large memory registration. The next patches introduce API for dynamically allocation from pages and it requires us to do the following: * Extract the code to alloc_from_pages_common. * Change the build of the table to iterate on the chunks and not on the SGEs. It will allow dynamic allocation of more SGEs. Since sg_alloc_table_from_pages allocate exactly the number of chunks, therefore chunks are equal to the number of SG entries. Signed-off-by: Maor Gottlieb <maorg@xxxxxxxxxx> Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxx> --- lib/scatterlist.c | 75 ++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 5d63a8857f36..292e785d21ee 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -365,38 +365,18 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) } EXPORT_SYMBOL(sg_alloc_table); -/** - * __sg_alloc_table_from_pages - Allocate and initialize an sg table from - * an array of pages - * @sgt: The sg table header to use - * @pages: Pointer to an array of page pointers - * @n_pages: Number of pages in the pages array - * @offset: Offset from start of the first page to the start of a buffer - * @size: Number of valid bytes in the buffer (after offset) - * @max_segment: Maximum size of a scatterlist node in bytes (page aligned) - * @gfp_mask: GFP allocation mask - * - * Description: - * Allocate and initialize an sg table from a list of pages. Contiguous - * ranges of the pages are squashed into a single scatterlist node up to the - * maximum size specified in @max_segment. An user may provide an offset at a - * start and a size of valid data in a buffer specified by the page array. - * The returned sg table is released by sg_free_table. - * - * Returns: - * 0 on success, negative error on failure - */ -int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, - unsigned int n_pages, unsigned int offset, - unsigned long size, unsigned int max_segment, - gfp_t gfp_mask) +static struct scatterlist * +alloc_from_pages_common(struct sg_table *sgt, struct page **pages, + unsigned int n_pages, unsigned int offset, + unsigned long size, unsigned int max_segment, + gfp_t gfp_mask) { unsigned int chunks, cur_page, seg_len, i; + struct scatterlist *prv, *s = NULL; int ret; - struct scatterlist *s; if (WARN_ON(!max_segment || offset_in_page(max_segment))) - return -EINVAL; + return ERR_PTR(-EINVAL); /* compute number of contiguous chunks */ chunks = 1; @@ -412,11 +392,12 @@ int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, ret = sg_alloc_table(sgt, chunks, gfp_mask); if (unlikely(ret)) - return ret; + return ERR_PTR(ret); /* merging chunks and putting them into the scatterlist */ cur_page = 0; - for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { + s = sgt->sgl; + for (i = 0; i < chunks; i++) { unsigned int j, chunk_size; /* look for the end of the current chunk */ @@ -435,9 +416,43 @@ int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, size -= chunk_size; offset = 0; cur_page = j; + prv = s; + s = sg_next(s); } + return prv; +} - return 0; +/** + * __sg_alloc_table_from_pages - Allocate and initialize an sg table from + * an array of pages + * @sgt: The sg table header to use + * @pages: Pointer to an array of page pointers + * @n_pages: Number of pages in the pages array + * @offset: Offset from start of the first page to the start of a buffer + * @size: Number of valid bytes in the buffer (after offset) + * @max_segment: Maximum size of a scatterlist node in bytes (page aligned) + * @gfp_mask: GFP allocation mask + * + * Description: + * Allocate and initialize an sg table from a list of pages. Contiguous + * ranges of the pages are squashed into a single scatterlist node up to the + * maximum size specified in @max_segment. A user may provide an offset at a + * start and a size of valid data in a buffer specified by the page array. + * The returned sg table is released by sg_free_table. + * + * Returns: + * 0 on success, negative error on failure + */ +int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, + unsigned int n_pages, unsigned int offset, + unsigned long size, unsigned int max_segment, + gfp_t gfp_mask) +{ + struct scatterlist *sg; + + sg = alloc_from_pages_common(sgt, pages, n_pages, offset, size, + max_segment, gfp_mask); + return PTR_ERR_OR_ZERO(sg); } EXPORT_SYMBOL(__sg_alloc_table_from_pages); -- 2.26.2