[PARISC] Fix futex support

Implements futex op support and makes futex cmpxchg atomic.
Tested on 64-bit SMP kernel running on 2 x PA8700s.

[jejb: checkpatch fixes]
Signed-off-by: Carlos O'Donell <carlos@systemhalted.org>
Tested-by: John David Anglin <dave.anglin@bell.net>
Cc: stable@kernel.org
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
Carlos O'Donell 2011-07-08 17:27:00 -04:00 committed by James Bottomley
parent 1765a367a3
commit d9ba5fe76d

View file

@ -5,11 +5,14 @@
#include <linux/futex.h> #include <linux/futex.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/atomic.h>
#include <asm/errno.h> #include <asm/errno.h>
static inline int static inline int
futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
{ {
unsigned long int flags;
u32 val;
int op = (encoded_op >> 28) & 7; int op = (encoded_op >> 28) & 7;
int cmp = (encoded_op >> 24) & 15; int cmp = (encoded_op >> 24) & 15;
int oparg = (encoded_op << 8) >> 20; int oparg = (encoded_op << 8) >> 20;
@ -18,21 +21,58 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
oparg = 1 << oparg; oparg = 1 << oparg;
if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr)))
return -EFAULT; return -EFAULT;
pagefault_disable(); pagefault_disable();
_atomic_spin_lock_irqsave(uaddr, flags);
switch (op) { switch (op) {
case FUTEX_OP_SET: case FUTEX_OP_SET:
/* *(int *)UADDR2 = OPARG; */
ret = get_user(oldval, uaddr);
if (!ret)
ret = put_user(oparg, uaddr);
break;
case FUTEX_OP_ADD: case FUTEX_OP_ADD:
/* *(int *)UADDR2 += OPARG; */
ret = get_user(oldval, uaddr);
if (!ret) {
val = oldval + oparg;
ret = put_user(val, uaddr);
}
break;
case FUTEX_OP_OR: case FUTEX_OP_OR:
/* *(int *)UADDR2 |= OPARG; */
ret = get_user(oldval, uaddr);
if (!ret) {
val = oldval | oparg;
ret = put_user(val, uaddr);
}
break;
case FUTEX_OP_ANDN: case FUTEX_OP_ANDN:
/* *(int *)UADDR2 &= ~OPARG; */
ret = get_user(oldval, uaddr);
if (!ret) {
val = oldval & ~oparg;
ret = put_user(val, uaddr);
}
break;
case FUTEX_OP_XOR: case FUTEX_OP_XOR:
/* *(int *)UADDR2 ^= OPARG; */
ret = get_user(oldval, uaddr);
if (!ret) {
val = oldval ^ oparg;
ret = put_user(val, uaddr);
}
break;
default: default:
ret = -ENOSYS; ret = -ENOSYS;
} }
_atomic_spin_unlock_irqrestore(uaddr, flags);
pagefault_enable(); pagefault_enable();
if (!ret) { if (!ret) {
@ -54,7 +94,9 @@ static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval) u32 oldval, u32 newval)
{ {
int ret;
u32 val; u32 val;
unsigned long flags;
/* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is /* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
* our gateway page, and causes no end of trouble... * our gateway page, and causes no end of trouble...
@ -65,12 +107,24 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
return -EFAULT; return -EFAULT;
if (get_user(val, uaddr)) /* HPPA has no cmpxchg in hardware and therefore the
return -EFAULT; * best we can do here is use an array of locks. The
if (val == oldval && put_user(newval, uaddr)) * lock selected is based on a hash of the userspace
return -EFAULT; * address. This should scale to a couple of CPUs.
*/
_atomic_spin_lock_irqsave(uaddr, flags);
ret = get_user(val, uaddr);
if (!ret && val == oldval)
ret = put_user(newval, uaddr);
*uval = val; *uval = val;
return 0;
_atomic_spin_unlock_irqrestore(uaddr, flags);
return ret;
} }
#endif /*__KERNEL__*/ #endif /*__KERNEL__*/