linux-block.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] scatterlist: add new capabilities
@ 2020-10-16  4:52 Douglas Gilbert
  2020-10-16  4:52 ` [PATCH 1/4] sgl_alloc_order: remove 4 GiB limit, sgl_free() warning Douglas Gilbert
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Douglas Gilbert @ 2020-10-16  4:52 UTC (permalink / raw)
  To: linux-scsi, linux-block, linux-kernel; +Cc: martin.petersen, axboe, bvanassche

Scatter-gather lists (sgl_s) are frequently used as data
carriers in the block layer. For example the SCSI and NVMe
subsystems interchange data with the block layer using
sgl_s. The sgl API is declared in <linux/scatterlist.h>

The author has extended these transient sgl use cases to
a store (i.e. ramdisk) in the scsi_debug driver. Other new
potential uses of sgl_s could be for caches. When this extra
step is taken, the need to copy between sgl_s becomes apparent.
The patchset adds sgl_copy_sgl() and a few other sgl
operations.

The existing sgl_alloc_order() function can be seen as a
replacement for vmalloc() for large, long-term allocations.
For what seems like no good reason, sgl_alloc_order()
currently restricts its total allocation to less than or
equal to 4 GiB. vmalloc() has no such restriction.

This patchset is against lk 5.9.0

Douglas Gilbert (4):
  sgl_alloc_order: remove 4 GiB limit, sgl_free() warning
  scatterlist: add sgl_copy_sgl() function
  scatterlist: add sgl_compare_sgl() function
  scatterlist: add sgl_memset()

 include/linux/scatterlist.h |  12 +++
 lib/scatterlist.c           | 204 +++++++++++++++++++++++++++++++++++-
 2 files changed, 213 insertions(+), 3 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/4] sgl_alloc_order: remove 4 GiB limit, sgl_free() warning
  2020-10-16  4:52 [PATCH 0/4] scatterlist: add new capabilities Douglas Gilbert
@ 2020-10-16  4:52 ` Douglas Gilbert
  2020-10-16  4:52 ` [PATCH 2/4] scatterlist: add sgl_copy_sgl() function Douglas Gilbert
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Douglas Gilbert @ 2020-10-16  4:52 UTC (permalink / raw)
  To: linux-scsi, linux-block, linux-kernel; +Cc: martin.petersen, axboe, bvanassche

This patch removes a check done by sgl_alloc_order() before it starts
any allocations. The comment before the removed code says: "Check for
integer overflow" arguably gives a false sense of security. The right
hand side of the expression in the condition is resolved as u32 so
cannot exceed UINT32_MAX (4 GiB) which means 'length' cannot exceed
that amount. If that was the intention then the comment above it
could be dropped and the condition rewritten more clearly as:
     if (length > UINT32_MAX) <<failure path >>;

The author's intention is to use sgl_alloc_order() to replace
vmalloc(unsigned long) for a large allocation (debug ramdisk).
vmalloc has no limit at 4 GiB so its seems unreasonable that:
    sgl_alloc_order(unsigned long long length, ....)
does. sgl_s made with sgl_alloc_order(chainable=false) have equally
sized segments placed in a scatter gather array. That allows O(1)
navigation around a big sgl using some simple integer maths.

Having previously sent a patch to fix a memory leak in
sg_alloc_order() take the opportunity to put a one line comment above
sgl_free()'s declaration that it is not suitable when order > 0 . The
mis-use of sgl_free() when order > 0 was the reason for the memory
leak. The other users of sgl_alloc_order() in the kernel where
checked and found to handle free-ing properly.

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
---
 include/linux/scatterlist.h | 1 +
 lib/scatterlist.c           | 3 ---
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index 45cf7b69d852..80178afc2a4a 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -302,6 +302,7 @@ struct scatterlist *sgl_alloc(unsigned long long length, gfp_t gfp,
 			      unsigned int *nent_p);
 void sgl_free_n_order(struct scatterlist *sgl, int nents, int order);
 void sgl_free_order(struct scatterlist *sgl, int order);
+/* Only use sgl_free() when order is 0 */
 void sgl_free(struct scatterlist *sgl);
 #endif /* CONFIG_SGL_ALLOC */
 
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index c448642e0f78..d5770e7f1030 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -493,9 +493,6 @@ struct scatterlist *sgl_alloc_order(unsigned long long length,
 	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 */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/4] scatterlist: add sgl_copy_sgl() function
  2020-10-16  4:52 [PATCH 0/4] scatterlist: add new capabilities Douglas Gilbert
  2020-10-16  4:52 ` [PATCH 1/4] sgl_alloc_order: remove 4 GiB limit, sgl_free() warning Douglas Gilbert
