[PATCH v5 3/4] um: add a UML specific futex implementation

Anton Ivanov anton.ivanov at cambridgegreys.com
Tue Dec 15 04:43:48 EST 2020


On 14/12/2020 18:14, anton.ivanov at cambridgegreys.com wrote:
> From: Anton Ivanov <anton.ivanov at cambridgegreys.com>
>
> The generic asm futex implementation emulates atomic access to
> memory by doing a get_user followed by put_user. These translate
> to two mapping operations on UML with paging enabled in the
> meantime. This, in turn may end up changing interrupts,
> invoking the signal loop, etc.
>
> This replaces the generic implementation by a mapping followed
> by an operation on the mapped segment.
>
> Signed-off-by: Anton Ivanov <anton.ivanov at cambridgegreys.com>
> ---
>   arch/um/include/asm/Kbuild    |   1 -
>   arch/um/include/asm/futex.h   |  14 ++++
>   arch/um/kernel/skas/uaccess.c | 130 ++++++++++++++++++++++++++++++++++
>   3 files changed, 144 insertions(+), 1 deletion(-)
>   create mode 100644 arch/um/include/asm/futex.h
>
> diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild
> index 1c63b260ecc4..873e6815bc50 100644
> --- a/arch/um/include/asm/Kbuild
> +++ b/arch/um/include/asm/Kbuild
> @@ -8,7 +8,6 @@ generic-y += emergency-restart.h
>   generic-y += exec.h
>   generic-y += extable.h
>   generic-y += ftrace.h
> -generic-y += futex.h
>   generic-y += hw_irq.h
>   generic-y += irq_regs.h
>   generic-y += irq_work.h
> diff --git a/arch/um/include/asm/futex.h b/arch/um/include/asm/futex.h
> new file mode 100644
> index 000000000000..0a01f1f60de0
> --- /dev/null
> +++ b/arch/um/include/asm/futex.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_GENERIC_FUTEX_H
> +#define _ASM_GENERIC_FUTEX_H

^^^^ Uggghhh

> +
> +#include <linux/futex.h>
> +#include <linux/uaccess.h>
> +#include <asm/errno.h>
> +
> +
> +extern int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr);
> +extern int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
> +			      u32 oldval, u32 newval);
^^^^ Did not address Johannes comments, will do in v6
> +
> +#endif
> diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c
> index 2dec915abe6f..85022e91bf53 100644
> --- a/arch/um/kernel/skas/uaccess.c
> +++ b/arch/um/kernel/skas/uaccess.c
> @@ -11,6 +11,7 @@
>   #include <asm/current.h>
>   #include <asm/page.h>
>   #include <kern_util.h>
> +#include <asm/futex.h>
>   #include <os.h>
>   
>   pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr)
> @@ -248,3 +249,132 @@ long __strnlen_user(const void __user *str, long len)
>   	return 0;
>   }
>   EXPORT_SYMBOL(__strnlen_user);
> +
> +/**
> + * 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
> + */
> +
> +int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
> +{
> +	int oldval, ret;
> +	struct page *page;
> +	unsigned long addr = (unsigned long) uaddr;
> +	pte_t *pte;
> +
> +	ret = -EFAULT;
> +	if (!access_ok(uaddr, sizeof(*uaddr)))
> +		return -EFAULT;
> +	preempt_disable();
> +	pte = maybe_map(addr, 1);
> +	if (pte == NULL)
> +		goto out_inuser;
> +
> +	page = pte_page(*pte);
> +#ifdef CONFIG_64BIT
> +	pagefault_disable();
> +	addr = (unsigned long) page_address(page) +
> +			(((unsigned long) addr) & ~PAGE_MASK);
> +#else
> +	addr = (unsigned long) kmap_atomic(page) +
> +		((unsigned long) addr & ~PAGE_MASK);
> +#endif
> +	uaddr = (u32 *) addr;
> +	oldval = *uaddr;
> +
> +	ret = 0;
> +
> +	switch (op) {
> +	case FUTEX_OP_SET:
> +		*uaddr = oparg;
> +		break;
> +	case FUTEX_OP_ADD:
> +		*uaddr += oparg;
> +		break;
> +	case FUTEX_OP_OR:
> +		*uaddr |= oparg;
> +		break;
> +	case FUTEX_OP_ANDN:
> +		*uaddr &= ~oparg;
> +		break;
> +	case FUTEX_OP_XOR:
> +		*uaddr ^= oparg;
> +		break;
> +	default:
> +		ret = -ENOSYS;
> +	}
> +#ifdef CONFIG_64BIT
> +	pagefault_enable();
> +#else
> +	kunmap_atomic((void *)addr);
> +#endif
> +
> +out_inuser:
> +	preempt_enable();
> +
> +	if (ret == 0)
> +		*oval = oldval;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(arch_futex_atomic_op_inuser);
> +
> +/**
> + * 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)
> + */
> +
> +int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
> +			      u32 oldval, u32 newval)
> +{
> +	u32 val;
> +	struct page *page;
> +	pte_t *pte;
> +	int ret = -EFAULT;
> +
> +	if (!access_ok(uaddr, sizeof(*uaddr)))
> +		return -EFAULT;
> +
> +	preempt_disable();
> +	pte = maybe_map((unsigned long) uaddr, 1);
> +	if (pte == NULL)
> +		goto out_inatomic;
> +
> +	page = pte_page(*pte);
> +	pagefault_disable();
> +	uaddr = page_address(page) + (((unsigned long) uaddr) & ~PAGE_MASK);
^^^ missing 32 bit case, sending v6 with fix shortly.
> +
> +	val = *uaddr;
> +
> +	ret = cmpxchg(uaddr, oldval, newval);
> +
> +	*uval = val;
> +	pagefault_enable();
> +	ret = 0;
> +
> +out_inatomic:
> +	preempt_enable();
> +	return ret;
> +}
> +EXPORT_SYMBOL(futex_atomic_cmpxchg_inatomic);

-- 
Anton R. Ivanov
Cambridgegreys Limited. Registered in England. Company Number 10273661
https://www.cambridgegreys.com/




More information about the linux-um mailing list