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 14 static 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 24 static 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 34 static inline int 35 futex_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 */ 116 static inline int 117 futex_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