@ 2020-10-16  4:52 ` Douglas Gilbert
  2020-10-16 11:17   ` Bodo Stroesser
  2020-10-16  4:52 ` [PATCH 3/4] scatterlist: add sgl_compare_sgl() function Douglas Gilbert
  2020-10-16  4:52 ` [PATCH 4/4] scatterlist: add sgl_memset() Douglas Gilbert
  3 siblings, 1 reply; 7+ messages in thread
From: Douglas Gilbert @ 2020-10-16  4:52 UTC (permalink / raw)
  To: linux-scsi, linux-block, linux-kernel; +Cc: martin.petersen, axboe, bvanassche

Both the SCSI and NVMe subsystems receive user data from the block
layer in scatterlist_s (aka scatter gather lists (sgl) which are
often arrays). If drivers in those subsystems represent storage
(e.g. a ramdisk) or cache "hot" user data then they may also
choose to use scatterlist_s. Currently there are no sgl to sgl
operations in the kernel. Start with a copy.

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
---
 include/linux/scatterlist.h |  4 ++
 lib/scatterlist.c           | 86 +++++++++++++++++++++++++++++++++++++
 2 files changed, 90 insertions(+)

diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index 80178afc2a4a..6649414c0749 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -321,6 +321,10 @@ size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
 size_t sg_zero_buffer(struct scatterlist *sgl, unsigned int nents,
 		       size_t buflen, off_t skip);
 
+size_t sgl_copy_sgl(struct scatterlist *d_sgl, unsigned int d_nents, off_t d_skip,
+		    struct scatterlist *s_sgl, unsigned int s_nents, off_t s_skip,
+		    size_t n_bytes);
+
 /*
  * Maximum number of entries that will be allocated in one piece, if
  * a list larger than this is required then chaining will be utilized.
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index d5770e7f1030..1ec2c909c8d4 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -974,3 +974,89 @@ size_t sg_zero_buffer(struct scatterlist *sgl, unsigned int nents,
 	return offset;
 }
 EXPORT_SYMBOL(sg_zero_buffer);
+
+/**
+ * sgl_copy_sgl - Copy over a destination sgl from a source sgl
+ * @d_sgl:		 Destination sgl
+ * @d_nents:		 Number of SG entries in destination sgl
+ * @d_skip:		 Number of bytes to skip in destination before copying
+ * @s_sgl:		 Source sgl
+ * @s_nents:		 Number of SG entries in source sgl
+ * @s_skip:		 Number of bytes to skip in source before copying
+ * @n_bytes:		 The number of bytes to copy
+ *
+ * Returns the number of copied bytes.
+ *
+ * Notes:
+ *   Destination arguments appear before the source arguments, as with memcpy().
+ *
+ *   Stops copying if the end of d_sgl or s_sgl is reached.
+ *
+ *   Since memcpy() is used, overlapping copies (where d_sgl and s_sgl belong
+ *   to the same sgl and the copy regions overlap) are not supported.
+ *
+ *   If d_skip is large, potentially spanning multiple d_nents then some
+ *   integer arithmetic to adjust d_sgl may improve performance. For example
+ *   if d_sgl is built using sgl_alloc_order(chainable=false) then the sgl
+ *   will be an array with equally sized segments facilitating that
+ *   arithmetic. The suggestion applies to s_skip, s_sgl and s_nents as well.
+ *
+ **/
+size_t sgl_copy_sgl(struct scatterlist *d_sgl, unsigned int d_nents, off_t d_skip,
+		    struct scatterlist *s_sgl, unsigned int s_nents, off_t s_skip,
+		    size_t n_bytes)
+{
+	size_t d_off, s_off, len, d_len, s_len;
+	size_t offset = 0;
+	struct sg_mapping_iter d_iter;
+	struct sg_mapping_iter s_iter;
+
+	if (n_bytes == 0)
+		return 0;
+	sg_miter_start(&d_iter, d_sgl, d_nents, SG_MITER_ATOMIC | SG_MITER_TO_SG);
+	sg_miter_start(&s_iter, s_sgl, s_nents, SG_MITER_ATOMIC | SG_MITER_FROM_SG);
+	if (!sg_miter_skip(&d_iter, d_skip))
+		goto fini;
+	if (!sg_miter_skip(&s_iter, s_skip))
+		goto fini;
+
+	for (d_off = 0, s_off = 0; true ; ) {
+		/* Assume d_iter.length and s_iter.length can never be 0 */
+		if (d_off == 0) {
+			if (!sg_miter_next(&d_iter))
+				break;
+			d_len = d_iter.length;
+		} else {
+			d_len = d_iter.length - d_off;
+		}
+		if (s_off == 0) {
+			if (!sg_miter_next(&s_iter))
+				break;
+			s_len = s_iter.length;
+		} else {
+			s_len = s_iter.length - s_off;
+		}
+		len = min3(d_len, s_len, n_bytes - offset);
+
+		memcpy(d_iter.addr + d_off, s_iter.addr + s_off, len);
+		offset += len;
+		if (offset >= n_bytes)
+			break;
+		if (d_len == s_len) {
+			d_off = 0;
+			s_off = 0;
+		} else if (d_len < s_len) {
+			d_off = 0;
+			s_off += len;
+		} else {
+			d_off += len;
+			s_off = 0;
+		}
+	}
+fini:
+	sg_miter_stop(&d_iter);
+	sg_miter_stop(&s_iter);
+	return offset;
+}
+EXPORT_SYMBOL(sgl_copy_sgl);
+
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/4] scatterlist: add sgl_compare_sgl() function
  2020-10-16  4:52 [PATCH 0/4] scatterlist: add new capabilities Douglas Gilbert
  2020-10-16  4:52 ` [PATCH 1/4] sgl_alloc_order: remove 4 GiB limit, sgl_free() warning Douglas Gilbert
  2020-10-16  4:52 ` [PATCH 2/4] scatterlist: add sgl_copy_sgl() function Douglas Gilbert
