Merge tag 'nvme-for-4.18' of git://git.infradead.org/nvme
[sfrench/cifs-2.6.git] / arch / arc / kernel / process.c
index 5ac3b547453fd5b4b5393fdc10dfff34a047941f..4674541eba3fd019a51aeb02db27b2bc04569412 100644 (file)
@@ -47,7 +47,8 @@ SYSCALL_DEFINE0(arc_gettls)
 SYSCALL_DEFINE3(arc_usr_cmpxchg, int *, uaddr, int, expected, int, new)
 {
        struct pt_regs *regs = current_pt_regs();
-       int uval = -EFAULT;
+       u32 uval;
+       int ret;
 
        /*
         * This is only for old cores lacking LLOCK/SCOND, which by defintion
@@ -60,23 +61,47 @@ SYSCALL_DEFINE3(arc_usr_cmpxchg, int *, uaddr, int, expected, int, new)
        /* Z indicates to userspace if operation succeded */
        regs->status32 &= ~STATUS_Z_MASK;
 
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
-               return -EFAULT;
+       ret = access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr));
+       if (!ret)
+                goto fail;
 
+again:
        preempt_disable();
 
-       if (__get_user(uval, uaddr))
-               goto done;
+       ret = __get_user(uval, uaddr);
+       if (ret)
+                goto fault;
 
-       if (uval == expected) {
-               if (!__put_user(new, uaddr))
-                       regs->status32 |= STATUS_Z_MASK;
-       }
+       if (uval != expected)
+                goto out;
 
-done:
-       preempt_enable();
+       ret = __put_user(new, uaddr);
+       if (ret)
+                goto fault;
+
+       regs->status32 |= STATUS_Z_MASK;
 
+out:
+       preempt_enable();
        return uval;
+
+fault:
+       preempt_enable();
+
+       if (unlikely(ret != -EFAULT))
+                goto fail;
+
+       down_read(&current->mm->mmap_sem);
+       ret = fixup_user_fault(current, current->mm, (unsigned long) uaddr,
+                              FAULT_FLAG_WRITE, NULL);
+       up_read(&current->mm->mmap_sem);
+
+       if (likely(!ret))
+                goto again;
+
+fail:
+       force_sig(SIGSEGV, current);
+       return ret;
 }
 
 #ifdef CONFIG_ISA_ARCV2