|  | /* SPDX-License-Identifier: GPL-2.0 */ | 
|  | #ifndef _ASM_GENERIC_FUTEX_H | 
|  | #define _ASM_GENERIC_FUTEX_H | 
|  |  | 
|  | #include <linux/futex.h> | 
|  | #include <linux/uaccess.h> | 
|  | #include <asm/errno.h> | 
|  |  | 
|  | #ifndef CONFIG_SMP | 
|  | /* | 
|  | * The following implementation only for uniprocessor machines. | 
|  | * It relies on preempt_disable() ensuring mutual exclusion. | 
|  | * | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant | 
|  | *			  argument and comparison of the previous | 
|  | *			  futex value with another constant. | 
|  | * | 
|  | * @encoded_op:	encoded operation to execute | 
|  | * @uaddr:	pointer to user space address | 
|  | * | 
|  | * Return: | 
|  | * 0 - On success | 
|  | * -EFAULT - User access resulted in a page fault | 
|  | * -EAGAIN - Atomic operation was unable to complete due to contention | 
|  | * -ENOSYS - Operation not supported | 
|  | */ | 
|  | static inline int | 
|  | arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) | 
|  | { | 
|  | int oldval, ret; | 
|  | u32 tmp; | 
|  |  | 
|  | preempt_disable(); | 
|  | pagefault_disable(); | 
|  |  | 
|  | ret = -EFAULT; | 
|  | if (unlikely(get_user(oldval, uaddr) != 0)) | 
|  | goto out_pagefault_enable; | 
|  |  | 
|  | ret = 0; | 
|  | tmp = oldval; | 
|  |  | 
|  | switch (op) { | 
|  | case FUTEX_OP_SET: | 
|  | tmp = oparg; | 
|  | break; | 
|  | case FUTEX_OP_ADD: | 
|  | tmp += oparg; | 
|  | break; | 
|  | case FUTEX_OP_OR: | 
|  | tmp |= oparg; | 
|  | break; | 
|  | case FUTEX_OP_ANDN: | 
|  | tmp &= ~oparg; | 
|  | break; | 
|  | case FUTEX_OP_XOR: | 
|  | tmp ^= oparg; | 
|  | break; | 
|  | default: | 
|  | ret = -ENOSYS; | 
|  | } | 
|  |  | 
|  | if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0)) | 
|  | ret = -EFAULT; | 
|  |  | 
|  | out_pagefault_enable: | 
|  | pagefault_enable(); | 
|  | preempt_enable(); | 
|  |  | 
|  | if (ret == 0) | 
|  | *oval = oldval; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the | 
|  | *				uaddr with newval if the current value is | 
|  | *				oldval. | 
|  | * @uval:	pointer to store content of @uaddr | 
|  | * @uaddr:	pointer to user space address | 
|  | * @oldval:	old value | 
|  | * @newval:	new value to store to @uaddr | 
|  | * | 
|  | * Return: | 
|  | * 0 - On success | 
|  | * -EFAULT - User access resulted in a page fault | 
|  | * -EAGAIN - Atomic operation was unable to complete due to contention | 
|  | * -ENOSYS - Function not implemented (only if !HAVE_FUTEX_CMPXCHG) | 
|  | */ | 
|  | static inline int | 
|  | futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, | 
|  | u32 oldval, u32 newval) | 
|  | { | 
|  | u32 val; | 
|  |  | 
|  | preempt_disable(); | 
|  | if (unlikely(get_user(val, uaddr) != 0)) { | 
|  | preempt_enable(); | 
|  | return -EFAULT; | 
|  | } | 
|  |  | 
|  | if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) { | 
|  | preempt_enable(); | 
|  | return -EFAULT; | 
|  | } | 
|  |  | 
|  | *uval = val; | 
|  | preempt_enable(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #else | 
|  | static inline int | 
|  | arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) | 
|  | { | 
|  | int oldval = 0, ret; | 
|  |  | 
|  | pagefault_disable(); | 
|  |  | 
|  | switch (op) { | 
|  | case FUTEX_OP_SET: | 
|  | case FUTEX_OP_ADD: | 
|  | case FUTEX_OP_OR: | 
|  | case FUTEX_OP_ANDN: | 
|  | case FUTEX_OP_XOR: | 
|  | default: | 
|  | ret = -ENOSYS; | 
|  | } | 
|  |  | 
|  | pagefault_enable(); | 
|  |  | 
|  | if (!ret) | 
|  | *oval = oldval; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static inline int | 
|  | futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, | 
|  | u32 oldval, u32 newval) | 
|  | { | 
|  | return -ENOSYS; | 
|  | } | 
|  |  | 
|  | #endif /* CONFIG_SMP */ | 
|  | #endif |