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 */
30static inline int
31arch_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
68out_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 */
92static inline int
93futex_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
116static inline int
117arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
118{
119	return -ENOSYS;
120}
121
122static inline int
123futex_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