linux/arch/parisc/include/asm/futex.h
<<
>>
Prefs
   1#ifndef _ASM_PARISC_FUTEX_H
   2#define _ASM_PARISC_FUTEX_H
   3
   4#ifdef __KERNEL__
   5
   6#include <linux/futex.h>
   7#include <linux/uaccess.h>
   8#include <asm/atomic.h>
   9#include <asm/errno.h>
  10
  11/* The following has to match the LWS code in syscall.S.  We have
  12   sixteen four-word locks. */
  13
  14static inline void
  15_futex_spin_lock_irqsave(u32 __user *uaddr, unsigned long int *flags)
  16{
  17        extern u32 lws_lock_start[];
  18        long index = ((long)uaddr & 0xf0) >> 2;
  19        arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
  20        local_irq_save(*flags);
  21        arch_spin_lock(s);
  22}
  23
  24static inline void
  25_futex_spin_unlock_irqrestore(u32 __user *uaddr, unsigned long int *flags)
  26{
  27        extern u32 lws_lock_start[];
  28        long index = ((long)uaddr & 0xf0) >> 2;
  29        arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
  30        arch_spin_unlock(s);
  31        local_irq_restore(*flags);
  32}
  33
  34static inline int
  35futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
  36{
  37        unsigned long int flags;
  38        u32 val;
  39        int op = (encoded_op >> 28) & 7;
  40        int cmp = (encoded_op >> 24) & 15;
  41        int oparg = (encoded_op << 8) >> 20;
  42        int cmparg = (encoded_op << 20) >> 20;
  43        int oldval = 0, ret;
  44        if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
  45                oparg = 1 << oparg;
  46
  47        if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr)))
  48                return -EFAULT;
  49
  50        pagefault_disable();
  51
  52        _futex_spin_lock_irqsave(uaddr, &flags);
  53
  54        switch (op) {
  55        case FUTEX_OP_SET:
  56                /* *(int *)UADDR2 = OPARG; */
  57                ret = get_user(oldval, uaddr);
  58                if (!ret)
  59                        ret = put_user(oparg, uaddr);
  60                break;
  61        case FUTEX_OP_ADD:
  62                /* *(int *)UADDR2 += OPARG; */
  63                ret = get_user(oldval, uaddr);
  64                if (!ret) {
  65                        val = oldval + oparg;
  66                        ret = put_user(val, uaddr);
  67                }
  68                break;
  69        case FUTEX_OP_OR:
  70                /* *(int *)UADDR2 |= OPARG; */
  71                ret = get_user(oldval, uaddr);
  72                if (!ret) {
  73                        val = oldval | oparg;
  74                        ret = put_user(val, uaddr);
  75                }
  76                break;
  77        case FUTEX_OP_ANDN:
  78                /* *(int *)UADDR2 &= ~OPARG; */
  79                ret = get_user(oldval, uaddr);
  80                if (!ret) {
  81                        val = oldval & ~oparg;
  82                        ret = put_user(val, uaddr);
  83                }
  84                break;
  85        case FUTEX_OP_XOR:
  86                /* *(int *)UADDR2 ^= OPARG; */
  87                ret = get_user(oldval, uaddr);
  88                if (!ret) {
  89                        val = oldval ^ oparg;
  90                        ret = put_user(val, uaddr);
  91                }
  92                break;
  93        default:
  94                ret = -ENOSYS;
  95        }
  96
  97        _futex_spin_unlock_irqrestore(uaddr, &flags);
  98
  99        pagefault_enable();
 100
 101        if (!ret) {
 102                switch (cmp) {
 103                case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
 104                case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
 105                case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
 106                case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
 107                case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
 108                case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
 109                default: ret = -ENOSYS;
 110                }
 111        }
 112        return ret;
 113}
 114
 115/* Non-atomic version */
 116static inline int
 117futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
 118                              u32 oldval, u32 newval)
 119{
 120        int ret;
 121        u32 val;
 122        unsigned long flags;
 123
 124        /* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
 125         * our gateway page, and causes no end of trouble...
 126         */
 127        if (segment_eq(KERNEL_DS, get_fs()) && !uaddr)
 128                return -EFAULT;
 129
 130        if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
 131                return -EFAULT;
 132
 133        /* HPPA has no cmpxchg in hardware and therefore the
 134         * best we can do here is use an array of locks. The
 135         * lock selected is based on a hash of the userspace
 136         * address. This should scale to a couple of CPUs.
 137         */
 138
 139        _futex_spin_lock_irqsave(uaddr, &flags);
 140
 141        ret = get_user(val, uaddr);
 142
 143        if (!ret && val == oldval)
 144                ret = put_user(newval, uaddr);
 145
 146        *uval = val;
 147
 148        _futex_spin_unlock_irqrestore(uaddr, &flags);
 149
 150        return ret;
 151}
 152
 153#endif /*__KERNEL__*/
 154#endif /*_ASM_PARISC_FUTEX_H*/
 155