Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
futex.h
Go to the documentation of this file.
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 ([email protected])
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"
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"
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 */