Skip to content

Commit

Permalink
asm-generic: add generic futex for !CONFIG_SMP
Browse files Browse the repository at this point in the history
Follow m68k futex implementation for !CONFIG_SMP.

Signed-off-by: Ley Foon Tan <lftan@altera.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
  • Loading branch information
Ley Foon Tan committed Dec 8, 2014
1 parent b2776bf commit 00f634b
Showing 1 changed file with 114 additions and 0 deletions.
114 changes: 114 additions & 0 deletions include/asm-generic/futex.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,119 @@
#include <linux/uaccess.h>
#include <asm/errno.h>

#ifndef CONFIG_SMP
/*
* The following implementation only for uniprocessor machines.
* For UP, it's relies on the fact that pagefault_disable() also disables
* preemption to ensure mutual exclusion.
*
*/

/**
* futex_atomic_op_inuser() - Atomic arithmetic operation with constant
* argument and comparison of the previous
* futex value with another constant.
*
* @encoded_op: encoded operation to execute
* @uaddr: pointer to user space address
*
* Return:
* 0 - On success
* <0 - On error
*/
static inline int
futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
{
int op = (encoded_op >> 28) & 7;
int cmp = (encoded_op >> 24) & 15;
int oparg = (encoded_op << 8) >> 20;
int cmparg = (encoded_op << 20) >> 20;
int oldval, ret;
u32 tmp;

if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
oparg = 1 << oparg;

pagefault_disable();

ret = -EFAULT;
if (unlikely(get_user(oldval, uaddr) != 0))
goto out_pagefault_enable;

ret = 0;
tmp = oldval;

switch (op) {
case FUTEX_OP_SET:
tmp = oparg;
break;
case FUTEX_OP_ADD:
tmp += oparg;
break;
case FUTEX_OP_OR:
tmp |= oparg;
break;
case FUTEX_OP_ANDN:
tmp &= ~oparg;
break;
case FUTEX_OP_XOR:
tmp ^= oparg;
break;
default:
ret = -ENOSYS;
}

if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0))
ret = -EFAULT;

out_pagefault_enable:
pagefault_enable();

if (ret == 0) {
switch (cmp) {
case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
default: ret = -ENOSYS;
}
}
return ret;
}

/**
* futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
* uaddr with newval if the current value is
* oldval.
* @uval: pointer to store content of @uaddr
* @uaddr: pointer to user space address
* @oldval: old value
* @newval: new value to store to @uaddr
*
* Return:
* 0 - On success
* <0 - On error
*/
static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval)
{
u32 val;

if (unlikely(get_user(val, uaddr) != 0))
return -EFAULT;

if (val == oldval && unlikely(put_user(newval, uaddr) != 0))
return -EFAULT;

*uval = val;

return 0;
}

#else
static inline int
futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
{
Expand Down Expand Up @@ -54,4 +167,5 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
return -ENOSYS;
}

#endif /* CONFIG_SMP */
#endif

0 comments on commit 00f634b

Please sign in to comment.