|
@@ -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(¤t->mm->mmap_sem);
|
|
|
+ ret = fixup_user_fault(current, current->mm, (unsigned long) uaddr,
|
|
|
+ FAULT_FLAG_WRITE, NULL);
|
|
|
+ up_read(¤t->mm->mmap_sem);
|
|
|
+
|
|
|
+ if (likely(!ret))
|
|
|
+ goto again;
|
|
|
+
|
|
|
+fail:
|
|
|
+ force_sig(SIGSEGV, current);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_ISA_ARCV2
|