[PATCH rdma-next 2/4] lib/scatterlist: Add support in dynamically allocation of SG entries

From: Leon Romanovsky
Date: Thu Sep 03 2020 - 11:12:41 EST


From: Maor Gottlieb <maorg@xxxxxxxxxx>

In order to support dynamic allocation of SG table, this patch
introduces sg_alloc_next. This function should be called to add more
entries to the table. In order to share the code, we will do the
following:
* Extract the allocation code from __sg_alloc_table to sg_alloc.
* Add a function to chain SGE to the next page.

Signed-off-by: Maor Gottlieb <maorg@xxxxxxxxxx>
Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxx>
---
include/linux/scatterlist.h | 29 ++++++----
lib/scatterlist.c | 110 ++++++++++++++++++++++++------------
2 files changed, 91 insertions(+), 48 deletions(-)

diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index 45cf7b69d852..877d6e160b06 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -165,6 +165,22 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
#define for_each_sgtable_dma_sg(sgt, sg, i) \
for_each_sg((sgt)->sgl, sg, (sgt)->nents, i)

+static inline void _sg_chain(struct scatterlist *chain_sg,
+ struct scatterlist *sgl)
+{
+ /*
+ * offset and length are unused for chain entry. Clear them.
+ */
+ chain_sg->offset = 0;
+ chain_sg->length = 0;
+
+ /*
+ * Set lowest bit to indicate a link pointer, and make sure to clear
+ * the termination bit if it happens to be set.
+ */
+ chain_sg->page_link = ((unsigned long) sgl | SG_CHAIN) & ~SG_END;
+}
+
/**
* sg_chain - Chain two sglists together
* @prv: First scatterlist
@@ -178,18 +194,7 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents,
struct scatterlist *sgl)
{
- /*
- * offset and length are unused for chain entry. Clear them.
- */
- prv[prv_nents - 1].offset = 0;
- prv[prv_nents - 1].length = 0;
-
- /*
- * Set lowest bit to indicate a link pointer, and make sure to clear
- * the termination bit if it happens to be set.
- */
- prv[prv_nents - 1].page_link = ((unsigned long) sgl | SG_CHAIN)
- & ~SG_END;
+ _sg_chain(&prv[prv_nents - 1], sgl);
}

