Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1608619 > unrolled thread
| Started by | Matthew Wilcox <willy@infradead.org> |
|---|---|
| First post | 2017-03-24 17:20 +0100 |
| Last post | 2017-03-24 17:20 +0100 |
| Articles | 6 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH v3 0/7] Add memsetN functions Matthew Wilcox <willy@infradead.org> - 2017-03-24 17:20 +0100
[PATCH v3 1/7] Add multibyte memset functions Matthew Wilcox <willy@infradead.org> - 2017-03-24 17:20 +0100
[PATCH v3 5/7] zram: Convert to using memset_l Matthew Wilcox <willy@infradead.org> - 2017-03-24 17:20 +0100
Re: [PATCH v3 5/7] zram: Convert to using memset_l Minchan Kim <minchan@kernel.org> - 2017-03-27 07:10 +0200
[PATCH v3 4/7] alpha: Add support for memset16 Matthew Wilcox <willy@infradead.org> - 2017-03-24 17:20 +0100
[PATCH v3 2/7] ARM: Implement memset16, memset32 & memset64 Matthew Wilcox <willy@infradead.org> - 2017-03-24 17:20 +0100
| From | Matthew Wilcox <willy@infradead.org> |
|---|---|
| Date | 2017-03-24 17:20 +0100 |
| Subject | [PATCH v3 0/7] Add memsetN functions |
| Message-ID | <toA6t-48t-3@gated-at.bofh.it> |
From: Matthew Wilcox <mawilcox@microsoft.com> zram was recently enhanced to support compressing pages with a repeating pattern up to the size of an unsigned long. As part of the discussion, we noted it would be nice if architectures had optimised routines to fill regions of memory with patterns larger than those contained in a single byte. Our suspicions were right; the x86 version offers approximately a 7% performance improvement over the C implementation. The generic memfill() function is part of Lars Wirzenius' publib, but it doesn't offer the most convenient interface. I chose to add five more-specific functions as part of this patchset -- memset16(), memset32(), memset64(), memset_l() (long) and memset_p() (pointer). It would be nice to have some more architectures implement optimised memsetN calls. It would also be nice to find more places in the kernel which could benefit from calling these functions. Maybe a coccinelle script could be written to find such places? We're looking for loops over an array where the value being stored into the array does not depend on the iteration variable. Since v1 of the patchset, I stumbled on Alpha's memsetw() which caused me to add memset16() to complete the set. I removed the '__HAVE_ARCH_MEMSET_PLUS' preprocessor symbol in favour of separate MEMSET16 MEMSET32 and MEMSET64 symbols. I also reviewed the scr_mem*w() usages across the different architectures and implemented some obvious missing optimisations. Alpha is still missing scr_memmovew() as it would be non-trivial to write. Russell's review on patch 2 only applies to the memset32/memset64 implementation. The memset16 is unreviewed (and, indeed, untested) to date. Matthew Wilcox (7): Add multibyte memset functions ARM: Implement memset16, memset32 & memset64 x86: Implement memset16, memset32 & memset64 alpha: Add support for memset16 zram: Convert to using memset_l sym53c8xx_2: Convert to use memset32 vga: Optimise console scrolling arch/alpha/include/asm/string.h | 15 ++++---- arch/alpha/include/asm/vga.h | 2 +- arch/alpha/lib/memset.S | 10 +++--- arch/arm/include/asm/string.h | 21 ++++++++++++ arch/arm/kernel/armksyms.c | 3 ++ arch/arm/lib/memset.S | 44 +++++++++++++++++++----- arch/mips/include/asm/vga.h | 6 ++++ arch/powerpc/include/asm/vga.h | 8 +++++ arch/sparc/include/asm/vga.h | 24 +++++++++++++ arch/x86/include/asm/string_32.h | 24 +++++++++++++ arch/x86/include/asm/string_64.h | 36 ++++++++++++++++++++ drivers/block/zram/zram_drv.c | 15 ++------ drivers/scsi/sym53c8xx_2/sym_hipd.c | 11 ++---- include/linux/string.h | 30 ++++++++++++++++ include/linux/vt_buffer.h | 12 +++++++ lib/string.c | 68 +++++++++++++++++++++++++++++++++++++ 16 files changed, 287 insertions(+), 42 deletions(-) -- 2.11.0
[toc] | [next] | [standalone]
| From | Matthew Wilcox <willy@infradead.org> |
|---|---|
| Date | 2017-03-24 17:20 +0100 |
| Subject | [PATCH v3 1/7] Add multibyte memset functions |
| Message-ID | <toA6u-48t-21@gated-at.bofh.it> |
| In reply to | #1608619 |
From: Matthew Wilcox <mawilcox@microsoft.com>
memset16(), memset32() and memset64() are like memset(), but allow the
caller to fill the destination with a multibyte pattern. memset_l()
and memset_p() allow the caller to use unsigned long and pointer
values respectively. memset64() is currently only available on 64-bit
architectures.
Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
---
include/linux/string.h | 30 ++++++++++++++++++++++
lib/string.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 98 insertions(+)
diff --git a/include/linux/string.h b/include/linux/string.h
index 26b6f6a66f83..b376875b650c 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -99,6 +99,36 @@ extern __kernel_size_t strcspn(const char *,const char *);
#ifndef __HAVE_ARCH_MEMSET
extern void * memset(void *,int,__kernel_size_t);
#endif
+
+#ifndef __HAVE_ARCH_MEMSET16
+extern void *memset16(uint16_t *, uint16_t, __kernel_size_t);
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET32
+extern void *memset32(uint32_t *, uint32_t, __kernel_size_t);
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET64
+extern void *memset64(uint64_t *, uint64_t, __kernel_size_t);
+#endif
+
+static inline void *memset_l(unsigned long *p, unsigned long v,
+ __kernel_size_t n)
+{
+ if (BITS_PER_LONG == 32)
+ return memset32((uint32_t *)p, v, n);
+ else
+ return memset64((uint64_t *)p, v, n);
+}
+
+static inline void *memset_p(void **p, void *v, __kernel_size_t n)
+{
+ if (BITS_PER_LONG == 32)
+ return memset32((uint32_t *)p, (uintptr_t)v, n);
+ else
+ return memset64((uint64_t *)p, (uintptr_t)v, n);
+}
+
#ifndef __HAVE_ARCH_MEMCPY
extern void * memcpy(void *,const void *,__kernel_size_t);
#endif
diff --git a/lib/string.c b/lib/string.c
index ed83562a53ae..f18ba402e503 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -697,6 +697,74 @@ void memzero_explicit(void *s, size_t count)
}
EXPORT_SYMBOL(memzero_explicit);
+#ifndef __HAVE_ARCH_MEMSET16
+/**
+ * memset16() - Fill a memory area with a uint16_t
+ * @s: Pointer to the start of the area.
+ * @v: The value to fill the area with
+ * @count: The number of values to store
+ *
+ * Differs from memset() in that it fills with a uint16_t instead
+ * of a byte. Remember that @count is the number of uint16_ts to
+ * store, not the number of bytes.
+ */
+void *memset16(uint16_t *s, uint16_t v, size_t count)
+{
+ uint16_t *xs = s;
+
+ while (count--)
+ *xs++ = v;
+ return s;
+}
+EXPORT_SYMBOL(memset16);
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET32
+/**
+ * memset32() - Fill a memory area with a uint32_t
+ * @s: Pointer to the start of the area.
+ * @v: The value to fill the area with
+ * @count: The number of values to store
+ *
+ * Differs from memset() in that it fills with a uint32_t instead
+ * of a byte. Remember that @count is the number of uint32_ts to
+ * store, not the number of bytes.
+ */
+void *memset32(uint32_t *s, uint32_t v, size_t count)
+{
+ uint32_t *xs = s;
+
+ while (count--)
+ *xs++ = v;
+ return s;
+}
+EXPORT_SYMBOL(memset32);
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET64
+#if BITS_PER_LONG > 32
+/**
+ * memset64() - Fill a memory area with a uint64_t
+ * @s: Pointer to the start of the area.
+ * @v: The value to fill the area with
+ * @count: The number of values to store
+ *
+ * Differs from memset() in that it fills with a uint64_t instead
+ * of a byte. Remember that @count is the number of uint64_ts to
+ * store, not the number of bytes.
+ */
+void *memset64(uint64_t *s, uint64_t v, size_t count)
+{
+ uint64_t *xs = s;
+
+ while (count--)
+ *xs++ = v;
+ return s;
+}
+EXPORT_SYMBOL(memset64);
+#endif
+#endif
+
#ifndef __HAVE_ARCH_MEMCPY
/**
* memcpy - Copy one area of memory to another
--
2.11.0
[toc] | [prev] | [next] | [standalone]
| From | Matthew Wilcox <willy@infradead.org> |
|---|---|
| Date | 2017-03-24 17:20 +0100 |
| Subject | [PATCH v3 5/7] zram: Convert to using memset_l |
| Message-ID | <toA6u-48t-27@gated-at.bofh.it> |
| In reply to | #1608619 |
From: Matthew Wilcox <mawilcox@microsoft.com>
zram was the motivation for creating memset_l(). Minchan Kim sees a 7%
performance improvement on x86 with 100MB of non-zero deduplicatable
data:
perf stat -r 10 dd if=/dev/zram0 of=/dev/null
vanilla: 0.232050465 seconds time elapsed ( +- 0.51% )
memset_l: 0.217219387 seconds time elapsed ( +- 0.07% )
Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
Tested-by: Minchan Kim <minchan@kernel.org>
---
drivers/block/zram/zram_drv.c | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index e27d89a36c34..25dcad309695 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -157,20 +157,11 @@ static inline void update_used_max(struct zram *zram,
} while (old_max != cur_max);
}
-static inline void zram_fill_page(char *ptr, unsigned long len,
+static inline void zram_fill_page(void *ptr, unsigned long len,
unsigned long value)
{
- int i;
- unsigned long *page = (unsigned long *)ptr;
-
WARN_ON_ONCE(!IS_ALIGNED(len, sizeof(unsigned long)));
-
- if (likely(value == 0)) {
- memset(ptr, 0, len);
- } else {
- for (i = 0; i < len / sizeof(*page); i++)
- page[i] = value;
- }
+ memset_l(ptr, value, len / sizeof(unsigned long));
}
static bool page_same_filled(void *ptr, unsigned long *element)
@@ -193,7 +184,7 @@ static bool page_same_filled(void *ptr, unsigned long *element)
static void handle_same_page(struct bio_vec *bvec, unsigned long element)
{
struct page *page = bvec->bv_page;
- void *user_mem;
+ char *user_mem;
user_mem = kmap_atomic(page);
zram_fill_page(user_mem + bvec->bv_offset, bvec->bv_len, element);
--
2.11.0
[toc] | [prev] | [next] | [standalone]
| From | Minchan Kim <minchan@kernel.org> |
|---|---|
| Date | 2017-03-27 07:10 +0200 |
| Subject | Re: [PATCH v3 5/7] zram: Convert to using memset_l |
| Message-ID | <tpv4J-2RJ-3@gated-at.bofh.it> |
| In reply to | #1608624 |
On Fri, Mar 24, 2017 at 09:13:16AM -0700, Matthew Wilcox wrote: > From: Matthew Wilcox <mawilcox@microsoft.com> > > zram was the motivation for creating memset_l(). Minchan Kim sees a 7% > performance improvement on x86 with 100MB of non-zero deduplicatable > data: > > perf stat -r 10 dd if=/dev/zram0 of=/dev/null > > vanilla: 0.232050465 seconds time elapsed ( +- 0.51% ) > memset_l: 0.217219387 seconds time elapsed ( +- 0.07% ) > > Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com> > Tested-by: Minchan Kim <minchan@kernel.org> Acked-by: Minchan Kim <minchan@kernel.org> Thanks!
[toc] | [prev] | [next] | [standalone]
| From | Matthew Wilcox <willy@infradead.org> |
|---|---|
| Date | 2017-03-24 17:20 +0100 |
| Subject | [PATCH v3 4/7] alpha: Add support for memset16 |
| Message-ID | <toA6u-48t-29@gated-at.bofh.it> |
| In reply to | #1608619 |
From: Matthew Wilcox <mawilcox@microsoft.com>
Alpha already had an optimised memset-16-bit-quantity assembler routine
called memsetw(). It has a slightly different calling convention
from memset16() in that it takes a byte count, not a count of words.
That's the same convention used by ARM's __memset16(), so rename Alpha's
routine to match and add a memset16() wrapper around it. Then convert
Alpha's scr_memsetw() to call memset16() instead of memsetw().
Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
---
arch/alpha/include/asm/string.h | 15 ++++++++-------
arch/alpha/include/asm/vga.h | 2 +-
arch/alpha/lib/memset.S | 10 +++++-----
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/arch/alpha/include/asm/string.h b/arch/alpha/include/asm/string.h
index c2911f591704..74c0a693b76b 100644
--- a/arch/alpha/include/asm/string.h
+++ b/arch/alpha/include/asm/string.h
@@ -65,13 +65,14 @@ extern void * memchr(const void *, int, size_t);
aligned values. The DEST and COUNT parameters must be even for
correct operation. */
-#define __HAVE_ARCH_MEMSETW
-extern void * __memsetw(void *dest, unsigned short, size_t count);
-
-#define memsetw(s, c, n) \
-(__builtin_constant_p(c) \
- ? __constant_c_memset((s),0x0001000100010001UL*(unsigned short)(c),(n)) \
- : __memsetw((s),(c),(n)))
+#define __HAVE_ARCH_MEMSET16
+extern void * __memset16(void *dest, unsigned short, size_t count);
+static inline void *memset16(uint16_t *p, uint16_t v, size_t n)
+{
+ if (__builtin_constant_p(v))
+ return __constant_c_memset(p, 0x0001000100010001UL * v, n * 2)
+ return __memset16(p, v, n * 2);
+}
#endif /* __KERNEL__ */
diff --git a/arch/alpha/include/asm/vga.h b/arch/alpha/include/asm/vga.h
index c00106bac521..3c1c2b6128e7 100644
--- a/arch/alpha/include/asm/vga.h
+++ b/arch/alpha/include/asm/vga.h
@@ -34,7 +34,7 @@ static inline void scr_memsetw(u16 *s, u16 c, unsigned int count)
if (__is_ioaddr(s))
memsetw_io((u16 __iomem *) s, c, count);
else
- memsetw(s, c, count);
+ memset16(s, c, count / 2);
}
/* Do not trust that the usage will be correct; analyze the arguments. */
diff --git a/arch/alpha/lib/memset.S b/arch/alpha/lib/memset.S
index 89a26f5e89de..f824969e9e77 100644
--- a/arch/alpha/lib/memset.S
+++ b/arch/alpha/lib/memset.S
@@ -20,7 +20,7 @@
.globl memset
.globl __memset
.globl ___memset
- .globl __memsetw
+ .globl __memset16
.globl __constant_c_memset
.ent ___memset
@@ -110,8 +110,8 @@ EXPORT_SYMBOL(___memset)
EXPORT_SYMBOL(__constant_c_memset)
.align 5
- .ent __memsetw
-__memsetw:
+ .ent __memset16
+__memset16:
.prologue 0
inswl $17,0,$1 /* E0 */
@@ -123,8 +123,8 @@ __memsetw:
or $1,$4,$17 /* E0 */
br __constant_c_memset /* .. E1 */
- .end __memsetw
-EXPORT_SYMBOL(__memsetw)
+ .end __memset16
+EXPORT_SYMBOL(__memset16)
memset = ___memset
__memset = ___memset
--
2.11.0
[toc] | [prev] | [next] | [standalone]
| From | Matthew Wilcox <willy@infradead.org> |
|---|---|
| Date | 2017-03-24 17:20 +0100 |
| Subject | [PATCH v3 2/7] ARM: Implement memset16, memset32 & memset64 |
| Message-ID | <toA6u-48t-33@gated-at.bofh.it> |
| In reply to | #1608619 |
From: Matthew Wilcox <mawilcox@microsoft.com>
ARM is only 32-bit, so it doesn't really need a memset64, but it was
essentially free to add it to the existing implementation.
Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
Reviewed-by: Russell King <rmk+kernel@armlinux.org.uk>
---
arch/arm/include/asm/string.h | 21 +++++++++++++++++++++
arch/arm/kernel/armksyms.c | 3 +++
arch/arm/lib/memset.S | 44 ++++++++++++++++++++++++++++++++++---------
3 files changed, 59 insertions(+), 9 deletions(-)
diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h
index cf4f3aad0fc1..bc7a1be7a76a 100644
--- a/arch/arm/include/asm/string.h
+++ b/arch/arm/include/asm/string.h
@@ -24,6 +24,27 @@ extern void * memchr(const void *, int, __kernel_size_t);
#define __HAVE_ARCH_MEMSET
extern void * memset(void *, int, __kernel_size_t);
+#define __HAVE_ARCH_MEMSET16
+extern void *__memset16(uint16_t *, uint16_t v, __kernel_size_t);
+static inline void *memset16(uint16_t *p, uint16_t v, __kernel_size_t n)
+{
+ return __memset16(p, v, n * 2);
+}
+
+#define __HAVE_ARCH_MEMSET32
+extern void *__memset32(uint32_t *, uint32_t v, __kernel_size_t);
+static inline void *memset32(uint32_t *p, uint32_t v, __kernel_size_t n)
+{
+ return __memset32(p, v, n * 4);
+}
+
+#define __HAVE_ARCH_MEMSET64
+extern void *__memset64(uint64_t *, uint32_t low, __kernel_size_t, uint32_t hi);
+static inline void *memset64(uint64_t *p, uint64_t v, __kernel_size_t n)
+{
+ return __memset64(p, v, n * 8, v >> 32);
+}
+
extern void __memzero(void *ptr, __kernel_size_t n);
#define memset(p,v,n) \
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 8e8d20cdbce7..633341ed0713 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -87,6 +87,9 @@ EXPORT_SYMBOL(__raw_writesl);
EXPORT_SYMBOL(strchr);
EXPORT_SYMBOL(strrchr);
EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(__memset16);
+EXPORT_SYMBOL(__memset32);
+EXPORT_SYMBOL(__memset64);
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(memchr);
diff --git a/arch/arm/lib/memset.S b/arch/arm/lib/memset.S
index 3c65e3bd790f..9adc9bdf3ffb 100644
--- a/arch/arm/lib/memset.S
+++ b/arch/arm/lib/memset.S
@@ -21,14 +21,14 @@ ENTRY(memset)
UNWIND( .fnstart )
ands r3, r0, #3 @ 1 unaligned?
mov ip, r0 @ preserve r0 as return value
+ orr r1, r1, r1, lsl #8
bne 6f @ 1
/*
* we know that the pointer in ip is aligned to a word boundary.
*/
-1: orr r1, r1, r1, lsl #8
- orr r1, r1, r1, lsl #16
+1: orr r1, r1, r1, lsl #16
mov r3, r1
- cmp r2, #16
+7: cmp r2, #16
blt 4f
#if ! CALGN(1)+0
@@ -41,7 +41,7 @@ UNWIND( .fnend )
UNWIND( .fnstart )
UNWIND( .save {r8, lr} )
mov r8, r1
- mov lr, r1
+ mov lr, r3
2: subs r2, r2, #64
stmgeia ip!, {r1, r3, r8, lr} @ 64 bytes at a time.
@@ -73,11 +73,11 @@ UNWIND( .fnend )
UNWIND( .fnstart )
UNWIND( .save {r4-r8, lr} )
mov r4, r1
- mov r5, r1
+ mov r5, r3
mov r6, r1
- mov r7, r1
+ mov r7, r3
mov r8, r1
- mov lr, r1
+ mov lr, r3
cmp r2, #96
tstgt ip, #31
@@ -114,12 +114,13 @@ UNWIND( .fnstart )
tst r2, #4
strne r1, [ip], #4
/*
- * When we get here, we've got less than 4 bytes to zero. We
+ * When we get here, we've got less than 4 bytes to set. We
* may have an unaligned pointer as well.
*/
5: tst r2, #2
+ movne r3, r1, lsr #8 @ the top half of a 16-bit pattern
strneb r1, [ip], #1
- strneb r1, [ip], #1
+ strneb r3, [ip], #1
tst r2, #1
strneb r1, [ip], #1
ret lr
@@ -135,3 +136,28 @@ UNWIND( .fnstart )
UNWIND( .fnend )
ENDPROC(memset)
ENDPROC(mmioset)
+
+ENTRY(__memset16)
+UNWIND( .fnstart )
+ tst r0, #2 @ pointer unaligned?
+ mov ip, r0 @ preserve r0 as return value
+ beq 1b @ jump into the middle of memset
+ subs r2, r2, #2 @ cope with n == 0
+ movge r3, r1, lsr #8 @ r3 = r1 >> 8
+ strgeb r1, [ip], #1 @ *ip = r1
+ strgeb r3, [ip], #1 @ *ip = r3
+ bgt 1b @ back into memset if n > 0
+ ret lr @ otherwise return
+UNWIND( .fnend )
+ENDPROC(__memset16)
+ENTRY(__memset32)
+UNWIND( .fnstart )
+ mov r3, r1 @ copy r1 to r3 and fall into memset64
+UNWIND( .fnend )
+ENDPROC(__memset32)
+ENTRY(__memset64)
+UNWIND( .fnstart )
+ mov ip, r0 @ preserve r0 as return value
+ b 7b @ jump into the middle of memset
+UNWIND( .fnend )
+ENDPROC(__memset64)
--
2.11.0
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web