diff options
Diffstat (limited to 'arch/arc/kernel/process.c')
| -rw-r--r-- | arch/arc/kernel/process.c | 47 | 
1 files changed, 36 insertions, 11 deletions
| diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c index 5ac3b547453f..4674541eba3f 100644 --- a/arch/arc/kernel/process.c +++ b/arch/arc/kernel/process.c @@ -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 |