diff options
Diffstat (limited to 'arch/xtensa/include/asm/futex.h')
| -rw-r--r-- | arch/xtensa/include/asm/futex.h | 147 | 
1 files changed, 147 insertions, 0 deletions
diff --git a/arch/xtensa/include/asm/futex.h b/arch/xtensa/include/asm/futex.h new file mode 100644 index 00000000000..b39531babec --- /dev/null +++ b/arch/xtensa/include/asm/futex.h @@ -0,0 +1,147 @@ +/* + * Atomic futex routines + * + * Based on the PowerPC implementataion + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Copyright (C) 2013 TangoTec Ltd. + * + * Baruch Siach <baruch@tkos.co.il> + */ + +#ifndef _ASM_XTENSA_FUTEX_H +#define _ASM_XTENSA_FUTEX_H + +#ifdef __KERNEL__ + +#include <linux/futex.h> +#include <linux/uaccess.h> +#include <linux/errno.h> + +#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ +	__asm__ __volatile(				\ +	"1:	l32i	%0, %2, 0\n"			\ +		insn "\n"				\ +	"	wsr	%0, scompare1\n"		\ +	"2:	s32c1i	%1, %2, 0\n"			\ +	"	bne	%1, %0, 1b\n"			\ +	"	movi	%1, 0\n"			\ +	"3:\n"						\ +	"	.section .fixup,\"ax\"\n"		\ +	"	.align 4\n"				\ +	"4:	.long	3b\n"				\ +	"5:	l32r	%0, 4b\n"			\ +	"	movi	%1, %3\n"			\ +	"	jx	%0\n"				\ +	"	.previous\n"				\ +	"	.section __ex_table,\"a\"\n"		\ +	"	.long 1b,5b,2b,5b\n"			\ +	"	.previous\n"				\ +	: "=&r" (oldval), "=&r" (ret)			\ +	: "r" (uaddr), "I" (-EFAULT), "r" (oparg)	\ +	: "memory") + +static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) +{ +	int op = (encoded_op >> 28) & 7; +	int cmp = (encoded_op >> 24) & 15; +	int oparg = (encoded_op << 8) >> 20; +	int cmparg = (encoded_op << 20) >> 20; +	int oldval = 0, ret; +	if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) +		oparg = 1 << oparg; + +	if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) +		return -EFAULT; + +#if !XCHAL_HAVE_S32C1I +	return -ENOSYS; +#endif + +	pagefault_disable(); + +	switch (op) { +	case FUTEX_OP_SET: +		__futex_atomic_op("mov %1, %4", ret, oldval, uaddr, oparg); +		break; +	case FUTEX_OP_ADD: +		__futex_atomic_op("add %1, %0, %4", ret, oldval, uaddr, +				oparg); +		break; +	case FUTEX_OP_OR: +		__futex_atomic_op("or %1, %0, %4", ret, oldval, uaddr, +				oparg); +		break; +	case FUTEX_OP_ANDN: +		__futex_atomic_op("and %1, %0, %4", ret, oldval, uaddr, +				~oparg); +		break; +	case FUTEX_OP_XOR: +		__futex_atomic_op("xor %1, %0, %4", ret, oldval, uaddr, +				oparg); +		break; +	default: +		ret = -ENOSYS; +	} + +	pagefault_enable(); + +	if (ret) +		return ret; + +	switch (cmp) { +	case FUTEX_OP_CMP_EQ: return (oldval == cmparg); +	case FUTEX_OP_CMP_NE: return (oldval != cmparg); +	case FUTEX_OP_CMP_LT: return (oldval < cmparg); +	case FUTEX_OP_CMP_GE: return (oldval >= cmparg); +	case FUTEX_OP_CMP_LE: return (oldval <= cmparg); +	case FUTEX_OP_CMP_GT: return (oldval > cmparg); +	} + +	return -ENOSYS; +} + +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, +			      u32 oldval, u32 newval) +{ +	int ret = 0; +	u32 prev; + +	if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) +		return -EFAULT; + +#if !XCHAL_HAVE_S32C1I +	return -ENOSYS; +#endif + +	__asm__ __volatile__ ( +	"	# futex_atomic_cmpxchg_inatomic\n" +	"1:	l32i	%1, %3, 0\n" +	"	mov	%0, %5\n" +	"	wsr	%1, scompare1\n" +	"2:	s32c1i	%0, %3, 0\n" +	"3:\n" +	"	.section .fixup,\"ax\"\n" +	"	.align 4\n" +	"4:	.long	3b\n" +	"5:	l32r	%1, 4b\n" +	"	movi	%0, %6\n" +	"	jx	%1\n" +	"	.previous\n" +	"	.section __ex_table,\"a\"\n" +	"	.long 1b,5b,2b,5b\n" +	"	.previous\n" +	: "+r" (ret), "=&r" (prev), "+m" (*uaddr) +	: "r" (uaddr), "r" (oldval), "r" (newval), "I" (-EFAULT) +	: "memory"); + +	*uval = prev; +	return ret; +} + +#endif /* __KERNEL__ */ +#endif /* _ASM_XTENSA_FUTEX_H */  | 