/**
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 292e785d21ee..669bd6e6d16a 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -242,38 +242,15 @@ void sg_free_table(struct sg_table *table)
}
EXPORT_SYMBOL(sg_free_table);

-/**
- * __sg_alloc_table - Allocate and initialize an sg table with given allocator
- * @table: The sg table header to use
- * @nents: Number of entries in sg list
- * @max_ents: The maximum number of entries the allocator returns per call
- * @nents_first_chunk: Number of entries int the (preallocated) first
- * scatterlist chunk, 0 means no such preallocated chunk provided by user
- * @gfp_mask: GFP allocation mask
- * @alloc_fn: Allocator to use
- *
- * Description:
- * This function returns a @table @nents long. The allocator is
- * defined to return scatterlist chunks of maximum size @max_ents.
- * Thus if @nents is bigger than @max_ents, the scatterlists will be
- * chained in units of @max_ents.
- *
- * Notes:
- * If this function returns non-0 (eg failure), the caller must call
- * __sg_free_table() to cleanup any leftover allocations.
- *
- **/
-int __sg_alloc_table(struct sg_table *table, unsigned int nents,
- unsigned int max_ents, struct scatterlist *first_chunk,
- unsigned int nents_first_chunk, gfp_t gfp_mask,
- sg_alloc_fn *alloc_fn)
+static int sg_alloc(struct sg_table *table, struct scatterlist *prv,
+ unsigned int nents, unsigned int max_ents,
+ struct scatterlist *first_chunk,
+ unsigned int nents_first_chunk,
+ gfp_t gfp_mask, sg_alloc_fn *alloc_fn)
{
- struct scatterlist *sg, *prv;
- unsigned int left;
- unsigned curr_max_ents = nents_first_chunk ?: max_ents;
- unsigned prv_max_ents;
-
- memset(table, 0, sizeof(*table));
+ unsigned int curr_max_ents = nents_first_chunk ?: max_ents;
+ unsigned int left, prv_max_ents = 0;
+ struct scatterlist *sg;

if (nents == 0)
return -EINVAL;
@@ -283,7 +260,6 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,
#endif

left = nents;
- prv = NULL;
do {
unsigned int sg_size, alloc_size = left;

@@ -308,7 +284,7 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,
* linkage. Without this, sg_kfree() may get
* confused.
*/
- if (prv)
+ if (prv_max_ents)
table->nents = ++table->orig_nents;

return -ENOMEM;
@@ -321,10 +297,17 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,
* If this is the first mapping, assign the sg table header.
* If this is not the first mapping, chain previous part.
*/
- if (prv)
- sg_chain(prv, prv_max_ents, sg);
- else
+ if (!prv)
table->sgl = sg;
+ else if (prv_max_ents)
+ sg_chain(prv, prv_max_ents, sg);
+ else {
+ _sg_chain(prv, sg);
+ /* We decrease one since the prvious last sge in used to
+ * chainning.
+ */
+ table->nents = table->orig_nents -= 1;
+ }

/*
* If no more entries after this one, mark the end
@@ -339,6 +322,61 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,

return 0;
}
+
+/**
+ * sg_alloc_next - Allocate and initialize new entries in the sg table
+ * @table: The sg table header to use
+ * @last: The last scatter list entry in the table
+ * @nents: Number of entries in sg list
+ * @max_ents: The maximum number of entries the allocator returns per call
+ * @gfp_mask: GFP allocation mask
+ * @alloc_fn: Allocator to use
+ *
+ * Description:
+ * This function extend @table with @nents long. The allocator is
+ * defined to return scatterlist chunks of maximum size @max_ents.
+ * Thus if @nents is bigger than @max_ents, the scatterlists will be
+ * chained in units of @max_ents.
+ *
+ **/
+static int sg_alloc_next(struct sg_table *table, struct scatterlist *last,
+ unsigned int nents, unsigned int max_ents,
+ gfp_t gfp_mask)
+{
+ return sg_alloc(table, last, nents, max_ents, NULL, 0, gfp_mask,
+ sg_kmalloc);
+}
+
+/**
+ * __sg_alloc_table - Allocate and initialize an sg table with given allocator
+ * @table: The sg table header to use
+ * @nents: Number of entries in sg list
+ * @max_ents: The maximum number of entries the allocator returns per call
+ * @nents_first_chunk: Number of entries int the (preallocated) first
+ * scatterlist chunk, 0 means no such preallocated chunk provided by user
+ * @gfp_mask: GFP allocation mask
+ * @alloc_fn: Allocator to use
+ *
+ * Description:
+ * This function returns a @table @nents long. The allocator is
+ * defined to return scatterlist chunks of maximum size @max_ents.
+ * Thus if @nents is bigger than @max_ents, the scatterlists will be
+ * chained in units of @max_ents.
+ *
+ * Notes:
+ * If this function returns non-0 (eg failure), the caller must call
+ * __sg_free_table() to cleanup any leftover allocations.
+ *
+ **/
+int __sg_alloc_table(struct sg_table *table, unsigned int nents,
+ unsigned int max_ents, struct scatterlist *first_chunk,
+ unsigned int nents_first_chunk, gfp_t gfp_mask,
+ sg_alloc_fn *alloc_fn)
+{
+ memset(table, 0, sizeof(*table));
+ return sg_alloc(table, NULL, nents, max_ents, first_chunk,
+ nents_first_chunk, gfp_mask, alloc_fn);
+}
EXPORT_SYMBOL(__sg_alloc_table);

/**
--
2.26.2