diff options
author | Baruch Siach <baruch@tkos.co.il> | 2013-11-17 23:46:43 -0500 |
---|---|---|
committer | Chris Zankel <chris@zankel.net> | 2014-01-14 13:19:51 -0500 |
commit | 220c062688c31bbf992921fca172ddd1e609f8f6 (patch) | |
tree | 1dcc29d05ab14df393f414b20bc9f29b28a1e137 /arch/xtensa/include | |
parent | a6e16b9aaf0df4b1de8853cad97f387ade9fa8f5 (diff) |
xtensa: implement robust futex atomic uaccess ops
This enables the set_robust_list(2) system call.
Signed-off-by: Baruch Siach <baruch@tkos.co.il>
Signed-off-by: Chris Zankel <chris@zankel.net>
Diffstat (limited to 'arch/xtensa/include')
-rw-r--r-- | arch/xtensa/include/asm/Kbuild | 1 | ||||
-rw-r--r-- | arch/xtensa/include/asm/futex.h | 147 |
2 files changed, 147 insertions, 1 deletions
diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild index 228d6aee3a16..5851db291583 100644 --- a/arch/xtensa/include/asm/Kbuild +++ b/arch/xtensa/include/asm/Kbuild | |||
@@ -8,7 +8,6 @@ generic-y += emergency-restart.h | |||
8 | generic-y += errno.h | 8 | generic-y += errno.h |
9 | generic-y += exec.h | 9 | generic-y += exec.h |
10 | generic-y += fcntl.h | 10 | generic-y += fcntl.h |
11 | generic-y += futex.h | ||
12 | generic-y += hardirq.h | 11 | generic-y += hardirq.h |
13 | generic-y += ioctl.h | 12 | generic-y += ioctl.h |
14 | generic-y += irq_regs.h | 13 | generic-y += irq_regs.h |
diff --git a/arch/xtensa/include/asm/futex.h b/arch/xtensa/include/asm/futex.h new file mode 100644 index 000000000000..b39531babec0 --- /dev/null +++ b/arch/xtensa/include/asm/futex.h | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * Atomic futex routines | ||
3 | * | ||
4 | * Based on the PowerPC implementataion | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * Copyright (C) 2013 TangoTec Ltd. | ||
11 | * | ||
12 | * Baruch Siach <baruch@tkos.co.il> | ||
13 | */ | ||
14 | |||
15 | #ifndef _ASM_XTENSA_FUTEX_H | ||
16 | #define _ASM_XTENSA_FUTEX_H | ||
17 | |||
18 | #ifdef __KERNEL__ | ||
19 | |||
20 | #include <linux/futex.h> | ||
21 | #include <linux/uaccess.h> | ||
22 | #include <linux/errno.h> | ||
23 | |||
24 | #define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ | ||
25 | __asm__ __volatile( \ | ||
26 | "1: l32i %0, %2, 0\n" \ | ||
27 | insn "\n" \ | ||
28 | " wsr %0, scompare1\n" \ | ||
29 | "2: s32c1i %1, %2, 0\n" \ | ||
30 | " bne %1, %0, 1b\n" \ | ||
31 | " movi %1, 0\n" \ | ||
32 | "3:\n" \ | ||
33 | " .section .fixup,\"ax\"\n" \ | ||
34 | " .align 4\n" \ | ||
35 | "4: .long 3b\n" \ | ||
36 | "5: l32r %0, 4b\n" \ | ||
37 | " movi %1, %3\n" \ | ||
38 | " jx %0\n" \ | ||
39 | " .previous\n" \ | ||
40 | " .section __ex_table,\"a\"\n" \ | ||
41 | " .long 1b,5b,2b,5b\n" \ | ||
42 | " .previous\n" \ | ||
43 | : "=&r" (oldval), "=&r" (ret) \ | ||
44 | : "r" (uaddr), "I" (-EFAULT), "r" (oparg) \ | ||
45 | : "memory") | ||
46 | |||
47 | static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) | ||
48 | { | ||
49 | int op = (encoded_op >> 28) & 7; | ||
50 | int cmp = (encoded_op >> 24) & 15; | ||
51 | int oparg = (encoded_op << 8) >> 20; | ||
52 | int cmparg = (encoded_op << 20) >> 20; | ||
53 | int oldval = 0, ret; | ||
54 | if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) | ||
55 | oparg = 1 << oparg; | ||
56 | |||
57 | if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) | ||
58 | return -EFAULT; | ||
59 | |||
60 | #if !XCHAL_HAVE_S32C1I | ||
61 | return -ENOSYS; | ||
62 | #endif | ||
63 | |||
64 | pagefault_disable(); | ||
65 | |||
66 | switch (op) { | ||
67 | case FUTEX_OP_SET: | ||
68 | __futex_atomic_op("mov %1, %4", ret, oldval, uaddr, oparg); | ||
69 | break; | ||
70 | case FUTEX_OP_ADD: | ||
71 | __futex_atomic_op("add %1, %0, %4", ret, oldval, uaddr, | ||
72 | oparg); | ||
73 | break; | ||
74 | case FUTEX_OP_OR: | ||
75 | __futex_atomic_op("or %1, %0, %4", ret, oldval, uaddr, | ||
76 | oparg); | ||
77 | break; | ||
78 | case FUTEX_OP_ANDN: | ||
79 | __futex_atomic_op("and %1, %0, %4", ret, oldval, uaddr, | ||
80 | ~oparg); | ||
81 | break; | ||
82 | case FUTEX_OP_XOR: | ||
83 | __futex_atomic_op("xor %1, %0, %4", ret, oldval, uaddr, | ||
84 | oparg); | ||
85 | break; | ||
86 | default: | ||
87 | ret = -ENOSYS; | ||
88 | } | ||
89 | |||
90 | pagefault_enable(); | ||
91 | |||
92 | if (ret) | ||
93 | return ret; | ||
94 | |||
95 | switch (cmp) { | ||
96 | case FUTEX_OP_CMP_EQ: return (oldval == cmparg); | ||
97 | case FUTEX_OP_CMP_NE: return (oldval != cmparg); | ||
98 | case FUTEX_OP_CMP_LT: return (oldval < cmparg); | ||
99 | case FUTEX_OP_CMP_GE: return (oldval >= cmparg); | ||
100 | case FUTEX_OP_CMP_LE: return (oldval <= cmparg); | ||
101 | case FUTEX_OP_CMP_GT: return (oldval > cmparg); | ||
102 | } | ||
103 | |||
104 | return -ENOSYS; | ||
105 | } | ||
106 | |||
107 | static inline int | ||
108 | futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, | ||
109 | u32 oldval, u32 newval) | ||
110 | { | ||
111 | int ret = 0; | ||
112 | u32 prev; | ||
113 | |||
114 | if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) | ||
115 | return -EFAULT; | ||
116 | |||
117 | #if !XCHAL_HAVE_S32C1I | ||
118 | return -ENOSYS; | ||
119 | #endif | ||
120 | |||
121 | __asm__ __volatile__ ( | ||
122 | " # futex_atomic_cmpxchg_inatomic\n" | ||
123 | "1: l32i %1, %3, 0\n" | ||
124 | " mov %0, %5\n" | ||
125 | " wsr %1, scompare1\n" | ||
126 | "2: s32c1i %0, %3, 0\n" | ||
127 | "3:\n" | ||
128 | " .section .fixup,\"ax\"\n" | ||
129 | " .align 4\n" | ||
130 | "4: .long 3b\n" | ||
131 | "5: l32r %1, 4b\n" | ||
132 | " movi %0, %6\n" | ||
133 | " jx %1\n" | ||
134 | " .previous\n" | ||
135 | " .section __ex_table,\"a\"\n" | ||
136 | " .long 1b,5b,2b,5b\n" | ||
137 | " .previous\n" | ||
138 | : "+r" (ret), "=&r" (prev), "+m" (*uaddr) | ||
139 | : "r" (uaddr), "r" (oldval), "r" (newval), "I" (-EFAULT) | ||
140 | : "memory"); | ||
141 | |||
142 | *uval = prev; | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | #endif /* __KERNEL__ */ | ||
147 | #endif /* _ASM_XTENSA_FUTEX_H */ | ||