@ 2020-10-16  4:52 ` Douglas Gilbert
  2020-10-16  4:52 ` [PATCH 4/4] scatterlist: add sgl_memset() Douglas Gilbert
  3 siblings, 0 replies; 7+ messages in thread
From: Douglas Gilbert @ 2020-10-16  4:52 UTC (permalink / raw)
  To: linux-scsi, linux-block, linux-kernel; +Cc: martin.petersen, axboe, bvanassche

After enabling copies between scatter gather lists (sgl_s),
another storage related operation is to compare two sgl_s.
This new function is modelled on NVMe's Compare command and
the SCSI VERIFY(BYTCHK=1) command. Like memcmp() this function
returns false on the first miscompare and stop comparing.

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
---
 include/linux/scatterlist.h |  4 ++
 lib/scatterlist.c           | 84 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index 6649414c0749..ae260dc5fedb 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -325,6 +325,10 @@ size_t sgl_copy_sgl(struct scatterlist *d_sgl, unsigned int d_nents, off_t d_ski
 		    struct scatterlist *s_sgl, unsigned int s_nents, off_t s_skip,
 		    size_t n_bytes);
 
+bool sgl_compare_sgl(struct scatterlist *x_sgl, unsigned int x_nents, off_t x_skip,
+		     struct scatterlist *y_sgl, unsigned int y_nents, off_t y_skip,
+		     size_t n_bytes);
+
 /*
  * Maximum number of entries that will be allocated in one piece, if
  * a list larger than this is required then chaining will be utilized.
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 1ec2c909c8d4..344725990b9d 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -979,10 +979,10 @@ EXPORT_SYMBOL(sg_zero_buffer);
  * sgl_copy_sgl - Copy over a destination sgl from a source sgl
  * @d_sgl:		 Destination sgl
  * @d_nents:		 Number of SG entries in destination sgl
- * @d_skip:		 Number of bytes to skip in destination before copying
+ * @d_skip:		 Number of bytes to skip in destination before starting
  * @s_sgl:		 Source sgl
  * @s_nents:		 Number of SG entries in source sgl
- * @s_skip:		 Number of bytes to skip in source before copying
+ * @s_skip:		 Number of bytes to skip in source before starting
  * @n_bytes:		 The number of bytes to copy
  *
  * Returns the number of copied bytes.
@@ -1060,3 +1060,83 @@ size_t sgl_copy_sgl(struct scatterlist *d_sgl, unsigned int d_nents, off_t d_ski
 }
 EXPORT_SYMBOL(sgl_copy_sgl);
 
+/**
+ * sgl_compare_sgl - Compare x and y (both sgl_s)
+ * @x_sgl:		 x (left) sgl
+ * @x_nents:		 Number of SG entries in x (left) sgl
+ * @x_skip:		 Number of bytes to skip in x (left) before starting
+ * @y_sgl:		 y (right) sgl
+ * @y_nents:		 Number of SG entries in y (right) sgl
+ * @y_skip:		 Number of bytes to skip in y (right) before starting
+ * @n_bytes:		 The number of bytes to compare
+ *
+ * Returns true if x and y compare equal before x, y or n_bytes is exhausted.
+ * Otherwise on a miscompare, returns false (and stops comparing).
+ *
+ * Notes:
+ *   x and y are symmetrical: they can be swapped and the result is the same.
+ *
+ *   Implementation is based on memcmp(). x and y segments may overlap.
+ *
+ *   Same comment from sgl_copy_sgl() about large _skip arguments applies here
+ *   as well.
+ *
+ **/
+bool sgl_compare_sgl(struct scatterlist *x_sgl, unsigned int x_nents, off_t x_skip,
+		     struct scatterlist *y_sgl, unsigned int y_nents, off_t y_skip,
+		     size_t n_bytes)
+{
+	bool equ = true;
+	size_t x_off, y_off, len, x_len, y_len;
+	size_t offset = 0;
+	struct sg_mapping_iter x_iter;
+	struct sg_mapping_iter y_iter;
+
+	if (n_bytes == 0)
+		return true;
+	sg_miter_start(&x_iter, x_sgl, x_nents, SG_MITER_ATOMIC | SG_MITER_TO_SG);
+	sg_miter_start(&y_iter, y_sgl, y_nents, SG_MITER_ATOMIC | SG_MITER_FROM_SG);
+	if (!sg_miter_skip(&x_iter, x_skip))
+		goto fini;
+	if (!sg_miter_skip(&y_iter, y_skip))
+		goto fini;
+
+	for (x_off = 0, y_off = 0; true ; ) {
+		/* Assume x_iter.length and y_iter.length can never be 0 */
+		if (x_off == 0) {
+			if (!sg_miter_next(&x_iter))
+				break;
+			x_len = x_iter.length;
+		} else {
+			x_len = x_iter.length - x_off;
+		}
+		if (y_off == 0) {
+			if (!sg_miter_next(&y_iter))
+				break;
+			y_len = y_iter.length;
+		} else {
+			y_len = y_iter.length - y_off;
+		}
+		len = min3(x_len, y_len, n_bytes - offset);
+
+		equ = memcmp(x_iter.addr + x_off, y_iter.addr + y_off, len) == 0;
+		offset += len;
+		if (!equ || offset >= n_bytes)
+			break;
+		if (x_len == y_len) {
+			x_off = 0;
+			y_off = 0;
+		} else if (x_len < y_len) {
+			x_off = 0;
+			y_off += len;
+		} else {
+			x_off += len;
+			y_off = 0;
+		}
+	}
+fini:
+	sg_miter_stop(&x_iter);
+	sg_miter_stop(&y_iter);
+	return equ;
+}
+EXPORT_SYMBOL(sgl_compare_sgl);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/4] scatterlist: add sgl_memset()
  2020-10-16  4:52 [PATCH 0/4] scatterlist: add new capabilities Douglas Gilbert
                   ` (2 preceding siblings ...)
  2020-10-16  4:52 ` [PATCH 3/4] scatterlist: add sgl_compare_sgl() function Douglas Gilbert
