1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2f8aaeaceSJeff Dike #ifndef _ASM_GENERIC_FUTEX_H
3f8aaeaceSJeff Dike #define _ASM_GENERIC_FUTEX_H
4f8aaeaceSJeff Dike
5f8aaeaceSJeff Dike #include <linux/futex.h>
6730f412cSJeff Dike #include <linux/uaccess.h>
7f8aaeaceSJeff Dike #include <asm/errno.h>
8f8aaeaceSJeff Dike
93f2bedabSArnd Bergmann #ifndef futex_atomic_cmpxchg_inatomic
1000f634bcSLey Foon Tan #ifndef CONFIG_SMP
1100f634bcSLey Foon Tan /*
1200f634bcSLey Foon Tan * The following implementation only for uniprocessor machines.
13f3dae07eSDavid Hildenbrand * It relies on preempt_disable() ensuring mutual exclusion.
1400f634bcSLey Foon Tan *
1500f634bcSLey Foon Tan */
163f2bedabSArnd Bergmann #define futex_atomic_cmpxchg_inatomic(uval, uaddr, oldval, newval) \
174e0d8463SArnd Bergmann futex_atomic_cmpxchg_inatomic_local(uval, uaddr, oldval, newval)
183f2bedabSArnd Bergmann #define arch_futex_atomic_op_inuser(op, oparg, oval, uaddr) \
194e0d8463SArnd Bergmann futex_atomic_op_inuser_local(op, oparg, oval, uaddr)
203f2bedabSArnd Bergmann #endif /* CONFIG_SMP */
213f2bedabSArnd Bergmann #endif
2200f634bcSLey Foon Tan
2300f634bcSLey Foon Tan /**
24*0f09c274SArnd Bergmann * futex_atomic_op_inuser_local() - Atomic arithmetic operation with constant
2500f634bcSLey Foon Tan * argument and comparison of the previous
2600f634bcSLey Foon Tan * futex value with another constant.
2700f634bcSLey Foon Tan *
2800f634bcSLey Foon Tan * @encoded_op: encoded operation to execute
2900f634bcSLey Foon Tan * @uaddr: pointer to user space address
3000f634bcSLey Foon Tan *
3100f634bcSLey Foon Tan * Return:
3200f634bcSLey Foon Tan * 0 - On success
3342750351SWill Deacon * -EFAULT - User access resulted in a page fault
3442750351SWill Deacon * -EAGAIN - Atomic operation was unable to complete due to contention
3542750351SWill Deacon * -ENOSYS - Operation not supported
3600f634bcSLey Foon Tan */
3700f634bcSLey Foon Tan static inline int
futex_atomic_op_inuser_local(int op,u32 oparg,int * oval,u32 __user * uaddr)383f2bedabSArnd Bergmann futex_atomic_op_inuser_local(int op, u32 oparg, int *oval, u32 __user *uaddr)
3900f634bcSLey Foon Tan {
4000f634bcSLey Foon Tan int oldval, ret;
4100f634bcSLey Foon Tan u32 tmp;
4200f634bcSLey Foon Tan
43f3dae07eSDavid Hildenbrand preempt_disable();
4400f634bcSLey Foon Tan
4500f634bcSLey Foon Tan ret = -EFAULT;
4600f634bcSLey Foon Tan if (unlikely(get_user(oldval, uaddr) != 0))
4700f634bcSLey Foon Tan goto out_pagefault_enable;
4800f634bcSLey Foon Tan
4900f634bcSLey Foon Tan ret = 0;
5000f634bcSLey Foon Tan tmp = oldval;
5100f634bcSLey Foon Tan
5200f634bcSLey Foon Tan switch (op) {
5300f634bcSLey Foon Tan case FUTEX_OP_SET:
5400f634bcSLey Foon Tan tmp = oparg;
5500f634bcSLey Foon Tan break;
5600f634bcSLey Foon Tan case FUTEX_OP_ADD:
5700f634bcSLey Foon Tan tmp += oparg;
5800f634bcSLey Foon Tan break;
5900f634bcSLey Foon Tan case FUTEX_OP_OR:
6000f634bcSLey Foon Tan tmp |= oparg;
6100f634bcSLey Foon Tan break;
6200f634bcSLey Foon Tan case FUTEX_OP_ANDN:
6300f634bcSLey Foon Tan tmp &= ~oparg;
6400f634bcSLey Foon Tan break;
6500f634bcSLey Foon Tan case FUTEX_OP_XOR:
6600f634bcSLey Foon Tan tmp ^= oparg;
6700f634bcSLey Foon Tan break;
6800f634bcSLey Foon Tan default:
6900f634bcSLey Foon Tan ret = -ENOSYS;
7000f634bcSLey Foon Tan }
7100f634bcSLey Foon Tan
7200f634bcSLey Foon Tan if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0))
7300f634bcSLey Foon Tan ret = -EFAULT;
7400f634bcSLey Foon Tan
7500f634bcSLey Foon Tan out_pagefault_enable:
76f3dae07eSDavid Hildenbrand preempt_enable();
7700f634bcSLey Foon Tan
7830d6e0a4SJiri Slaby if (ret == 0)
7930d6e0a4SJiri Slaby *oval = oldval;
8030d6e0a4SJiri Slaby
8100f634bcSLey Foon Tan return ret;
8200f634bcSLey Foon Tan }
8300f634bcSLey Foon Tan
8400f634bcSLey Foon Tan /**
853f2bedabSArnd Bergmann * futex_atomic_cmpxchg_inatomic_local() - Compare and exchange the content of the
8600f634bcSLey Foon Tan * uaddr with newval if the current value is
8700f634bcSLey Foon Tan * oldval.
8800f634bcSLey Foon Tan * @uval: pointer to store content of @uaddr
8900f634bcSLey Foon Tan * @uaddr: pointer to user space address
9000f634bcSLey Foon Tan * @oldval: old value
9100f634bcSLey Foon Tan * @newval: new value to store to @uaddr
9200f634bcSLey Foon Tan *
9300f634bcSLey Foon Tan * Return:
9400f634bcSLey Foon Tan * 0 - On success
9542750351SWill Deacon * -EFAULT - User access resulted in a page fault
9642750351SWill Deacon * -EAGAIN - Atomic operation was unable to complete due to contention
9700f634bcSLey Foon Tan */
9800f634bcSLey Foon Tan static inline int
futex_atomic_cmpxchg_inatomic_local(u32 * uval,u32 __user * uaddr,u32 oldval,u32 newval)993f2bedabSArnd Bergmann futex_atomic_cmpxchg_inatomic_local(u32 *uval, u32 __user *uaddr,
10000f634bcSLey Foon Tan u32 oldval, u32 newval)
10100f634bcSLey Foon Tan {
10200f634bcSLey Foon Tan u32 val;
10300f634bcSLey Foon Tan
104d9b9ff8cSDavid Hildenbrand preempt_disable();
105fba7cd68SRomain Perier if (unlikely(get_user(val, uaddr) != 0)) {
106fba7cd68SRomain Perier preempt_enable();
10700f634bcSLey Foon Tan return -EFAULT;
108fba7cd68SRomain Perier }
10900f634bcSLey Foon Tan
110fba7cd68SRomain Perier if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
111fba7cd68SRomain Perier preempt_enable();
11200f634bcSLey Foon Tan return -EFAULT;
113fba7cd68SRomain Perier }
11400f634bcSLey Foon Tan
11500f634bcSLey Foon Tan *uval = val;
116d9b9ff8cSDavid Hildenbrand preempt_enable();
11700f634bcSLey Foon Tan
11800f634bcSLey Foon Tan return 0;
11900f634bcSLey Foon Tan }
12000f634bcSLey Foon Tan
121f8aaeaceSJeff Dike #endif
122