Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[sfrench/cifs-2.6.git] / arch / arm / include / asm / futex.h
1 #ifndef _ASM_ARM_FUTEX_H
2 #define _ASM_ARM_FUTEX_H
3
4 #ifdef __KERNEL__
5
6 #include <linux/futex.h>
7 #include <linux/uaccess.h>
8 #include <asm/errno.h>
9
10 #define __futex_atomic_ex_table(err_reg)                        \
11         "3:\n"                                                  \
12         "       .pushsection __ex_table,\"a\"\n"                \
13         "       .align  3\n"                                    \
14         "       .long   1b, 4f, 2b, 4f\n"                       \
15         "       .popsection\n"                                  \
16         "       .pushsection .text.fixup,\"ax\"\n"              \
17         "       .align  2\n"                                    \
18         "4:     mov     %0, " err_reg "\n"                      \
19         "       b       3b\n"                                   \
20         "       .popsection"
21
22 #ifdef CONFIG_SMP
23
24 #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
25 ({                                                              \
26         unsigned int __ua_flags;                                \
27         smp_mb();                                               \
28         prefetchw(uaddr);                                       \
29         __ua_flags = uaccess_save_and_enable();                 \
30         __asm__ __volatile__(                                   \
31         "1:     ldrex   %1, [%3]\n"                             \
32         "       " insn "\n"                                     \
33         "2:     strex   %2, %0, [%3]\n"                         \
34         "       teq     %2, #0\n"                               \
35         "       bne     1b\n"                                   \
36         "       mov     %0, #0\n"                               \
37         __futex_atomic_ex_table("%5")                           \
38         : "=&r" (ret), "=&r" (oldval), "=&r" (tmp)              \
39         : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT)              \
40         : "cc", "memory");                                      \
41         uaccess_restore(__ua_flags);                            \
42 })
43
44 static inline int
45 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
46                               u32 oldval, u32 newval)
47 {
48         unsigned int __ua_flags;
49         int ret;
50         u32 val;
51
52         if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
53                 return -EFAULT;
54
55         smp_mb();
56         /* Prefetching cannot fault */
57         prefetchw(uaddr);
58         __ua_flags = uaccess_save_and_enable();
59         __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
60         "1:     ldrex   %1, [%4]\n"
61         "       teq     %1, %2\n"
62         "       ite     eq      @ explicit IT needed for the 2b label\n"
63         "2:     strexeq %0, %3, [%4]\n"
64         "       movne   %0, #0\n"
65         "       teq     %0, #0\n"
66         "       bne     1b\n"
67         __futex_atomic_ex_table("%5")
68         : "=&r" (ret), "=&r" (val)
69         : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
70         : "cc", "memory");
71         uaccess_restore(__ua_flags);
72         smp_mb();
73
74         *uval = val;
75         return ret;
76 }
77
78 #else /* !SMP, we can work around lack of atomic ops by disabling preemption */
79
80 #include <linux/preempt.h>
81 #include <asm/domain.h>
82
83 #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
84 ({                                                              \
85         unsigned int __ua_flags = uaccess_save_and_enable();    \
86         __asm__ __volatile__(                                   \
87         "1:     " TUSER(ldr) "  %1, [%3]\n"                     \
88         "       " insn "\n"                                     \
89         "2:     " TUSER(str) "  %0, [%3]\n"                     \
90         "       mov     %0, #0\n"                               \
91         __futex_atomic_ex_table("%5")                           \
92         : "=&r" (ret), "=&r" (oldval), "=&r" (tmp)              \
93         : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT)              \
94         : "cc", "memory");                                      \
95         uaccess_restore(__ua_flags);                            \
96 })
97
98 static inline int
99 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
100                               u32 oldval, u32 newval)
101 {
102         unsigned int __ua_flags;
103         int ret = 0;
104         u32 val;
105
106         if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
107                 return -EFAULT;
108
109         preempt_disable();
110         __ua_flags = uaccess_save_and_enable();
111         __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
112         "1:     " TUSER(ldr) "  %1, [%4]\n"
113         "       teq     %1, %2\n"
114         "       it      eq      @ explicit IT needed for the 2b label\n"
115         "2:     " TUSER(streq) "        %3, [%4]\n"
116         __futex_atomic_ex_table("%5")
117         : "+r" (ret), "=&r" (val)
118         : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
119         : "cc", "memory");
120         uaccess_restore(__ua_flags);
121
122         *uval = val;
123         preempt_enable();
124
125         return ret;
126 }
127
128 #endif /* !SMP */
129
130 static inline int
131 futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
132 {
133         int op = (encoded_op >> 28) & 7;
134         int cmp = (encoded_op >> 24) & 15;
135         int oparg = (encoded_op << 8) >> 20;
136         int cmparg = (encoded_op << 20) >> 20;
137         int oldval = 0, ret, tmp;
138
139         if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
140                 oparg = 1 << oparg;
141
142         if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
143                 return -EFAULT;
144
145 #ifndef CONFIG_SMP
146         preempt_disable();
147 #endif
148         pagefault_disable();
149
150         switch (op) {
151         case FUTEX_OP_SET:
152                 __futex_atomic_op("mov  %0, %4", ret, oldval, tmp, uaddr, oparg);
153                 break;
154         case FUTEX_OP_ADD:
155                 __futex_atomic_op("add  %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
156                 break;
157         case FUTEX_OP_OR:
158                 __futex_atomic_op("orr  %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
159                 break;
160         case FUTEX_OP_ANDN:
161                 __futex_atomic_op("and  %0, %1, %4", ret, oldval, tmp, uaddr, ~oparg);
162                 break;
163         case FUTEX_OP_XOR:
164                 __futex_atomic_op("eor  %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
165                 break;
166         default:
167                 ret = -ENOSYS;
168         }
169
170         pagefault_enable();
171 #ifndef CONFIG_SMP
172         preempt_enable();
173 #endif
174
175         if (!ret) {
176                 switch (cmp) {
177                 case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
178                 case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
179                 case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
180                 case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
181                 case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
182                 case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
183                 default: ret = -ENOSYS;
184                 }
185         }
186         return ret;
187 }
188
189 #endif /* __KERNEL__ */
190 #endif /* _ASM_ARM_FUTEX_H */