@ 2020-10-16  4:52 ` Douglas Gilbert
  3 siblings, 0 replies; 7+ messages in thread
From: Douglas Gilbert @ 2020-10-16  4:52 UTC (permalink / raw)
  To: linux-scsi, linux-block, linux-kernel; +Cc: martin.petersen, axboe, bvanassche

The existing sg_zero_buffer() function is a bit restrictive.
For example protection information (PI) blocks are usually
initialized to 0xff bytes. As its name suggests sgl_memset()
is modelled on memset(). One difference is the type of the
val argument which is u8 rather than int.

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
---
 include/linux/scatterlist.h |  3 +++
 lib/scatterlist.c           | 39 +++++++++++++++++++++++++++++++++++--
 2 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index ae260dc5fedb..e50dc9a6d887 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -329,6 +329,9 @@ bool sgl_compare_sgl(struct scatterlist *x_sgl, unsigned int x_nents, off_t x_sk
 		     struct scatterlist *y_sgl, unsigned int y_nents, off_t y_skip,
 		     size_t n_bytes);
 
+void sgl_memset(struct scatterlist *sgl, unsigned int nents, off_t skip,
+		u8 val, size_t n_bytes);
+
 /*
  * Maximum number of entries that will be allocated in one piece, if
  * a list larger than this is required then chaining will be utilized.
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 344725990b9d..3ca66f0c949f 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -1083,8 +1083,8 @@ EXPORT_SYMBOL(sgl_copy_sgl);
  *
  **/
 bool sgl_compare_sgl(struct scatterlist *x_sgl, unsigned int x_nents, off_t x_skip,
-		     struct scatterlist *y_sgl, unsigned int y_nents, off_t y_skip,
-		     size_t n_bytes)
+		    struct scatterlist *y_sgl, unsigned int y_nents, off_t y_skip,
+		    size_t n_bytes)
 {
 	bool equ = true;
 	size_t x_off, y_off, len, x_len, y_len;
@@ -1140,3 +1140,38 @@ bool sgl_compare_sgl(struct scatterlist *x_sgl, unsigned int x_nents, off_t x_sk
 	return equ;
 }
 EXPORT_SYMBOL(sgl_compare_sgl);
+
+/**
+ * sgl_memset - set byte 'val' n_bytes times on SG list
+ * @sgl:		 The SG list
+ * @nents:		 Number of SG entries in sgl
+ * @skip:		 Number of bytes to skip before starting
+ * @val:		 byte value to write to sgl
+ * @n_bytes:		 The number of bytes to modify
+ *
+ * Notes:
+ *   Writes val n_bytes times or until sgl is exhausted.
+ *
+ **/
+void sgl_memset(struct scatterlist *sgl, unsigned int nents, off_t skip,
+		u8 val, size_t n_bytes)
+{
+	size_t offset = 0;
+	size_t len;
+	struct sg_mapping_iter miter;
+	unsigned int sg_flags = SG_MITER_ATOMIC | SG_MITER_TO_SG;
+
+	if (n_bytes == 0)
+		return;
+	sg_miter_start(&miter, sgl, nents, sg_flags);
+	if (!sg_miter_skip(&miter, skip))
+		goto fini;
+
+	while ((offset < n_bytes) && sg_miter_next(&miter)) {
+		len = min(miter.length, n_bytes - offset);
+		memset(miter.addr, val, len);
+		offset += len;
+	}
+fini:
+	sg_miter_stop(&miter);
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/4] scatterlist: add sgl_copy_sgl() function
  2020-10-16  4:52 ` [PATCH 2/4] scatterlist: add sgl_copy_sgl() function Douglas Gilbert
