xref: /linux/arch/parisc/include/asm/futex.h (revision d0585d742ff2d82accd26c661c60a6d260429c4a)
1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2deae26bfSKyle McMartin #ifndef _ASM_PARISC_FUTEX_H
3deae26bfSKyle McMartin #define _ASM_PARISC_FUTEX_H
4deae26bfSKyle McMartin 
5deae26bfSKyle McMartin #include <linux/futex.h>
6deae26bfSKyle McMartin #include <linux/uaccess.h>
7d9ba5fe7SCarlos O'Donell #include <asm/atomic.h>
8deae26bfSKyle McMartin #include <asm/errno.h>
9deae26bfSKyle McMartin 
108b232816SJohn David Anglin /* The following has to match the LWS code in syscall.S.  We have
11*d0585d74SJohn David Anglin  * 256 four-word locks. We use bits 20-27 of the futex virtual
12*d0585d74SJohn David Anglin  * address for the hash index.
13*d0585d74SJohn David Anglin  */
14*d0585d74SJohn David Anglin 
15*d0585d74SJohn David Anglin static inline unsigned long _futex_hash_index(unsigned long ua)
16*d0585d74SJohn David Anglin {
17*d0585d74SJohn David Anglin 	return (ua >> 2) & 0x3fc;
18*d0585d74SJohn David Anglin }
198b232816SJohn David Anglin 
208b232816SJohn David Anglin static inline void
21*d0585d74SJohn David Anglin _futex_spin_lock_irqsave(arch_spinlock_t *s, unsigned long *flags)
228b232816SJohn David Anglin {
23*d0585d74SJohn David Anglin 	local_irq_save(*flags);
248b232816SJohn David Anglin 	arch_spin_lock(s);
258b232816SJohn David Anglin }
268b232816SJohn David Anglin 
278b232816SJohn David Anglin static inline void
28*d0585d74SJohn David Anglin _futex_spin_unlock_irqrestore(arch_spinlock_t *s, unsigned long *flags)
298b232816SJohn David Anglin {
308b232816SJohn David Anglin 	arch_spin_unlock(s);
31*d0585d74SJohn David Anglin 	local_irq_restore(*flags);
328b232816SJohn David Anglin }
338b232816SJohn David Anglin 
34deae26bfSKyle McMartin static inline int
3530d6e0a4SJiri Slaby arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
36deae26bfSKyle McMartin {
37*d0585d74SJohn David Anglin 	extern u32 lws_lock_start[];
38*d0585d74SJohn David Anglin 	unsigned long ua = (unsigned long)uaddr;
39*d0585d74SJohn David Anglin 	arch_spinlock_t *s;
40*d0585d74SJohn David Anglin 	unsigned long flags;
4199aed91aSJohn David Anglin 	int oldval, ret;
4299aed91aSJohn David Anglin 	u32 tmp;
4399aed91aSJohn David Anglin 
44*d0585d74SJohn David Anglin 	s = (arch_spinlock_t *)&lws_lock_start[_futex_hash_index(ua)];
45*d0585d74SJohn David Anglin 	_futex_spin_lock_irqsave(s, &flags);
467e992711SDave Anglin 
47*d0585d74SJohn David Anglin 	/* Return -EFAULT if we encounter a page fault or COW break */
48*d0585d74SJohn David Anglin 	if (unlikely(get_user(oldval, uaddr) != 0)) {
49*d0585d74SJohn David Anglin 		ret = -EFAULT;
5099aed91aSJohn David Anglin 		goto out_pagefault_enable;
51*d0585d74SJohn David Anglin 	}
5299aed91aSJohn David Anglin 
5399aed91aSJohn David Anglin 	ret = 0;
5499aed91aSJohn David Anglin 	tmp = oldval;
55d9ba5fe7SCarlos O'Donell 
56deae26bfSKyle McMartin 	switch (op) {
57deae26bfSKyle McMartin 	case FUTEX_OP_SET:
5899aed91aSJohn David Anglin 		tmp = oparg;
59d9ba5fe7SCarlos O'Donell 		break;
60deae26bfSKyle McMartin 	case FUTEX_OP_ADD:
6199aed91aSJohn David Anglin 		tmp += oparg;
62d9ba5fe7SCarlos O'Donell 		break;
63deae26bfSKyle McMartin 	case FUTEX_OP_OR:
6499aed91aSJohn David Anglin 		tmp |= oparg;
65d9ba5fe7SCarlos O'Donell 		break;
66deae26bfSKyle McMartin 	case FUTEX_OP_ANDN:
6799aed91aSJohn David Anglin 		tmp &= ~oparg;
68d9ba5fe7SCarlos O'Donell 		break;
69deae26bfSKyle McMartin 	case FUTEX_OP_XOR:
7099aed91aSJohn David Anglin 		tmp ^= oparg;
71d9ba5fe7SCarlos O'Donell 		break;
72deae26bfSKyle McMartin 	default:
73deae26bfSKyle McMartin 		ret = -ENOSYS;
74*d0585d74SJohn David Anglin 		goto out_pagefault_enable;
75deae26bfSKyle McMartin 	}
76deae26bfSKyle McMartin 
77*d0585d74SJohn David Anglin 	if (unlikely(put_user(tmp, uaddr) != 0))
7899aed91aSJohn David Anglin 		ret = -EFAULT;
7999aed91aSJohn David Anglin 
8099aed91aSJohn David Anglin out_pagefault_enable:
81*d0585d74SJohn David Anglin 	_futex_spin_unlock_irqrestore(s, &flags);
82d9ba5fe7SCarlos O'Donell 
8330d6e0a4SJiri Slaby 	if (!ret)
8430d6e0a4SJiri Slaby 		*oval = oldval;
8530d6e0a4SJiri Slaby 
86deae26bfSKyle McMartin 	return ret;
87deae26bfSKyle McMartin }
88deae26bfSKyle McMartin 
89deae26bfSKyle McMartin static inline int
908d7718aaSMichel Lespinasse futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
918d7718aaSMichel Lespinasse 			      u32 oldval, u32 newval)
92deae26bfSKyle McMartin {
93*d0585d74SJohn David Anglin 	extern u32 lws_lock_start[];
94*d0585d74SJohn David Anglin 	unsigned long ua = (unsigned long)uaddr;
95*d0585d74SJohn David Anglin 	arch_spinlock_t *s;
968d7718aaSMichel Lespinasse 	u32 val;
97*d0585d74SJohn David Anglin 	unsigned long flags;
98deae26bfSKyle McMartin 
99deae26bfSKyle McMartin 	/* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
100deae26bfSKyle McMartin 	 * our gateway page, and causes no end of trouble...
101deae26bfSKyle McMartin 	 */
102db68ce10SAl Viro 	if (uaccess_kernel() && !uaddr)
103deae26bfSKyle McMartin 		return -EFAULT;
104deae26bfSKyle McMartin 
10596d4f267SLinus Torvalds 	if (!access_ok(uaddr, sizeof(u32)))
106deae26bfSKyle McMartin 		return -EFAULT;
107deae26bfSKyle McMartin 
108d9ba5fe7SCarlos O'Donell 	/* HPPA has no cmpxchg in hardware and therefore the
109d9ba5fe7SCarlos O'Donell 	 * best we can do here is use an array of locks. The
110*d0585d74SJohn David Anglin 	 * lock selected is based on a hash of the virtual
111*d0585d74SJohn David Anglin 	 * address of the futex. This should scale to a couple
112*d0585d74SJohn David Anglin 	 * of CPUs.
113d9ba5fe7SCarlos O'Donell 	 */
114d9ba5fe7SCarlos O'Donell 
115*d0585d74SJohn David Anglin 	s = (arch_spinlock_t *)&lws_lock_start[_futex_hash_index(ua)];
116*d0585d74SJohn David Anglin 	_futex_spin_lock_irqsave(s, &flags);
11799aed91aSJohn David Anglin 	if (unlikely(get_user(val, uaddr) != 0)) {
118*d0585d74SJohn David Anglin 		_futex_spin_unlock_irqrestore(s, &flags);
11999aed91aSJohn David Anglin 		return -EFAULT;
12099aed91aSJohn David Anglin 	}
121d9ba5fe7SCarlos O'Donell 
12299aed91aSJohn David Anglin 	if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
123*d0585d74SJohn David Anglin 		_futex_spin_unlock_irqrestore(s, &flags);
12499aed91aSJohn David Anglin 		return -EFAULT;
12599aed91aSJohn David Anglin 	}
126d9ba5fe7SCarlos O'Donell 
12737a9d912SMichel Lespinasse 	*uval = val;
128*d0585d74SJohn David Anglin 	_futex_spin_unlock_irqrestore(s, &flags);
129d9ba5fe7SCarlos O'Donell 
13099aed91aSJohn David Anglin 	return 0;
131deae26bfSKyle McMartin }
132deae26bfSKyle McMartin 
133deae26bfSKyle McMartin #endif /*_ASM_PARISC_FUTEX_H*/
134