3.18-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Chris Metcalf <cmetc...@ezchip.com>

commit 5bf6c07a1843813d0065feaaecba622d49148d7e upstream.

This change enables the generic strncpy_from_user() and strnlen_user()
using word-at-a-time.h.  The tile implementation is trivial since
both tilepro and tilegx have SIMD operations that do byte-wise
comparisons against immediate zero for each byte, and return an
0x01 byte in each position where there is a 0x00 byte.

Signed-off-by: Chris Metcalf <cmetc...@ezchip.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 arch/tile/Kconfig                      |    2 +
 arch/tile/include/asm/uaccess.h        |   66 +++++----------------------------
 arch/tile/include/asm/word-at-a-time.h |   36 ++++++++++++++++++
 arch/tile/lib/exports.c                |    2 -
 arch/tile/lib/usercopy_32.S            |   46 -----------------------
 arch/tile/lib/usercopy_64.S            |   46 -----------------------
 6 files changed, 48 insertions(+), 150 deletions(-)

--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -27,6 +27,8 @@ config TILE
        select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select HAVE_DEBUG_STACKOVERFLOW
        select ARCH_WANT_FRAME_POINTERS
+       select GENERIC_STRNCPY_FROM_USER
+       select GENERIC_STRNLEN_USER
 
 # FIXME: investigate whether we need/want these options.
 #      select HAVE_IOREMAP_PROT
--- a/arch/tile/include/asm/uaccess.h
+++ b/arch/tile/include/asm/uaccess.h
@@ -65,6 +65,13 @@ static inline int is_arch_mappable_range
 #endif
 
 /*
+ * Note that using this definition ignores is_arch_mappable_range(),
+ * so on tilepro code that uses user_addr_max() is constrained not
+ * to reference the tilepro user-interrupt region.
+ */
+#define user_addr_max() (current_thread_info()->addr_limit.seg)
+
+/*
  * Test whether a block of memory is a valid user space address.
  * Returns 0 if the range is valid, nonzero otherwise.
  */
@@ -462,62 +469,9 @@ copy_in_user(void __user *to, const void
 #endif
 
 
-/**
- * strlen_user: - Get the size of a string in user space.
- * @str: The string to measure.
- *
- * Context: User context only.  This function may sleep.
- *
- * Get the size of a NUL-terminated string in user space.
- *
- * Returns the size of the string INCLUDING the terminating NUL.
- * On exception, returns 0.
- *
- * If there is a limit on the length of a valid string, you may wish to
- * consider using strnlen_user() instead.
- */
-extern long strnlen_user_asm(const char __user *str, long n);
-static inline long __must_check strnlen_user(const char __user *str, long n)
-{
-       might_fault();
-       return strnlen_user_asm(str, n);
-}
-#define strlen_user(str) strnlen_user(str, LONG_MAX)
-
-/**
- * strncpy_from_user: - Copy a NUL terminated string from userspace, with less 
checking.
- * @dst:   Destination address, in kernel space.  This buffer must be at
- *         least @count bytes long.
- * @src:   Source address, in user space.
- * @count: Maximum number of bytes to copy, including the trailing NUL.
- *
- * Copies a NUL-terminated string from userspace to kernel space.
- * Caller must check the specified block with access_ok() before calling
- * this function.
- *
- * On success, returns the length of the string (not including the trailing
- * NUL).
- *
- * If access to userspace fails, returns -EFAULT (some data may have been
- * copied).
- *
- * If @count is smaller than the length of the string, copies @count bytes
- * and returns @count.
- */
-extern long strncpy_from_user_asm(char *dst, const char __user *src, long);
-static inline long __must_check __strncpy_from_user(
-       char *dst, const char __user *src, long count)
-{
-       might_fault();
-       return strncpy_from_user_asm(dst, src, count);
-}
-static inline long __must_check strncpy_from_user(
-       char *dst, const char __user *src, long count)
-{
-       if (access_ok(VERIFY_READ, src, 1))
-               return __strncpy_from_user(dst, src, count);
-       return -EFAULT;
-}
+extern long strnlen_user(const char __user *str, long n);
+extern long strlen_user(const char __user *str);
+extern long strncpy_from_user(char *dst, const char __user *src, long);
 
 /**
  * clear_user: - Zero a block of memory in user space.
--- /dev/null
+++ b/arch/tile/include/asm/word-at-a-time.h
@@ -0,0 +1,36 @@
+#ifndef _ASM_WORD_AT_A_TIME_H
+#define _ASM_WORD_AT_A_TIME_H
+
+#include <asm/byteorder.h>
+
+struct word_at_a_time { /* unused */ };
+#define WORD_AT_A_TIME_CONSTANTS {}
+
+/* Generate 0x01 byte values for non-zero bytes using a SIMD instruction. */
+static inline unsigned long has_zero(unsigned long val, unsigned long *data,
+                                    const struct word_at_a_time *c)
+{
+#ifdef __tilegx__
+       unsigned long mask = __insn_v1cmpeqi(val, 0);
+#else /* tilepro */
+       unsigned long mask = __insn_seqib(val, 0);
+#endif
+       *data = mask;
+       return mask;
+}
+
+/* These operations are both nops. */
+#define prep_zero_mask(val, data, c) (data)
+#define create_zero_mask(data) (data)
+
+/* And this operation just depends on endianness. */
+static inline long find_zero(unsigned long mask)
+{
+#ifdef __BIG_ENDIAN
+       return __builtin_clzl(mask) >> 3;
+#else
+       return __builtin_ctzl(mask) >> 3;
+#endif
+}
+
+#endif /* _ASM_WORD_AT_A_TIME_H */
--- a/arch/tile/lib/exports.c
+++ b/arch/tile/lib/exports.c
@@ -18,8 +18,6 @@
 
 /* arch/tile/lib/usercopy.S */
 #include <linux/uaccess.h>
