xref: /linux/arch/parisc/include/asm/futex.h (revision 99aed91a8dc6e398003333decacc101e5e504988)
1deae26bfSKyle McMartin #ifndef _ASM_PARISC_FUTEX_H
2deae26bfSKyle McMartin #define _ASM_PARISC_FUTEX_H
3deae26bfSKyle McMartin 
4deae26bfSKyle McMartin #ifdef __KERNEL__
5deae26bfSKyle McMartin 
6deae26bfSKyle McMartin #include <linux/futex.h>
7deae26bfSKyle McMartin #include <linux/uaccess.h>
8d9ba5fe7SCarlos O'Donell #include <asm/atomic.h>
9deae26bfSKyle McMartin #include <asm/errno.h>
10deae26bfSKyle McMartin 
118b232816SJohn David Anglin /* The following has to match the LWS code in syscall.S.  We have
128b232816SJohn David Anglin    sixteen four-word locks. */
138b232816SJohn David Anglin 
148b232816SJohn David Anglin static inline void
158b232816SJohn David Anglin _futex_spin_lock_irqsave(u32 __user *uaddr, unsigned long int *flags)
168b232816SJohn David Anglin {
178b232816SJohn David Anglin 	extern u32 lws_lock_start[];
188b232816SJohn David Anglin 	long index = ((long)uaddr & 0xf0) >> 2;
198b232816SJohn David Anglin 	arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
208b232816SJohn David Anglin 	local_irq_save(*flags);
218b232816SJohn David Anglin 	arch_spin_lock(s);
228b232816SJohn David Anglin }
238b232816SJohn David Anglin 
248b232816SJohn David Anglin static inline void
258b232816SJohn David Anglin _futex_spin_unlock_irqrestore(u32 __user *uaddr, unsigned long int *flags)
268b232816SJohn David Anglin {
278b232816SJohn David Anglin 	extern u32 lws_lock_start[];
288b232816SJohn David Anglin 	long index = ((long)uaddr & 0xf0) >> 2;
298b232816SJohn David Anglin 	arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
308b232816SJohn David Anglin 	arch_spin_unlock(s);
318b232816SJohn David Anglin 	local_irq_restore(*flags);
328b232816SJohn David Anglin }
338b232816SJohn David Anglin 
34deae26bfSKyle McMartin static inline int
358d7718aaSMichel Lespinasse futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
36deae26bfSKyle McMartin {
37d9ba5fe7SCarlos O'Donell 	unsigned long int flags;
38deae26bfSKyle McMartin 	int op = (encoded_op >> 28) & 7;
39deae26bfSKyle McMartin 	int cmp = (encoded_op >> 24) & 15;
40deae26bfSKyle McMartin 	int oparg = (encoded_op << 8) >> 20;
41deae26bfSKyle McMartin 	int cmparg = (encoded_op << 20) >> 20;
42*99aed91aSJohn David Anglin 	int oldval, ret;
43*99aed91aSJohn David Anglin 	u32 tmp;
44*99aed91aSJohn David Anglin 
45deae26bfSKyle McMartin 	if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
46deae26bfSKyle McMartin 		oparg = 1 << oparg;
47deae26bfSKyle McMartin 
48d9ba5fe7SCarlos O'Donell 	if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr)))
49deae26bfSKyle McMartin 		return -EFAULT;
50deae26bfSKyle McMartin 
51*99aed91aSJohn David Anglin 	_futex_spin_lock_irqsave(uaddr, &flags);
52deae26bfSKyle McMartin 	pagefault_disable();
53deae26bfSKyle McMartin 
54*99aed91aSJohn David Anglin 	ret = -EFAULT;
55*99aed91aSJohn David Anglin 	if (unlikely(get_user(oldval, uaddr) != 0))
56*99aed91aSJohn David Anglin 		goto out_pagefault_enable;
57*99aed91aSJohn David Anglin 
58*99aed91aSJohn David Anglin 	ret = 0;
59*99aed91aSJohn David Anglin 	tmp = oldval;
60d9ba5fe7SCarlos O'Donell 
61deae26bfSKyle McMartin 	switch (op) {
62deae26bfSKyle McMartin 	case FUTEX_OP_SET:
63*99aed91aSJohn David Anglin 		tmp = oparg;
64d9ba5fe7SCarlos O'Donell 		break;
65deae26bfSKyle McMartin 	case FUTEX_OP_ADD:
66*99aed91aSJohn David Anglin 		tmp += oparg;
67d9ba5fe7SCarlos O'Donell 		break;
68deae26bfSKyle McMartin 	case FUTEX_OP_OR:
69*99aed91aSJohn David Anglin 		tmp |= oparg;
70d9ba5fe7SCarlos O'Donell 		break;
71deae26bfSKyle McMartin 	case FUTEX_OP_ANDN:
72*99aed91aSJohn David Anglin 		tmp &= ~oparg;
73d9ba5fe7SCarlos O'Donell 		break;
74deae26bfSKyle McMartin 	case FUTEX_OP_XOR:
75*99aed91aSJohn David Anglin 		tmp ^= oparg;
76d9ba5fe7SCarlos O'Donell 		break;
77deae26bfSKyle McMartin 	default:
78deae26bfSKyle McMartin 		ret = -ENOSYS;
79deae26bfSKyle McMartin 	}
80deae26bfSKyle McMartin 
81*99aed91aSJohn David Anglin 	if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0))
82*99aed91aSJohn David Anglin 		ret = -EFAULT;
83*99aed91aSJohn David Anglin 
84*99aed91aSJohn David Anglin out_pagefault_enable:
85*99aed91aSJohn David Anglin 	pagefault_enable();
868b232816SJohn David Anglin 	_futex_spin_unlock_irqrestore(uaddr, &flags);
87d9ba5fe7SCarlos O'Donell 
88*99aed91aSJohn David Anglin 	if (ret == 0) {
89deae26bfSKyle McMartin 		switch (cmp) {
90deae26bfSKyle McMartin 		case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
91deae26bfSKyle McMartin 		case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
92deae26bfSKyle McMartin 		case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
93deae26bfSKyle McMartin 		case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
94deae26bfSKyle McMartin 		case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
95deae26bfSKyle McMartin 		case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
96deae26bfSKyle McMartin 		default: ret = -ENOSYS;
97deae26bfSKyle McMartin 		}
98deae26bfSKyle McMartin 	}
99deae26bfSKyle McMartin 	return ret;
100deae26bfSKyle McMartin }
101deae26bfSKyle McMartin 
102deae26bfSKyle McMartin static inline int
1038d7718aaSMichel Lespinasse futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
1048d7718aaSMichel Lespinasse 			      u32 oldval, u32 newval)
105deae26bfSKyle McMartin {
1068d7718aaSMichel Lespinasse 	u32 val;
107d9ba5fe7SCarlos O'Donell 	unsigned long flags;
108deae26bfSKyle McMartin 
109deae26bfSKyle McMartin 	/* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
110deae26bfSKyle McMartin 	 * our gateway page, and causes no end of trouble...
111deae26bfSKyle McMartin 	 */
112deae26bfSKyle McMartin 	if (segment_eq(KERNEL_DS, get_fs()) && !uaddr)
113deae26bfSKyle McMartin 		return -EFAULT;
114deae26bfSKyle McMartin 
1158d7718aaSMichel Lespinasse 	if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
116deae26bfSKyle McMartin 		return -EFAULT;
117deae26bfSKyle McMartin 
118d9ba5fe7SCarlos O'Donell 	/* HPPA has no cmpxchg in hardware and therefore the
119d9ba5fe7SCarlos O'Donell 	 * best we can do here is use an array of locks. The
120d9ba5fe7SCarlos O'Donell 	 * lock selected is based on a hash of the userspace
121d9ba5fe7SCarlos O'Donell 	 * address. This should scale to a couple of CPUs.
122d9ba5fe7SCarlos O'Donell 	 */
123d9ba5fe7SCarlos O'Donell 
1248b232816SJohn David Anglin 	_futex_spin_lock_irqsave(uaddr, &flags);
125*99aed91aSJohn David Anglin 	if (unlikely(get_user(val, uaddr) != 0)) {
126*99aed91aSJohn David Anglin 		_futex_spin_unlock_irqrestore(uaddr, &flags);
127*99aed91aSJohn David Anglin 		return -EFAULT;
128*99aed91aSJohn David Anglin 	}
129d9ba5fe7SCarlos O'Donell 
130*99aed91aSJohn David Anglin 	if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
131*99aed91aSJohn David Anglin 		_futex_spin_unlock_irqrestore(uaddr, &flags);
132*99aed91aSJohn David Anglin 		return -EFAULT;
133*99aed91aSJohn David Anglin 	}
134d9ba5fe7SCarlos O'Donell 
13537a9d912SMichel Lespinasse 	*uval = val;
1368b232816SJohn David Anglin 	_futex_spin_unlock_irqrestore(uaddr, &flags);
137d9ba5fe7SCarlos O'Donell 
138*99aed91aSJohn David Anglin 	return 0;
139deae26bfSKyle McMartin }
140deae26bfSKyle McMartin 
141deae26bfSKyle McMartin #endif /*__KERNEL__*/
142deae26bfSKyle McMartin #endif /*_ASM_PARISC_FUTEX_H*/
143