int arch_futex_atomic_op_inuser()

in kernel/skas/uaccess.c [249:310]


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;
}