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