xref: /linux/include/asm-generic/futex.h (revision a1c3be890440a1769ed6f822376a3e3ab0d42994)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _ASM_GENERIC_FUTEX_H
3 #define _ASM_GENERIC_FUTEX_H
4 
5 #include <linux/futex.h>
6 #include <linux/uaccess.h>
7 #include <asm/errno.h>
8 
9 #ifndef CONFIG_SMP
10 /*
11  * The following implementation only for uniprocessor machines.
12  * It relies on preempt_disable() ensuring mutual exclusion.
13  *
14  */
15 
16 /**
17  * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
18  *			  argument and comparison of the previous
19  *			  futex value with another constant.
20  *
21  * @encoded_op:	encoded operation to execute
22  * @uaddr:	pointer to user space address
23  *
24  * Return:
25  * 0 - On success
26  * -EFAULT - User access resulted in a page fault
27  * -EAGAIN - Atomic operation was unable to complete due to contention
28  * -ENOSYS - Operation not supported
29  */
30 static inline int
31 arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
32 {
33 	int oldval, ret;
34 	u32 tmp;
35 
36 	preempt_disable();
37 
38 	ret = -EFAULT;
39 	if (unlikely(get_user(oldval, uaddr) != 0))
40 		goto out_pagefault_enable;
41 
42 	ret = 0;
43 	tmp = oldval;
44 
45 	switch (op) {
46 	case FUTEX_OP_SET:
47 		tmp = oparg;
48 		break;
49 	case FUTEX_OP_ADD:
50 		tmp += oparg;
51 		break;
52 	case FUTEX_OP_OR:
53 		tmp |= oparg;
54 		break;
55 	case FUTEX_OP_ANDN:
56 		tmp &= ~oparg;
57 		break;
58 	case FUTEX_OP_XOR:
59 		tmp ^= oparg;
60 		break;
61 	default:
62 		ret = -ENOSYS;
63 	}
64 
65 	if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0))
66 		ret = -EFAULT;
67 
68 out_pagefault_enable:
69 	preempt_enable();
70 
71 	if (ret == 0)
72 		*oval = oldval;
73 
74 	return ret;
75 }
76 
77 /**
78  * futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
79  *				uaddr with newval if the current value is
80  *				oldval.
81  * @uval:	pointer to store content of @uaddr
82  * @uaddr:	pointer to user space address
83  * @oldval:	old value
84  * @newval:	new value to store to @uaddr
85  *
86  * Return:
87  * 0 - On success
88  * -EFAULT - User access resulted in a page fault
89  * -EAGAIN - Atomic operation was unable to complete due to contention
90  * -ENOSYS - Function not implemented (only if !HAVE_FUTEX_CMPXCHG)
91  */
92 static inline int
93 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
94 			      u32 oldval, u32 newval)
95 {
96 	u32 val;
97 
98 	preempt_disable();
99 	if (unlikely(get_user(val, uaddr) != 0)) {
100 		preempt_enable();
101 		return -EFAULT;
102 	}
103 
104 	if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
105 		preempt_enable();
106 		return -EFAULT;
107 	}
108 
109 	*uval = val;
110 	preempt_enable();
111 
112 	return 0;
113 }
114 
115 #else
116 static inline int
117 arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
118 {
119 	return -ENOSYS;
120 }
121 
122 static inline int
123 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
124 			      u32 oldval, u32 newval)
125 {
126 	return -ENOSYS;
127 }
128 
129 #endif /* CONFIG_SMP */
130 #endif
131