xref: /openbmc/linux/include/asm-generic/futex.h (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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