@ 2020-10-16 11:17   ` Bodo Stroesser
  2020-10-16 15:28     ` Douglas Gilbert
  0 siblings, 1 reply; 7+ messages in thread
From: Bodo Stroesser @ 2020-10-16 11:17 UTC (permalink / raw)
  To: Douglas Gilbert, linux-scsi, linux-block, linux-kernel
  Cc: martin.petersen, axboe, bvanassche

Hi Douglas,

AFAICS this patch - and also patch 3 - are not correct.
When started with SG_MITER_ATOMIC, sg_miter_next and sg_miter_stop use
the k(un)map_atomic calls. But these have to be used strictly nested
according to docu and code.
The below code uses the atomic mappings in overlapping mode.

Regards,
Bodo

Am 16.10.20 um 06:52 schrieb Douglas Gilbert:
> Both the SCSI and NVMe subsystems receive user data from the block
> layer in scatterlist_s (aka scatter gather lists (sgl) which are
> often arrays). If drivers in those subsystems represent storage
> (e.g. a ramdisk) or cache "hot" user data then they may also
> choose to use scatterlist_s. Currently there are no sgl to sgl
> operations in the kernel. Start with a copy.
> 
> Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
> ---
>   include/linux/scatterlist.h |  4 ++
>   lib/scatterlist.c           | 86 +++++++++++++++++++++++++++++++++++++
>   2 files changed, 90 insertions(+)
> 
> diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
> index 80178afc2a4a..6649414c0749 100644
> --- a/include/linux/scatterlist.h
> +++ b/include/linux/scatterlist.h
> @@ -321,6 +321,10 @@ size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
>   size_t sg_zero_buffer(struct scatterlist *sgl, unsigned int nents,
>   		       size_t buflen, off_t skip);
>   
> +size_t sgl_copy_sgl(struct scatterlist *d_sgl, unsigned int d_nents, off_t d_skip,
> +		    struct scatterlist *s_sgl, unsigned int s_nents, off_t s_skip,
> +		    size_t n_bytes);
> +
>   /*
>    * Maximum number of entries that will be allocated in one piece, if
>    * a list larger than this is required then chaining will be utilized.
> diff --git a/lib/scatterlist.c b/lib/scatterlist.c
> index d5770e7f1030..1ec2c909c8d4 100644
> --- a/lib/scatterlist.c
> +++ b/lib/scatterlist.c
> @@ -974,3 +974,89 @@ size_t sg_zero_buffer(struct scatterlist *sgl, unsigned int nents,
>   	return offset;
>   }
>   EXPORT_SYMBOL(sg_zero_buffer);
> +
> +/**
> + * sgl_copy_sgl - Copy over a destination sgl from a source sgl
> + * @d_sgl:		 Destination sgl
> + * @d_nents:		 Number of SG entries in destination sgl
> + * @d_skip:		 Number of bytes to skip in destination before copying
> + * @s_sgl:		 Source sgl
> + * @s_nents:		 Number of SG entries in source sgl
> + * @s_skip:		 Number of bytes to skip in source before copying
> + * @n_bytes:		 The number of bytes to copy
> + *
> + * Returns the number of copied bytes.
> + *
> + * Notes:
> + *   Destination arguments appear before the source arguments, as with memcpy().
> + *
> + *   Stops copying if the end of d_sgl or s_sgl is reached.
> + *
> + *   Since memcpy() is used, overlapping copies (where d_sgl and s_sgl belong
> + *   to the same sgl and the copy regions overlap) are not supported.
> + *
> + *   If d_skip is large, potentially spanning multiple d_nents then some
> + *   integer arithmetic to adjust d_sgl may improve performance. For example
> + *   if d_sgl is built using sgl_alloc_order(chainable=false) then the sgl
> + *   will be an array with equally sized segments facilitating that
> + *   arithmetic. The suggestion applies to s_skip, s_sgl and s_nents as well.
> + *
> + **/
> +size_t sgl_copy_sgl(struct scatterlist *d_sgl, unsigned int d_nents, off_t d_skip,
> +		    struct scatterlist *s_sgl, unsigned int s_nents, off_t s_skip,
> +		    size_t n_bytes)
> +{
> +	size_t d_off, s_off, len, d_len, s_len;
> +	size_t offset = 0;
> +	struct sg_mapping_iter d_iter;
> +	struct sg_mapping_iter s_iter;
> +
> +	if (n_bytes == 0)
> +		return 0;
> +	sg_miter_start(&d_iter, d_sgl, d_nents, SG_MITER_ATOMIC | SG_MITER_TO_SG);
> +	sg_miter_start(&s_iter, s_sgl, s_nents, SG_MITER_ATOMIC | SG_MITER_FROM_SG);
> +	if (!sg_miter_skip(&d_iter, d_skip))
> +		goto fini;
> +	if (!sg_miter_skip(&s_iter, s_skip))
> +		goto fini;
> +
> +	for (d_off = 0, s_off = 0; true ; ) {
> +		/* Assume d_iter.length and s_iter.length can never be 0 */
> +		if (d_off == 0) {
> +			if (!sg_miter_next(&d_iter))
> +				break;
> +			d_len = d_iter.length;
> +		} else {
> +			d_len = d_iter.length - d_off;
> +		}
> +		if (s_off == 0) {
> +			if (!sg_miter_next(&s_iter))
> +				break;
> +			s_len = s_iter.length;
> +		} else {
> +			s_len = s_iter.length - s_off;
> +		}
> +		len = min3(d_len, s_len, n_bytes - offset);
> +
> +		memcpy(d_iter.addr + d_off, s_iter.addr + s_off, len);
> +		offset += len;
> +		if (offset >= n_bytes)
> +			break;
> +		if (d_len == s_len) {
> +			d_off = 0;
> +			s_off = 0;
> +		} else if (d_len < s_len) {
> +			d_off = 0;
> +			s_off += len;
> +		} else {
> +			d_off += len;
> +			s_off = 0;
> +		}
> +	}
> +fini:
> +	sg_miter_stop(&d_iter);
> +	sg_miter_stop(&s_iter);
> +	return offset;
> +}
> +EXPORT_SYMBOL(sgl_copy_sgl);
> +
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/4] scatterlist: add sgl_copy_sgl() function
  2020-10-16 11:17   ` Bodo Stroesser
@ 2020-10-16 15:28     ` Douglas Gilbert
  0 siblings, 0 replies; 7+ messages in thread
