162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef _ASM_GENERIC_FUTEX_H 362306a36Sopenharmony_ci#define _ASM_GENERIC_FUTEX_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/futex.h> 662306a36Sopenharmony_ci#include <linux/uaccess.h> 762306a36Sopenharmony_ci#include <asm/errno.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#ifndef futex_atomic_cmpxchg_inatomic 1062306a36Sopenharmony_ci#ifndef CONFIG_SMP 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * The following implementation only for uniprocessor machines. 1362306a36Sopenharmony_ci * It relies on preempt_disable() ensuring mutual exclusion. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci#define futex_atomic_cmpxchg_inatomic(uval, uaddr, oldval, newval) \ 1762306a36Sopenharmony_ci futex_atomic_cmpxchg_inatomic_local(uval, uaddr, oldval, newval) 1862306a36Sopenharmony_ci#define arch_futex_atomic_op_inuser(op, oparg, oval, uaddr) \ 1962306a36Sopenharmony_ci futex_atomic_op_inuser_local(op, oparg, oval, uaddr) 2062306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 2162306a36Sopenharmony_ci#endif 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/** 2462306a36Sopenharmony_ci * futex_atomic_op_inuser_local() - Atomic arithmetic operation with constant 2562306a36Sopenharmony_ci * argument and comparison of the previous 2662306a36Sopenharmony_ci * futex value with another constant. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * @encoded_op: encoded operation to execute 2962306a36Sopenharmony_ci * @uaddr: pointer to user space address 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Return: 3262306a36Sopenharmony_ci * 0 - On success 3362306a36Sopenharmony_ci * -EFAULT - User access resulted in a page fault 3462306a36Sopenharmony_ci * -EAGAIN - Atomic operation was unable to complete due to contention 3562306a36Sopenharmony_ci * -ENOSYS - Operation not supported 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistatic inline int 3862306a36Sopenharmony_cifutex_atomic_op_inuser_local(int op, u32 oparg, int *oval, u32 __user *uaddr) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci int oldval, ret; 4162306a36Sopenharmony_ci u32 tmp; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci preempt_disable(); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ret = -EFAULT; 4662306a36Sopenharmony_ci if (unlikely(get_user(oldval, uaddr) != 0)) 4762306a36Sopenharmony_ci goto out_pagefault_enable; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci ret = 0; 5062306a36Sopenharmony_ci tmp = oldval; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci switch (op) { 5362306a36Sopenharmony_ci case FUTEX_OP_SET: 5462306a36Sopenharmony_ci tmp = oparg; 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case FUTEX_OP_ADD: 5762306a36Sopenharmony_ci tmp += oparg; 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci case FUTEX_OP_OR: 6062306a36Sopenharmony_ci tmp |= oparg; 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci case FUTEX_OP_ANDN: 6362306a36Sopenharmony_ci tmp &= ~oparg; 6462306a36Sopenharmony_ci break; 6562306a36Sopenharmony_ci case FUTEX_OP_XOR: 6662306a36Sopenharmony_ci tmp ^= oparg; 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci default: 6962306a36Sopenharmony_ci ret = -ENOSYS; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0)) 7362306a36Sopenharmony_ci ret = -EFAULT; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciout_pagefault_enable: 7662306a36Sopenharmony_ci preempt_enable(); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (ret == 0) 7962306a36Sopenharmony_ci *oval = oldval; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/** 8562306a36Sopenharmony_ci * futex_atomic_cmpxchg_inatomic_local() - Compare and exchange the content of the 8662306a36Sopenharmony_ci * uaddr with newval if the current value is 8762306a36Sopenharmony_ci * oldval. 8862306a36Sopenharmony_ci * @uval: pointer to store content of @uaddr 8962306a36Sopenharmony_ci * @uaddr: pointer to user space address 9062306a36Sopenharmony_ci * @oldval: old value 9162306a36Sopenharmony_ci * @newval: new value to store to @uaddr 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * Return: 9462306a36Sopenharmony_ci * 0 - On success 9562306a36Sopenharmony_ci * -EFAULT - User access resulted in a page fault 9662306a36Sopenharmony_ci * -EAGAIN - Atomic operation was unable to complete due to contention 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistatic inline int 9962306a36Sopenharmony_cifutex_atomic_cmpxchg_inatomic_local(u32 *uval, u32 __user *uaddr, 10062306a36Sopenharmony_ci u32 oldval, u32 newval) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci u32 val; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci preempt_disable(); 10562306a36Sopenharmony_ci if (unlikely(get_user(val, uaddr) != 0)) { 10662306a36Sopenharmony_ci preempt_enable(); 10762306a36Sopenharmony_ci return -EFAULT; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) { 11162306a36Sopenharmony_ci preempt_enable(); 11262306a36Sopenharmony_ci return -EFAULT; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci *uval = val; 11662306a36Sopenharmony_ci preempt_enable(); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#endif 122