-EXPORT_SYMBOL(strnlen_user_asm);
-EXPORT_SYMBOL(strncpy_from_user_asm);
 EXPORT_SYMBOL(clear_user_asm);
 EXPORT_SYMBOL(flush_user_asm);
 EXPORT_SYMBOL(finv_user_asm);
--- a/arch/tile/lib/usercopy_32.S
+++ b/arch/tile/lib/usercopy_32.S
@@ -20,52 +20,6 @@
 /* Access user memory, but use MMU to avoid propagating kernel exceptions. */
 
 /*
- * strnlen_user_asm takes the pointer in r0, and the length bound in r1.
- * It returns the length, including the terminating NUL, or zero on exception.
- * If length is greater than the bound, returns one plus the bound.
- */
-STD_ENTRY(strnlen_user_asm)
-       { bz r1, 2f; addi r3, r0, -1 }  /* bias down to include NUL */
-1:      { lb_u r4, r0; addi r1, r1, -1 }
-       bz r4, 2f
-       { bnzt r1, 1b; addi r0, r0, 1 }
-2:      { sub r0, r0, r3; jrp lr }
-       STD_ENDPROC(strnlen_user_asm)
-       .pushsection .fixup,"ax"
-strnlen_user_fault:
-       { move r0, zero; jrp lr }
-       ENDPROC(strnlen_user_fault)
-       .section __ex_table,"a"
-       .align 4
-       .word 1b, strnlen_user_fault
-       .popsection
-
-/*
- * strncpy_from_user_asm takes the kernel target pointer in r0,
- * the userspace source pointer in r1, and the length bound (including
- * the trailing NUL) in r2.  On success, it returns the string length
- * (not including the trailing NUL), or -EFAULT on failure.
- */
-STD_ENTRY(strncpy_from_user_asm)
-       { bz r2, 2f; move r3, r0 }
-1:     { lb_u r4, r1; addi r1, r1, 1; addi r2, r2, -1 }
-       { sb r0, r4; addi r0, r0, 1 }
-       bz r4, 2f
-       bnzt r2, 1b
-       { sub r0, r0, r3; jrp lr }
-2:     addi r0, r0, -1   /* don't count the trailing NUL */
-       { sub r0, r0, r3; jrp lr }
-       STD_ENDPROC(strncpy_from_user_asm)
-       .pushsection .fixup,"ax"
-strncpy_from_user_fault:
-       { movei r0, -EFAULT; jrp lr }
-       ENDPROC(strncpy_from_user_fault)
-       .section __ex_table,"a"
-       .align 4
-       .word 1b, strncpy_from_user_fault
-       .popsection
-
-/*
  * clear_user_asm takes the user target address in r0 and the
  * number of bytes to zero in r1.
  * It returns the number of uncopiable bytes (hopefully zero) in r0.
--- a/arch/tile/lib/usercopy_64.S
+++ b/arch/tile/lib/usercopy_64.S
@@ -20,52 +20,6 @@
 /* Access user memory, but use MMU to avoid propagating kernel exceptions. */
 
 /*
- * strnlen_user_asm takes the pointer in r0, and the length bound in r1.
- * It returns the length, including the terminating NUL, or zero on exception.
- * If length is greater than the bound, returns one plus the bound.
- */
-STD_ENTRY(strnlen_user_asm)
-       { beqz r1, 2f; addi r3, r0, -1 }  /* bias down to include NUL */
-1:      { ld1u r4, r0; addi r1, r1, -1 }
-       beqz r4, 2f
-       { bnezt r1, 1b; addi r0, r0, 1 }
-2:      { sub r0, r0, r3; jrp lr }
-       STD_ENDPROC(strnlen_user_asm)
-       .pushsection .fixup,"ax"
-strnlen_user_fault:
-       { move r0, zero; jrp lr }
-       ENDPROC(strnlen_user_fault)
-       .section __ex_table,"a"
-       .align 8
-       .quad 1b, strnlen_user_fault
-       .popsection
-
-/*
- * strncpy_from_user_asm takes the kernel target pointer in r0,
- * the userspace source pointer in r1, and the length bound (including
- * the trailing NUL) in r2.  On success, it returns the string length
- * (not including the trailing NUL), or -EFAULT on failure.
- */
-STD_ENTRY(strncpy_from_user_asm)
-       { beqz r2, 2f; move r3, r0 }
-1:     { ld1u r4, r1; addi r1, r1, 1; addi r2, r2, -1 }
-       { st1 r0, r4; addi r0, r0, 1 }
-       beqz r4, 2f
-       bnezt r2, 1b
-       { sub r0, r0, r3; jrp lr }
-2:     addi r0, r0, -1   /* don't count the trailing NUL */
-       { sub r0, r0, r3; jrp lr }
-       STD_ENDPROC(strncpy_from_user_asm)
-       .pushsection .fixup,"ax"
-strncpy_from_user_fault:
-       { movei r0, -EFAULT; jrp lr }
-       ENDPROC(strncpy_from_user_fault)
-       .section __ex_table,"a"
-       .align 8
-       .quad 1b, strncpy_from_user_fault
-       .popsection
-
-/*
  * clear_user_asm takes the user target address in r0 and the
  * number of bytes to zero in r1.
  * It returns the number of uncopiable bytes (hopefully zero) in r0.


Reply via email to