MIPS: Make set_pte() SMP safe.
[pandora-kernel.git] / arch / mips / include / asm / futex.h
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (c) 2006  Ralf Baechle (ralf@linux-mips.org)
7  */
8 #ifndef _ASM_FUTEX_H
9 #define _ASM_FUTEX_H
10
11 #ifdef __KERNEL__
12
13 #include <linux/futex.h>
14 #include <linux/uaccess.h>
15 #include <asm/barrier.h>
16 #include <asm/errno.h>
17 #include <asm/war.h>
18
19 #define __futex_atomic_op(insn, ret, oldval, uaddr, oparg)              \
20 {                                                                       \
21         if (cpu_has_llsc && R10000_LLSC_WAR) {                          \
22                 __asm__ __volatile__(                                   \
23                 "       .set    push                            \n"     \
24                 "       .set    noat                            \n"     \
25                 "       .set    mips3                           \n"     \
26                 "1:     ll      %1, %4  # __futex_atomic_op     \n"     \
27                 "       .set    mips0                           \n"     \
28                 "       " insn  "                               \n"     \
29                 "       .set    mips3                           \n"     \
30                 "2:     sc      $1, %2                          \n"     \
31                 "       beqzl   $1, 1b                          \n"     \
32                 __WEAK_LLSC_MB                                          \
33                 "3:                                             \n"     \
34                 "       .set    pop                             \n"     \
35                 "       .set    mips0                           \n"     \
36                 "       .section .fixup,\"ax\"                  \n"     \
37                 "4:     li      %0, %6                          \n"     \
38                 "       j       3b                              \n"     \
39                 "       .previous                               \n"     \
40                 "       .section __ex_table,\"a\"               \n"     \
41                 "       "__UA_ADDR "\t1b, 4b                    \n"     \
42                 "       "__UA_ADDR "\t2b, 4b                    \n"     \
43                 "       .previous                               \n"     \
44                 : "=r" (ret), "=&r" (oldval), "=R" (*uaddr)             \
45                 : "0" (0), "R" (*uaddr), "Jr" (oparg), "i" (-EFAULT)    \
46                 : "memory");                                            \
47         } else if (cpu_has_llsc) {                                      \
48                 __asm__ __volatile__(                                   \
49                 "       .set    push                            \n"     \
50                 "       .set    noat                            \n"     \
51                 "       .set    mips3                           \n"     \
52                 "1:     ll      %1, %4  # __futex_atomic_op     \n"     \
53                 "       .set    mips0                           \n"     \
54                 "       " insn  "                               \n"     \
55                 "       .set    mips3                           \n"     \
56                 "2:     sc      $1, %2                          \n"     \
57                 "       beqz    $1, 1b                          \n"     \
58                 __WEAK_LLSC_MB                                          \
59                 "3:                                             \n"     \
60                 "       .set    pop                             \n"     \
61                 "       .set    mips0                           \n"     \
62                 "       .section .fixup,\"ax\"                  \n"     \
63                 "4:     li      %0, %6                          \n"     \
64                 "       j       3b                              \n"     \
65                 "       .previous                               \n"     \
66                 "       .section __ex_table,\"a\"               \n"     \
67                 "       "__UA_ADDR "\t1b, 4b                    \n"     \
68                 "       "__UA_ADDR "\t2b, 4b                    \n"     \
69                 "       .previous                               \n"     \
70                 : "=r" (ret), "=&r" (oldval), "=R" (*uaddr)             \
71                 : "0" (0), "R" (*uaddr), "Jr" (oparg), "i" (-EFAULT)    \
72                 : "memory");                                            \
73         } else                                                          \
74                 ret = -ENOSYS;                                          \
75 }
76
77 static inline int
78 futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
79 {
80         int op = (encoded_op >> 28) & 7;
81         int cmp = (encoded_op >> 24) & 15;
82         int oparg = (encoded_op << 8) >> 20;
83         int cmparg = (encoded_op << 20) >> 20;
84         int oldval = 0, ret;
85         if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
86                 oparg = 1 << oparg;
87
88         if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
89                 return -EFAULT;
90
91         pagefault_disable();
92
93         switch (op) {
94         case FUTEX_OP_SET:
95                 __futex_atomic_op("move $1, %z5", ret, oldval, uaddr, oparg);
96                 break;
97
98         case FUTEX_OP_ADD:
99                 __futex_atomic_op("addu $1, %1, %z5",
100                                   ret, oldval, uaddr, oparg);
101                 break;
102         case FUTEX_OP_OR:
103                 __futex_atomic_op("or   $1, %1, %z5",
104                                   ret, oldval, uaddr, oparg);
105                 break;
106         case FUTEX_OP_ANDN:
107                 __futex_atomic_op("and  $1, %1, %z5",
108                                   ret, oldval, uaddr, ~oparg);
109                 break;
110         case FUTEX_OP_XOR:
111                 __futex_atomic_op("xor  $1, %1, %z5",
112                                   ret, oldval, uaddr, oparg);
113                 break;
114         default:
115                 ret = -ENOSYS;
116         }
117
118         pagefault_enable();
119
120         if (!ret) {
121                 switch (cmp) {
122                 case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
123                 case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
124                 case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
125                 case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
126                 case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
127                 case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
128                 default: ret = -ENOSYS;
129                 }
130         }
131         return ret;
132 }
133
134 static inline int
135 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
136                               u32 oldval, u32 newval)
137 {
138         int ret = 0;
139         u32 val;
140
141         if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
142                 return -EFAULT;
143
144         if (cpu_has_llsc && R10000_LLSC_WAR) {
145                 __asm__ __volatile__(
146                 "# futex_atomic_cmpxchg_inatomic                        \n"
147                 "       .set    push                                    \n"
148                 "       .set    noat                                    \n"
149                 "       .set    mips3                                   \n"
150                 "1:     ll      %1, %3                                  \n"
151                 "       bne     %1, %z4, 3f                             \n"
152                 "       .set    mips0                                   \n"
153                 "       move    $1, %z5                                 \n"
154                 "       .set    mips3                                   \n"
155                 "2:     sc      $1, %2                                  \n"
156                 "       beqzl   $1, 1b                                  \n"
157                 __WEAK_LLSC_MB
158                 "3:                                                     \n"
159                 "       .set    pop                                     \n"
160                 "       .section .fixup,\"ax\"                          \n"
161                 "4:     li      %0, %6                                  \n"
162                 "       j       3b                                      \n"
163                 "       .previous                                       \n"
164                 "       .section __ex_table,\"a\"                       \n"
165                 "       "__UA_ADDR "\t1b, 4b                            \n"
166                 "       "__UA_ADDR "\t2b, 4b                            \n"
167                 "       .previous                                       \n"
168                 : "+r" (ret), "=&r" (val), "=R" (*uaddr)
169                 : "R" (*uaddr), "Jr" (oldval), "Jr" (newval), "i" (-EFAULT)
170                 : "memory");
171         } else if (cpu_has_llsc) {
172                 __asm__ __volatile__(
173                 "# futex_atomic_cmpxchg_inatomic                        \n"
174                 "       .set    push                                    \n"
175                 "       .set    noat                                    \n"
176                 "       .set    mips3                                   \n"
177                 "1:     ll      %1, %3                                  \n"
178                 "       bne     %1, %z4, 3f                             \n"
179                 "       .set    mips0                                   \n"
180                 "       move    $1, %z5                                 \n"
181                 "       .set    mips3                                   \n"
182                 "2:     sc      $1, %2                                  \n"
183                 "       beqz    $1, 1b                                  \n"
184                 __WEAK_LLSC_MB
185                 "3:                                                     \n"
186                 "       .set    pop                                     \n"
187                 "       .section .fixup,\"ax\"                          \n"
188                 "4:     li      %0, %6                                  \n"
189                 "       j       3b                                      \n"
190                 "       .previous                                       \n"
191                 "       .section __ex_table,\"a\"                       \n"
192                 "       "__UA_ADDR "\t1b, 4b                            \n"
193                 "       "__UA_ADDR "\t2b, 4b                            \n"
194                 "       .previous                                       \n"
195                 : "+r" (ret), "=&r" (val), "=R" (*uaddr)
196                 : "R" (*uaddr), "Jr" (oldval), "Jr" (newval), "i" (-EFAULT)
197                 : "memory");
198         } else
199                 return -ENOSYS;
200
201         *uval = val;
202         return ret;
203 }
204
205 #endif
206 #endif /* _ASM_FUTEX_H */