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