diff options
author | Carlos O'Donell <carlos@systemhalted.org> | 2011-07-08 17:27:00 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-15 18:31:32 -0700 |
commit | ec36ea64e4b31f4eba07f75bcce0a6124923d733 (patch) | |
tree | 8ede8db8d7e8e4274395a6705550abcd57044ca4 | |
parent | 6985fbb2fd41fa001e4926b4507798d9593f1b9f (diff) |
Fix futex support
commit d9ba5fe76d604514444b1ea0a19f38c6196a46e3 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>
-rw-r--r-- | arch/parisc/include/asm/futex.h | 66 |
1 files changed, 60 insertions, 6 deletions
diff --git a/arch/parisc/include/asm/futex.h b/arch/parisc/include/asm/futex.h index 67a33cc27ef..2388bdb3283 100644 --- a/arch/parisc/include/asm/futex.h +++ b/arch/parisc/include/asm/futex.h @@ -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; @@ -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) { @@ -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... @@ -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__*/ |