Skip to content

Commit

Permalink
Fix futex support
Browse files Browse the repository at this point in the history
commit d9ba5fe upstream.

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>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Carlos O'Donell authored and Greg Kroah-Hartman committed Aug 16, 2011
1 parent 6985fbb commit ec36ea6
Showing 1 changed file with 60 additions and 6 deletions.
66 changes: 60 additions & 6 deletions arch/parisc/include/asm/futex.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@

#include <linux/futex.h>
#include <linux/uaccess.h>
#include <asm/atomic.h>
#include <asm/errno.h>

static inline int
futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
{
unsigned long int flags;
u32 val;
int op = (encoded_op >> 28) & 7;
int cmp = (encoded_op >> 24) & 15;
int oparg = (encoded_op << 8) >> 20;
Expand All @@ -18,21 +21,58 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
oparg = 1 << oparg;

if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr)))
return -EFAULT;

pagefault_disable();

_atomic_spin_lock_irqsave(uaddr, flags);

switch (op) {
case FUTEX_OP_SET:
/* *(int *)UADDR2 = OPARG; */
ret = get_user(oldval, uaddr);
if (!ret)
ret = put_user(oparg, uaddr);
break;
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:
/* *(int *)UADDR2 |= OPARG; */
ret = get_user(oldval, uaddr);
if (!ret) {
val = oldval | oparg;
ret = put_user(val, uaddr);
}
break;
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:
/* *(int *)UADDR2 ^= OPARG; */
ret = get_user(oldval, uaddr);
if (!ret) {
val = oldval ^ oparg;
ret = put_user(val, uaddr);
}
break;
default:
ret = -ENOSYS;
}

_atomic_spin_unlock_irqrestore(uaddr, flags);

pagefault_enable();

if (!ret) {
Expand All @@ -54,7 +94,9 @@ static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval)
{
int ret;
u32 val;
unsigned long flags;

/* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
* our gateway page, and causes no end of trouble...
Expand All @@ -65,12 +107,24 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
return -EFAULT;

if (get_user(val, uaddr))
return -EFAULT;
if (val == oldval && put_user(newval, uaddr))
return -EFAULT;
/* HPPA has no cmpxchg in hardware and therefore the
* best we can do here is use an array of locks. The
* lock selected is based on a hash of the userspace
* 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;
return 0;

_atomic_spin_unlock_irqrestore(uaddr, flags);

return ret;
}

#endif /*__KERNEL__*/
Expand Down

0 comments on commit ec36ea6

Please sign in to comment.