unsafe_put_user() is designed to take benefit of 'asm goto'. Instead of using the standard __put_user() approach and branch based on the returned error, use 'asm goto' to branch directly to the error label from the .fixup code.
This change significantly simplifies functions using unsafe_put_user() Small exemple of the benefit with the following code: struct test { unsigned long item1; unsigned long item2; unsigned long item3; }; int set_test_to_user(struct test __user *test, unsigned long item1, unsigned long item2, unsigned long item3) { unsafe_put_user(item1, &test->item1, failed); unsafe_put_user(item2, &test->item2, failed); unsafe_put_user(item3, &test->item3, failed); return 0; failed: return -EFAULT; } Before the patch: 00000d94 <set_test_to_user>: d94: 39 20 00 00 li r9,0 d98: 90 83 00 00 stw r4,0(r3) d9c: 2f 89 00 00 cmpwi cr7,r9,0 da0: 40 9e 00 30 bne cr7,dd0 <set_test_to_user+0x3c> da4: 39 43 00 04 addi r10,r3,4 da8: 90 aa 00 00 stw r5,0(r10) dac: 2f 89 00 00 cmpwi cr7,r9,0 db0: 40 9e 00 20 bne cr7,dd0 <set_test_to_user+0x3c> db4: 38 63 00 08 addi r3,r3,8 db8: 90 c3 00 00 stw r6,0(r3) dbc: 21 29 00 00 subfic r9,r9,0 dc0: 7d 29 49 10 subfe r9,r9,r9 dc4: 38 60 ff f2 li r3,-14 dc8: 7d 23 18 38 and r3,r9,r3 dcc: 4e 80 00 20 blr dd0: 38 60 ff f2 li r3,-14 dd4: 4e 80 00 20 blr 00000000 <.fixup>: ... b8: 39 20 ff f2 li r9,-14 bc: 48 00 00 00 b bc <.fixup+0xbc> bc: R_PPC_REL24 .text+0xd9c c0: 39 20 ff f2 li r9,-14 c4: 48 00 00 00 b c4 <.fixup+0xc4> c4: R_PPC_REL24 .text+0xdac c8: 39 20 ff f2 li r9,-14 cc: 48 00 00 00 b cc <.fixup+0xcc> cc: R_PPC_REL24 .text+0xdbc After the patch: 00000d94 <set_test_to_user>: d94: 90 83 00 00 stw r4,0(r3) d98: 39 23 00 04 addi r9,r3,4 d9c: 90 a9 00 00 stw r5,0(r9) da0: 38 63 00 08 addi r3,r3,8 da4: 90 c3 00 00 stw r6,0(r3) da8: 38 60 00 00 li r3,0 dac: 4e 80 00 20 blr db0: 38 60 ff f2 li r3,-14 db4: 4e 80 00 20 blr 00000000 <.fixup>: ... b8: 48 00 00 00 b b8 <.fixup+0xb8> b8: R_PPC_REL24 .text+0xdb0 bc: 48 00 00 00 b bc <.fixup+0xbc> bc: R_PPC_REL24 .text+0xdb0 c0: 48 00 00 00 b c0 <.fixup+0xc0> c0: R_PPC_REL24 .text+0xdb0 Signed-off-by: Christophe Leroy <christophe.le...@c-s.fr> --- arch/powerpc/include/asm/uaccess.h | 63 +++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index 2f500debae21..b904f3c56463 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -93,12 +93,10 @@ static inline int __access_ok(unsigned long addr, unsigned long size, #define __get_user(x, ptr) \ __get_user_nocheck((x), (ptr), sizeof(*(ptr)), true) #define __put_user(x, ptr) \ - __put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)), true) + __put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr))) #define __get_user_allowed(x, ptr) \ __get_user_nocheck((x), (ptr), sizeof(*(ptr)), false) -#define __put_user_allowed(x, ptr) \ - __put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)), false) #define __get_user_inatomic(x, ptr) \ __get_user_nosleep((x), (ptr), sizeof(*(ptr))) @@ -124,9 +122,24 @@ extern long __put_user_bad(void); : "=r" (err) \ : "r" (x), "b" (addr), "i" (-EFAULT), "0" (err)) +#define __put_user_asm_goto(x, addr, label, op) \ + asm volatile goto( \ + "1: " op " %0,0(%1) # put_user\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: b %l2\n" \ + ".previous\n" \ + EX_TABLE(1b, 3b) \ + : \ + : "r" (x), "b" (addr) \ + : \ + : label) + #ifdef __powerpc64__ #define __put_user_asm2(x, ptr, retval) \ __put_user_asm(x, ptr, retval, "std") +#define __put_user_asm2_goto(x, ptr, label) \ + __put_user_asm_goto(x, ptr, label, "std") #else /* __powerpc64__ */ #define __put_user_asm2(x, addr, err) \ __asm__ __volatile__( \ @@ -141,6 +154,20 @@ extern long __put_user_bad(void); EX_TABLE(2b, 4b) \ : "=r" (err) \ : "r" (x), "b" (addr), "i" (-EFAULT), "0" (err)) +#define __put_user_asm2_goto(x, addr, label) \ + asm volatile goto( \ + "1: stw %0,0(%1)\n" \ + "2: stw %L0,4(%1)\n" \ + "3:\n" \ + ".section .fixup,\"ax\"\n" \ + "4: b %l2\n" \ + ".previous\n" \ + EX_TABLE(1b, 4b) \ + EX_TABLE(2b, 4b) \ + : \ + : "r" (x), "b" (addr) \ + : \ + : label) #endif /* __powerpc64__ */ #define __put_user_size_allowed(x, ptr, size, retval) \ @@ -155,6 +182,17 @@ do { \ } \ } while (0) +#define __put_user_size_goto(x, ptr, size, label) \ +do { \ + switch (size) { \ + case 1: __put_user_asm_goto(x, ptr, label, "stb"); break; \ + case 2: __put_user_asm_goto(x, ptr, label, "sth"); break; \ + case 4: __put_user_asm_goto(x, ptr, label, "stw"); break; \ + case 8: __put_user_asm2_goto(x, ptr, label); break; \ + default: __put_user_bad(); \ + } \ +} while (0) + #define __put_user_size(x, ptr, size, retval) \ do { \ allow_write_to_user(ptr, size); \ @@ -162,20 +200,26 @@ do { \ prevent_write_to_user(ptr, size); \ } while (0) -#define __put_user_nocheck(x, ptr, size, do_allow) \ +#define __put_user_nocheck(x, ptr, size) \ ({ \ long __pu_err; \ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ if (!is_kernel_addr((unsigned long)__pu_addr)) \ might_fault(); \ __chk_user_ptr(ptr); \ - if (do_allow) \ - __put_user_size((x), __pu_addr, (size), __pu_err); \ - else \ - __put_user_size_allowed((x), __pu_addr, (size), __pu_err); \ + __put_user_size((x), __pu_addr, (size), __pu_err); \ __pu_err; \ }) +#define __put_user_goto(x, ptr, size, label) \ +do { \ + __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ + if (!is_kernel_addr((unsigned long)__pu_addr)) \ + might_fault(); \ + __chk_user_ptr(ptr); \ + __put_user_size_goto((x), __pu_addr, (size), label); \ +} while (0) + #define __put_user_check(x, ptr, size) \ ({ \ long __pu_err = -EFAULT; \ @@ -470,7 +514,8 @@ static __must_check inline bool user_access_begin(const void __user *ptr, size_t #define unsafe_op_wrap(op, err) do { if (unlikely(op)) goto err; } while (0) #define unsafe_get_user(x, p, e) unsafe_op_wrap(__get_user_allowed(x, p), e) -#define unsafe_put_user(x, p, e) unsafe_op_wrap(__put_user_allowed(x, p), e) +#define unsafe_put_user(x, p, e) \ + __put_user_goto((__typeof__(*(p)))(x), (p), sizeof(*(p)), e) #define unsafe_copy_to_user(d, s, l, e) \ unsafe_op_wrap(raw_copy_to_user_allowed(d, s, l), e) -- 2.25.0