From: Douglas Gilbert @ 2020-10-16 15:28 UTC (permalink / raw)
  To: Bodo Stroesser, linux-scsi, linux-block, linux-kernel
  Cc: martin.petersen, axboe, bvanassche

On 2020-10-16 7:17 a.m., Bodo Stroesser wrote:
> Hi Douglas,
> 
> AFAICS this patch - and also patch 3 - are not correct.
> When started with SG_MITER_ATOMIC, sg_miter_next and sg_miter_stop use
> the k(un)map_atomic calls. But these have to be used strictly nested
> according to docu and code.
> The below code uses the atomic mappings in overlapping mode.

That being the case, I'll add d_flags and s_flags arguments that are
expected to take either 0 or SG_MITER_ATOMIC and re-test. There probably
should be a warning in the notes not to set both d_flags and s_flags
to SG_MITER_ATOMIC.

My testing to date has not been in irq or soft interrupt state. I
should be able to rig a test for the latter.

Thanks
Doug Gilbert

> Am 16.10.20 um 06:52 schrieb Douglas Gilbert:
>> Both the SCSI and NVMe subsystems receive user data from the block
>> layer in scatterlist_s (aka scatter gather lists (sgl) which are
>> often arrays). If drivers in those subsystems represent storage
>> (e.g. a ramdisk) or cache "hot" user data then they may also
>> choose to use scatterlist_s. Currently there are no sgl to sgl
>> operations in the kernel. Start with a copy.
>>
>> Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
>> ---
>>   include/linux/scatterlist.h |  4 ++
>>   lib/scatterlist.c           | 86 +++++++++++++++++++++++++++++++++++++
>>   2 files changed, 90 insertions(+)
>>
>> diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
>> index 80178afc2a4a..6649414c0749 100644
>> --- a/include/linux/scatterlist.h
>> +++ b/include/linux/scatterlist.h
>> @@ -321,6 +321,10 @@ size_t sg_pcopy_to_buffer(struct scatterlist *sgl, 
>> unsigned int nents,
>>   size_t sg_zero_buffer(struct scatterlist *sgl, unsigned int nents,
>>                  size_t buflen, off_t skip);
>> +size_t sgl_copy_sgl(struct scatterlist *d_sgl, unsigned int d_nents, off_t 
>> d_skip,
>> +            struct scatterlist *s_sgl, unsigned int s_nents, off_t s_skip,
>> +            size_t n_bytes);
>> +
>>   /*
>>    * Maximum number of entries that will be allocated in one piece, if
>>    * a list larger than this is required then chaining will be utilized.
>> diff --git a/lib/scatterlist.c b/lib/scatterlist.c
>> index d5770e7f1030..1ec2c909c8d4 100644
>> --- a/lib/scatterlist.c
>> +++ b/lib/scatterlist.c
>> @@ -974,3 +974,89 @@ size_t sg_zero_buffer(struct scatterlist *sgl, unsigned 
>> int nents,
>>       return offset;
>>   }
>>   EXPORT_SYMBOL(sg_zero_buffer);
>> +
>> +/**
>> + * sgl_copy_sgl - Copy over a destination sgl from a source sgl
>> + * @d_sgl:         Destination sgl
>> + * @d_nents:         Number of SG entries in destination sgl
>> + * @d_skip:         Number of bytes to skip in destination before copying
>> + * @s_sgl:         Source sgl
>> + * @s_nents:         Number of SG entries in source sgl
>> + * @s_skip:         Number of bytes to skip in source before copying
>> + * @n_bytes:         The number of bytes to copy
>> + *
>> + * Returns the number of copied bytes.
>> + *
>> + * Notes:
>> + *   Destination arguments appear before the source arguments, as with memcpy().
>> + *
>> + *   Stops copying if the end of d_sgl or s_sgl is reached.
>> + *
>> + *   Since memcpy() is used, overlapping copies (where d_sgl and s_sgl belong
>> + *   to the same sgl and the copy regions overlap) are not supported.
>> + *
>> + *   If d_skip is large, potentially spanning multiple d_nents then some
>> + *   integer arithmetic to adjust d_sgl may improve performance. For example
>> + *   if d_sgl is built using sgl_alloc_order(chainable=false) then the sgl
>> + *   will be an array with equally sized segments facilitating that
>> + *   arithmetic. The suggestion applies to s_skip, s_sgl and s_nents as well.
>> + *
>> + **/
>> +size_t sgl_copy_sgl(struct scatterlist *d_sgl, unsigned int d_nents, off_t 
>> d_skip,
>> +            struct scatterlist *s_sgl, unsigned int s_nents, off_t s_skip,
>> +            size_t n_bytes)
>> +{
>> +    size_t d_off, s_off, len, d_len, s_len;
>> +    size_t offset = 0;
>> +    struct sg_mapping_iter d_iter;
>> +    struct sg_mapping_iter s_iter;
>> +
>> +    if (n_bytes == 0)
>> +        return 0;
>> +    sg_miter_start(&d_iter, d_sgl, d_nents, SG_MITER_ATOMIC | SG_MITER_TO_SG);
>> +    sg_miter_start(&s_iter, s_sgl, s_nents, SG_MITER_ATOMIC | SG_MITER_FROM_SG);
>> +    if (!sg_miter_skip(&d_iter, d_skip))
>> +        goto fini;
>> +    if (!sg_miter_skip(&s_iter, s_skip))
>> +        goto fini;
>> +
>> +    for (d_off = 0, s_off = 0; true ; ) {
>> +        /* Assume d_iter.length and s_iter.length can never be 0 */
>> +        if (d_off == 0) {
>> +            if (!sg_miter_next(&d_iter))
>> +                break;
>> +            d_len = d_iter.length;
>> +        } else {
>> +            d_len = d_iter.length - d_off;
>> +        }
>> +        if (s_off == 0) {
>> +            if (!sg_miter_next(&s_iter))
>> +                break;
>> +            s_len = s_iter.length;
>> +        } else {
>> +            s_len = s_iter.length - s_off;
>> +        }
>> +        len = min3(d_len, s_len, n_bytes - offset);
>> +
>> +        memcpy(d_iter.addr + d_off, s_iter.addr + s_off, len);
>> +        offset += len;
>> +        if (offset >= n_bytes)
>> +            break;
>> +        if (d_len == s_len) {
>> +            d_off = 0;
>> +            s_off = 0;
>> +        } else if (d_len < s_len) {
>> +            d_off = 0;
>> +            s_off += len;
>> +        } else {
>> +            d_off += len;
>> +            s_off = 0;
>> +        }
>> +    }
>> +fini:
>> +    sg_miter_stop(&d_iter);
>> +    sg_miter_stop(&s_iter);
>> +    return offset;
>> +}
>> +EXPORT_SYMBOL(sgl_copy_sgl);
>> +
>>


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-10-16 15:29 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-16  4:52 [PATCH 0/4] scatterlist: add new capabilities Douglas Gilbert
2020-10-16  4:52 ` [PATCH 1/4] sgl_alloc_order: remove 4 GiB limit, sgl_free() warning Douglas Gilbert
2020-10-16  4:52 ` [PATCH 2/4] scatterlist: add sgl_copy_sgl() function Douglas Gilbert
2020-10-16 11:17   ` Bodo Stroesser
2020-10-16 15:28     ` Douglas Gilbert
2020-10-16  4:52 ` [PATCH 3/4] scatterlist: add sgl_compare_sgl() function Douglas Gilbert
2020-10-16  4:52 ` [PATCH 4/4] scatterlist: add sgl_memset() Douglas Gilbert

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).