ARC: change return value of userspace cmpxchg assist syscall
authorVineet Gupta <vgupta@synopsys.com>
Mon, 7 Nov 2016 18:36:46 +0000 (10:36 -0800)
committerVineet Gupta <vgupta@synopsys.com>
Mon, 7 Nov 2016 19:01:12 +0000 (11:01 -0800)
The original syscall only used to return errno to indicate if cmpxchg
succeeded. It was not returning the "previous" value which typical cmpxchg
callers are interested in to build their slowpaths or retry loops.
Given user preemption in syscall return path etc, it is not wise to
check this in userspace afterwards, but should be what kernel actually
observed in the syscall.

So change the syscall interface to always return the previous value and
additionally set Z flag to indicate whether operation succeeded or not
(just like ARM implementation when they used to have this syscall)
The flag approach avoids having to put_user errno which is nice given
the use case for this syscall cares mostly about the "previous" value.

Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
arch/arc/include/asm/arcregs.h
arch/arc/kernel/process.c

index 7f3f9f63708cf4c3b0e1d77f9b1ac17e3505c4ba..1bd24ec3e350243de4abd0a45cee201027ccf52a 100644 (file)
 #define STATUS_AE_BIT          5       /* Exception active */
 #define STATUS_DE_BIT          6       /* PC is in delay slot */
 #define STATUS_U_BIT           7       /* User/Kernel mode */
+#define STATUS_Z_BIT            11
 #define STATUS_L_BIT           12      /* Loop inhibit */
 
 /* These masks correspond to the status word(STATUS_32) bits */
 #define STATUS_AE_MASK         (1<<STATUS_AE_BIT)
 #define STATUS_DE_MASK         (1<<STATUS_DE_BIT)
 #define STATUS_U_MASK          (1<<STATUS_U_BIT)
+#define STATUS_Z_MASK          (1<<STATUS_Z_BIT)
 #define STATUS_L_MASK          (1<<STATUS_L_BIT)
 
 /*
index 59aa43cb146e732e01544790913f0b018a923cc6..a41a79a4f4feaca96306577077bd4745d6cd8537 100644 (file)
@@ -43,8 +43,8 @@ SYSCALL_DEFINE0(arc_gettls)
 
 SYSCALL_DEFINE3(arc_usr_cmpxchg, int *, uaddr, int, expected, int, new)
 {
-       int uval;
-       int ret;
+       struct pt_regs *regs = current_pt_regs();
+       int uval = -EFAULT;
 
        /*
         * This is only for old cores lacking LLOCK/SCOND, which by defintion
@@ -54,24 +54,26 @@ SYSCALL_DEFINE3(arc_usr_cmpxchg, int *, uaddr, int, expected, int, new)
         */
        WARN_ON_ONCE(IS_ENABLED(CONFIG_SMP));
 
+       /* Z indicates to userspace if operation succeded */
+       regs->status32 &= ~STATUS_Z_MASK;
+
        if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
                return -EFAULT;
 
        preempt_disable();
 
-       ret = __get_user(uval, uaddr);
-       if (ret)
+       if (__get_user(uval, uaddr))
                goto done;
 
-       if (uval != expected)
-               ret = -EAGAIN;
-       else
-               ret = __put_user(new, uaddr);
+       if (uval == expected) {
+               if (!__put_user(new, uaddr))
+                       regs->status32 |= STATUS_Z_MASK;
+       }
 
 done:
        preempt_enable();
 
-       return ret;
+       return uval;
 }
 
 void arch_cpu_idle(void)