From: Maor Gottlieb <ma...@nvidia.com>

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 <ma...@nvidia.com>
Signed-off-by: Leon Romanovsky <leo...@nvidia.com>
---
 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

Reply via email to