| @@ -1,632 +1,634 @@ | | | @@ -1,632 +1,634 @@ |
1 | /* $NetBSD: linux_futex.c,v 1.18.4.1 2009/02/26 20:40:52 snj Exp $ */ | | 1 | /* $NetBSD: linux_futex.c,v 1.18.4.2 2009/03/16 01:20:37 snj Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2005 Emmanuel Dreyfus, all rights reserved. | | 4 | * Copyright (c) 2005 Emmanuel Dreyfus, all rights reserved. |
5 | * | | 5 | * |
6 | * Redistribution and use in source and binary forms, with or without | | 6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions | | 7 | * modification, are permitted provided that the following conditions |
8 | * are met: | | 8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright | | 9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. | | 10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright | | 11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the | | 12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. | | 13 | * documentation and/or other materials provided with the distribution. |
14 | * 3. All advertising materials mentioning features or use of this software | | 14 | * 3. All advertising materials mentioning features or use of this software |
15 | * must display the following acknowledgement: | | 15 | * must display the following acknowledgement: |
16 | * This product includes software developed by Emmanuel Dreyfus | | 16 | * This product includes software developed by Emmanuel Dreyfus |
17 | * 4. The name of the author may not be used to endorse or promote | | 17 | * 4. The name of the author may not be used to endorse or promote |
18 | * products derived from this software without specific prior written | | 18 | * products derived from this software without specific prior written |
19 | * permission. | | 19 | * permission. |
20 | * | | 20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR AND CONTRIBUTORS ``AS IS'' | | 21 | * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR AND CONTRIBUTORS ``AS IS'' |
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | | 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
23 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 23 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS | | 24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS |
25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 | * POSSIBILITY OF SUCH DAMAGE. | | 31 | * POSSIBILITY OF SUCH DAMAGE. |
32 | */ | | 32 | */ |
33 | | | 33 | |
34 | #include <sys/cdefs.h> | | 34 | #include <sys/cdefs.h> |
35 | __KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.18.4.1 2009/02/26 20:40:52 snj Exp $"); | | 35 | __KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.18.4.2 2009/03/16 01:20:37 snj Exp $"); |
36 | | | 36 | |
37 | #include <sys/param.h> | | 37 | #include <sys/param.h> |
38 | #include <sys/time.h> | | 38 | #include <sys/time.h> |
39 | #include <sys/systm.h> | | 39 | #include <sys/systm.h> |
40 | #include <sys/proc.h> | | 40 | #include <sys/proc.h> |
41 | #include <sys/lwp.h> | | 41 | #include <sys/lwp.h> |
42 | #include <sys/queue.h> | | 42 | #include <sys/queue.h> |
43 | #include <sys/condvar.h> | | 43 | #include <sys/condvar.h> |
44 | #include <sys/mutex.h> | | 44 | #include <sys/mutex.h> |
45 | #include <sys/once.h> | | 45 | #include <sys/once.h> |
46 | #include <sys/kmem.h> | | 46 | #include <sys/kmem.h> |
47 | #include <sys/kernel.h> | | 47 | #include <sys/kernel.h> |
48 | #include <sys/atomic.h> | | 48 | #include <sys/atomic.h> |
49 | | | 49 | |
50 | #include <compat/linux/common/linux_types.h> | | 50 | #include <compat/linux/common/linux_types.h> |
51 | #include <compat/linux/common/linux_emuldata.h> | | 51 | #include <compat/linux/common/linux_emuldata.h> |
52 | #include <compat/linux/common/linux_exec.h> | | 52 | #include <compat/linux/common/linux_exec.h> |
53 | #include <compat/linux/common/linux_signal.h> | | 53 | #include <compat/linux/common/linux_signal.h> |
54 | #include <compat/linux/common/linux_futex.h> | | 54 | #include <compat/linux/common/linux_futex.h> |
55 | #include <compat/linux/common/linux_ipc.h> | | 55 | #include <compat/linux/common/linux_ipc.h> |
56 | #include <compat/linux/common/linux_sem.h> | | 56 | #include <compat/linux/common/linux_sem.h> |
57 | #include <compat/linux/linux_syscallargs.h> | | 57 | #include <compat/linux/linux_syscallargs.h> |
58 | | | 58 | |
59 | struct futex; | | 59 | struct futex; |
60 | | | 60 | |
61 | struct waiting_proc { | | 61 | struct waiting_proc { |
62 | lwp_t *wp_l; | | 62 | lwp_t *wp_l; |
63 | struct futex *wp_new_futex; | | 63 | struct futex *wp_new_futex; |
64 | kcondvar_t wp_futex_cv; | | 64 | kcondvar_t wp_futex_cv; |
65 | TAILQ_ENTRY(waiting_proc) wp_list; | | 65 | TAILQ_ENTRY(waiting_proc) wp_list; |
66 | }; | | 66 | }; |
67 | struct futex { | | 67 | struct futex { |
68 | void *f_uaddr; | | 68 | void *f_uaddr; |
69 | int f_refcount; | | 69 | int f_refcount; |
70 | LIST_ENTRY(futex) f_list; | | 70 | LIST_ENTRY(futex) f_list; |
71 | TAILQ_HEAD(lf_waiting_proc, waiting_proc) f_waiting_proc; | | 71 | TAILQ_HEAD(lf_waiting_proc, waiting_proc) f_waiting_proc; |
72 | }; | | 72 | }; |
73 | | | 73 | |
74 | static LIST_HEAD(futex_list, futex) futex_list; | | 74 | static LIST_HEAD(futex_list, futex) futex_list; |
75 | static kmutex_t futex_lock; | | 75 | static kmutex_t futex_lock; |
76 | | | 76 | |
77 | #define FUTEX_LOCK mutex_enter(&futex_lock); | | 77 | #define FUTEX_LOCK mutex_enter(&futex_lock); |
78 | #define FUTEX_UNLOCK mutex_exit(&futex_lock); | | 78 | #define FUTEX_UNLOCK mutex_exit(&futex_lock); |
79 | | | 79 | |
80 | #define FUTEX_LOCKED 1 | | 80 | #define FUTEX_LOCKED 1 |
81 | #define FUTEX_UNLOCKED 0 | | 81 | #define FUTEX_UNLOCKED 0 |
82 | | | 82 | |
83 | #define FUTEX_SYSTEM_LOCK KERNEL_LOCK(1, NULL); | | 83 | #define FUTEX_SYSTEM_LOCK KERNEL_LOCK(1, NULL); |
84 | #define FUTEX_SYSTEM_UNLOCK KERNEL_UNLOCK_ONE(0); | | 84 | #define FUTEX_SYSTEM_UNLOCK KERNEL_UNLOCK_ONE(0); |
85 | | | 85 | |
86 | #ifdef DEBUG_LINUX_FUTEX | | 86 | #ifdef DEBUG_LINUX_FUTEX |
87 | #define FUTEXPRINTF(a) printf a | | 87 | #define FUTEXPRINTF(a) printf a |
88 | #else | | 88 | #else |
89 | #define FUTEXPRINTF(a) | | 89 | #define FUTEXPRINTF(a) |
90 | #endif | | 90 | #endif |
91 | | | 91 | |
92 | static ONCE_DECL(futex_once); | | 92 | static ONCE_DECL(futex_once); |
93 | | | 93 | |
94 | static int | | 94 | static int |
95 | futex_init(void) | | 95 | futex_init(void) |
96 | { | | 96 | { |
97 | printf("futex_init: initializing futex\n"); | | 97 | printf("futex_init: initializing futex\n"); |
98 | mutex_init(&futex_lock, MUTEX_DEFAULT, IPL_NONE); | | 98 | mutex_init(&futex_lock, MUTEX_DEFAULT, IPL_NONE); |
99 | return 0; | | 99 | return 0; |
100 | } | | 100 | } |
101 | | | 101 | |
102 | static struct futex *futex_get(void *, int); | | 102 | static struct futex *futex_get(void *, int); |
103 | static void futex_put(struct futex *); | | 103 | static void futex_put(struct futex *); |
104 | static int futex_sleep(struct futex *, lwp_t *, unsigned long); | | 104 | static int futex_sleep(struct futex *, lwp_t *, unsigned long); |
105 | static int futex_wake(struct futex *, int, struct futex *, int); | | 105 | static int futex_wake(struct futex *, int, struct futex *, int); |
106 | static int futex_atomic_op(lwp_t *, int, void *); | | 106 | static int futex_atomic_op(lwp_t *, int, void *); |
107 | | | 107 | |
108 | int | | 108 | int |
109 | linux_sys_futex(struct lwp *l, const struct linux_sys_futex_args *uap, register_t *retval) | | 109 | linux_sys_futex(struct lwp *l, const struct linux_sys_futex_args *uap, register_t *retval) |
110 | { | | 110 | { |
111 | /* { | | 111 | /* { |
112 | syscallarg(int *) uaddr; | | 112 | syscallarg(int *) uaddr; |
113 | syscallarg(int) op; | | 113 | syscallarg(int) op; |
114 | syscallarg(int) val; | | 114 | syscallarg(int) val; |
115 | syscallarg(const struct timespec *) timeout; | | 115 | syscallarg(const struct timespec *) timeout; |
116 | syscallarg(int *) uaddr2; | | 116 | syscallarg(int *) uaddr2; |
117 | syscallarg(int) val3; | | 117 | syscallarg(int) val3; |
118 | } */ | | 118 | } */ |
119 | int val; | | 119 | int val; |
120 | int ret; | | 120 | int ret; |
121 | struct timespec timeout = { 0, 0 }; | | 121 | struct timespec timeout = { 0, 0 }; |
122 | int error = 0; | | 122 | int error = 0; |
123 | struct futex *f; | | 123 | struct futex *f; |
124 | struct futex *newf; | | 124 | struct futex *newf; |
125 | int timeout_hz; | | 125 | int timeout_hz; |
126 | struct timeval tv = {0, 0}; | | 126 | struct timeval tv = {0, 0}; |
127 | struct futex *f2; | | 127 | struct futex *f2; |
128 | int op_ret; | | 128 | int op_ret; |
129 | | | 129 | |
130 | RUN_ONCE(&futex_once, futex_init); | | 130 | RUN_ONCE(&futex_once, futex_init); |
131 | | | 131 | |
132 | /* | | 132 | /* |
133 | * Our implementation provides only private futexes. Most of the apps | | 133 | * Our implementation provides only private futexes. Most of the apps |
134 | * should use private futexes but don't claim so. Therefore we treat | | 134 | * should use private futexes but don't claim so. Therefore we treat |
135 | * all futexes as private by clearing the FUTEX_PRIVATE_FLAG. It works | | 135 | * all futexes as private by clearing the FUTEX_PRIVATE_FLAG. It works |
136 | * in most cases (ie. when futexes are not shared on file descriptor | | 136 | * in most cases (ie. when futexes are not shared on file descriptor |
137 | * or between different processes). | | 137 | * or between different processes). |
138 | */ | | 138 | */ |
139 | switch (SCARG(uap, op) & ~LINUX_FUTEX_PRIVATE_FLAG) { | | 139 | switch (SCARG(uap, op) & ~LINUX_FUTEX_PRIVATE_FLAG) { |
140 | case LINUX_FUTEX_WAIT: | | 140 | case LINUX_FUTEX_WAIT: |
141 | FUTEX_SYSTEM_LOCK; | | 141 | FUTEX_SYSTEM_LOCK; |
142 | | | 142 | |
143 | if ((error = copyin(SCARG(uap, uaddr), | | 143 | if ((error = copyin(SCARG(uap, uaddr), |
144 | &val, sizeof(val))) != 0) { | | 144 | &val, sizeof(val))) != 0) { |
145 | FUTEX_SYSTEM_UNLOCK; | | 145 | FUTEX_SYSTEM_UNLOCK; |
146 | return error; | | 146 | return error; |
147 | } | | 147 | } |
148 | | | 148 | |
149 | if (val != SCARG(uap, val)) { | | 149 | if (val != SCARG(uap, val)) { |
150 | FUTEX_SYSTEM_UNLOCK; | | 150 | FUTEX_SYSTEM_UNLOCK; |
151 | return EWOULDBLOCK; | | 151 | return EWOULDBLOCK; |
152 | } | | 152 | } |
153 | | | 153 | |
154 | if (SCARG(uap, timeout) != NULL) { | | 154 | if (SCARG(uap, timeout) != NULL) { |
155 | if ((error = copyin(SCARG(uap, timeout), | | 155 | if ((error = copyin(SCARG(uap, timeout), |
156 | &timeout, sizeof(timeout))) != 0) { | | 156 | &timeout, sizeof(timeout))) != 0) { |
157 | FUTEX_SYSTEM_UNLOCK; | | 157 | FUTEX_SYSTEM_UNLOCK; |
158 | return error; | | 158 | return error; |
159 | } | | 159 | } |
160 | } | | 160 | } |
161 | | | 161 | |
162 | FUTEXPRINTF(("FUTEX_WAIT %d.%d: val = %d, uaddr = %p, " | | 162 | FUTEXPRINTF(("FUTEX_WAIT %d.%d: val = %d, uaddr = %p, " |
163 | "*uaddr = %d, timeout = %lld.%09ld\n", | | 163 | "*uaddr = %d, timeout = %lld.%09ld\n", |
164 | l->l_proc->p_pid, l->l_lid, SCARG(uap, val), | | 164 | l->l_proc->p_pid, l->l_lid, SCARG(uap, val), |
165 | SCARG(uap, uaddr), val, (long long)timeout.tv_sec, | | 165 | SCARG(uap, uaddr), val, (long long)timeout.tv_sec, |
166 | timeout.tv_nsec)); | | 166 | timeout.tv_nsec)); |
167 | | | 167 | |
168 | tv.tv_usec = timeout.tv_sec * 1000000 + timeout.tv_nsec / 1000; | | 168 | tv.tv_usec = timeout.tv_sec * 1000000 + timeout.tv_nsec / 1000; |
169 | timeout_hz = tvtohz(&tv); | | 169 | timeout_hz = tvtohz(&tv); |
170 | | | 170 | |
171 | if (timeout.tv_sec == 0 && timeout.tv_nsec == 0) | | 171 | if (timeout.tv_sec == 0 && timeout.tv_nsec == 0) |
172 | timeout_hz = 0; | | 172 | timeout_hz = 0; |
173 | | | 173 | |
174 | /* | | 174 | /* |
175 | * If the user process requests a non null timeout, | | 175 | * If the user process requests a non null timeout, |
176 | * make sure we do not turn it into an infinite | | 176 | * make sure we do not turn it into an infinite |
177 | * timeout because timeout_hz is 0. | | 177 | * timeout because timeout_hz is 0. |
178 | * | | 178 | * |
179 | * We use a minimal timeout of 1/hz. Maybe it would make | | 179 | * We use a minimal timeout of 1/hz. Maybe it would make |
180 | * sense to just return ETIMEDOUT without sleeping. | | 180 | * sense to just return ETIMEDOUT without sleeping. |
181 | */ | | 181 | */ |
182 | if (((timeout.tv_sec != 0) || (timeout.tv_nsec != 0)) && | | 182 | if (((timeout.tv_sec != 0) || (timeout.tv_nsec != 0)) && |
183 | (timeout_hz == 0)) | | 183 | (timeout_hz == 0)) |
184 | timeout_hz = 1; | | 184 | timeout_hz = 1; |
185 | | | 185 | |
186 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); | | 186 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); |
187 | ret = futex_sleep(f, l, timeout_hz); | | 187 | ret = futex_sleep(f, l, timeout_hz); |
188 | futex_put(f); | | 188 | futex_put(f); |
189 | | | 189 | |
190 | FUTEXPRINTF(("FUTEX_WAIT %d.%d: uaddr = %p, " | | 190 | FUTEXPRINTF(("FUTEX_WAIT %d.%d: uaddr = %p, " |
191 | "ret = %d\n", l->l_proc->p_pid, l->l_lid, | | 191 | "ret = %d\n", l->l_proc->p_pid, l->l_lid, |
192 | SCARG(uap, uaddr), ret)); | | 192 | SCARG(uap, uaddr), ret)); |
193 | | | 193 | |
194 | FUTEX_SYSTEM_UNLOCK; | | 194 | FUTEX_SYSTEM_UNLOCK; |
195 | switch (ret) { | | 195 | switch (ret) { |
196 | case EWOULDBLOCK: /* timeout */ | | 196 | case EWOULDBLOCK: /* timeout */ |
197 | return ETIMEDOUT; | | 197 | return ETIMEDOUT; |
198 | break; | | 198 | break; |
199 | case EINTR: /* signal */ | | 199 | case EINTR: /* signal */ |
200 | return EINTR; | | 200 | return EINTR; |
201 | break; | | 201 | break; |
202 | case 0: /* FUTEX_WAKE received */ | | 202 | case 0: /* FUTEX_WAKE received */ |
203 | FUTEXPRINTF(("FUTEX_WAIT %d.%d: uaddr = %p, got it\n", | | 203 | FUTEXPRINTF(("FUTEX_WAIT %d.%d: uaddr = %p, got it\n", |
204 | l->l_proc->p_pid, l->l_lid, SCARG(uap, uaddr))); | | 204 | l->l_proc->p_pid, l->l_lid, SCARG(uap, uaddr))); |
205 | return 0; | | 205 | return 0; |
206 | break; | | 206 | break; |
207 | default: | | 207 | default: |
208 | FUTEXPRINTF(("FUTEX_WAIT: unexpected ret = %d\n", ret)); | | 208 | FUTEXPRINTF(("FUTEX_WAIT: unexpected ret = %d\n", ret)); |
209 | break; | | 209 | break; |
210 | } | | 210 | } |
211 | | | 211 | |
212 | /* NOTREACHED */ | | 212 | /* NOTREACHED */ |
213 | break; | | 213 | break; |
214 | | | 214 | |
215 | case LINUX_FUTEX_WAKE: | | 215 | case LINUX_FUTEX_WAKE: |
216 | FUTEX_SYSTEM_LOCK; | | 216 | FUTEX_SYSTEM_LOCK; |
217 | /* | | 217 | /* |
218 | * XXX: Linux is able cope with different addresses | | 218 | * XXX: Linux is able cope with different addresses |
219 | * corresponding to the same mapped memory in the sleeping | | 219 | * corresponding to the same mapped memory in the sleeping |
220 | * and the waker process(es). | | 220 | * and the waker process(es). |
221 | */ | | 221 | */ |
222 | FUTEXPRINTF(("FUTEX_WAKE %d.%d: uaddr = %p, val = %d\n", | | 222 | FUTEXPRINTF(("FUTEX_WAKE %d.%d: uaddr = %p, val = %d\n", |
223 | l->l_proc->p_pid, l->l_lid, | | 223 | l->l_proc->p_pid, l->l_lid, |
224 | SCARG(uap, uaddr), SCARG(uap, val))); | | 224 | SCARG(uap, uaddr), SCARG(uap, val))); |
225 | | | 225 | |
226 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); | | 226 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); |
227 | *retval = futex_wake(f, SCARG(uap, val), NULL, 0); | | 227 | *retval = futex_wake(f, SCARG(uap, val), NULL, 0); |
228 | futex_put(f); | | 228 | futex_put(f); |
229 | | | 229 | |
230 | FUTEX_SYSTEM_UNLOCK; | | 230 | FUTEX_SYSTEM_UNLOCK; |
231 | | | 231 | |
232 | break; | | 232 | break; |
233 | | | 233 | |
234 | case LINUX_FUTEX_CMP_REQUEUE: | | 234 | case LINUX_FUTEX_CMP_REQUEUE: |
235 | FUTEX_SYSTEM_LOCK; | | 235 | FUTEX_SYSTEM_LOCK; |
236 | | | 236 | |
237 | if ((error = copyin(SCARG(uap, uaddr), | | 237 | if ((error = copyin(SCARG(uap, uaddr), |
238 | &val, sizeof(val))) != 0) { | | 238 | &val, sizeof(val))) != 0) { |
239 | FUTEX_SYSTEM_UNLOCK; | | 239 | FUTEX_SYSTEM_UNLOCK; |
240 | return error; | | 240 | return error; |
241 | } | | 241 | } |
242 | | | 242 | |
243 | if (val != SCARG(uap, val3)) { | | 243 | if (val != SCARG(uap, val3)) { |
244 | FUTEX_SYSTEM_UNLOCK; | | 244 | FUTEX_SYSTEM_UNLOCK; |
245 | return EAGAIN; | | 245 | return EAGAIN; |
246 | } | | 246 | } |
247 | | | 247 | |
248 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); | | 248 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); |
249 | newf = futex_get(SCARG(uap, uaddr2), FUTEX_UNLOCKED); | | 249 | newf = futex_get(SCARG(uap, uaddr2), FUTEX_UNLOCKED); |
250 | *retval = futex_wake(f, SCARG(uap, val), newf, | | 250 | *retval = futex_wake(f, SCARG(uap, val), newf, |
251 | (int)(unsigned long)SCARG(uap, timeout)); | | 251 | (int)(unsigned long)SCARG(uap, timeout)); |
252 | futex_put(f); | | 252 | futex_put(f); |
253 | futex_put(newf); | | 253 | futex_put(newf); |
254 | | | 254 | |
255 | FUTEX_SYSTEM_UNLOCK; | | 255 | FUTEX_SYSTEM_UNLOCK; |
256 | break; | | 256 | break; |
257 | | | 257 | |
258 | case LINUX_FUTEX_REQUEUE: | | 258 | case LINUX_FUTEX_REQUEUE: |
259 | FUTEX_SYSTEM_LOCK; | | 259 | FUTEX_SYSTEM_LOCK; |
260 | | | 260 | |
261 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); | | 261 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); |
262 | newf = futex_get(SCARG(uap, uaddr2), FUTEX_UNLOCKED); | | 262 | newf = futex_get(SCARG(uap, uaddr2), FUTEX_UNLOCKED); |
263 | *retval = futex_wake(f, SCARG(uap, val), newf, | | 263 | *retval = futex_wake(f, SCARG(uap, val), newf, |
264 | (int)(unsigned long)SCARG(uap, timeout)); | | 264 | (int)(unsigned long)SCARG(uap, timeout)); |
265 | futex_put(f); | | 265 | futex_put(f); |
266 | futex_put(newf); | | 266 | futex_put(newf); |
267 | | | 267 | |
268 | FUTEX_SYSTEM_UNLOCK; | | 268 | FUTEX_SYSTEM_UNLOCK; |
269 | break; | | 269 | break; |
270 | | | 270 | |
271 | case LINUX_FUTEX_FD: | | 271 | case LINUX_FUTEX_FD: |
272 | FUTEXPRINTF(("linux_sys_futex: unimplemented op %d\n", | | 272 | FUTEXPRINTF(("linux_sys_futex: unimplemented op %d\n", |
273 | SCARG(uap, op))); | | 273 | SCARG(uap, op))); |
274 | return ENOSYS; | | 274 | return ENOSYS; |
275 | case LINUX_FUTEX_WAKE_OP: | | 275 | case LINUX_FUTEX_WAKE_OP: |
276 | FUTEX_SYSTEM_LOCK; | | 276 | FUTEX_SYSTEM_LOCK; |
277 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); | | 277 | f = futex_get(SCARG(uap, uaddr), FUTEX_UNLOCKED); |
278 | f2 = futex_get(SCARG(uap, uaddr2), FUTEX_UNLOCKED); | | 278 | f2 = futex_get(SCARG(uap, uaddr2), FUTEX_UNLOCKED); |
279 | /* | | 279 | /* |
280 | * This function returns positive number as results and | | 280 | * This function returns positive number as results and |
281 | * negative as errors | | 281 | * negative as errors |
282 | */ | | 282 | */ |
283 | op_ret = futex_atomic_op(l, SCARG(uap, val3), SCARG(uap, uaddr2)); | | 283 | op_ret = futex_atomic_op(l, SCARG(uap, val3), SCARG(uap, uaddr2)); |
284 | if (op_ret < 0) { | | 284 | if (op_ret < 0) { |
285 | /* XXX: We don't handle EFAULT yet */ | | 285 | /* XXX: We don't handle EFAULT yet */ |
286 | if (op_ret != -EFAULT) { | | 286 | if (op_ret != -EFAULT) { |
287 | futex_put(f); | | 287 | futex_put(f); |
288 | futex_put(f2); | | 288 | futex_put(f2); |
289 | FUTEX_SYSTEM_UNLOCK; | | 289 | FUTEX_SYSTEM_UNLOCK; |
290 | return -op_ret; | | 290 | return -op_ret; |
291 | } | | 291 | } |
292 | futex_put(f); | | 292 | futex_put(f); |
293 | futex_put(f2); | | 293 | futex_put(f2); |
294 | FUTEX_SYSTEM_UNLOCK; | | 294 | FUTEX_SYSTEM_UNLOCK; |
295 | return EFAULT; | | 295 | return EFAULT; |
296 | } | | 296 | } |
297 | | | 297 | |
298 | ret = futex_wake(f, SCARG(uap, val), NULL, 0); | | 298 | ret = futex_wake(f, SCARG(uap, val), NULL, 0); |
299 | futex_put(f); | | 299 | futex_put(f); |
300 | if (op_ret > 0) { | | 300 | if (op_ret > 0) { |
301 | op_ret = 0; | | 301 | op_ret = 0; |
302 | /* | | 302 | /* |
303 | * Linux abuses the address of the timespec parameter | | 303 | * Linux abuses the address of the timespec parameter |
304 | * as the number of retries | | 304 | * as the number of retries |
305 | */ | | 305 | */ |
306 | op_ret += futex_wake(f2, | | 306 | op_ret += futex_wake(f2, |
307 | (int)(unsigned long)SCARG(uap, timeout), NULL, 0); | | 307 | (int)(unsigned long)SCARG(uap, timeout), NULL, 0); |
308 | ret += op_ret; | | 308 | ret += op_ret; |
309 | } | | 309 | } |
310 | futex_put(f2); | | 310 | futex_put(f2); |
311 | *retval = ret; | | 311 | *retval = ret; |
312 | FUTEX_SYSTEM_UNLOCK; | | 312 | FUTEX_SYSTEM_UNLOCK; |
313 | break; | | 313 | break; |
314 | default: | | 314 | default: |
315 | FUTEXPRINTF(("linux_sys_futex: unknown op %d\n", | | 315 | FUTEXPRINTF(("linux_sys_futex: unknown op %d\n", |
316 | SCARG(uap, op))); | | 316 | SCARG(uap, op))); |
317 | return ENOSYS; | | 317 | return ENOSYS; |
318 | } | | 318 | } |
319 | return 0; | | 319 | return 0; |
320 | } | | 320 | } |
321 | | | 321 | |
322 | static struct futex * | | 322 | static struct futex * |
323 | futex_get(void *uaddr, int locked) | | 323 | futex_get(void *uaddr, int locked) |
324 | { | | 324 | { |
325 | struct futex *f; | | 325 | struct futex *f; |
326 | | | 326 | |
327 | if (locked == FUTEX_UNLOCKED) | | 327 | if (locked == FUTEX_UNLOCKED) |
328 | FUTEX_LOCK; | | 328 | FUTEX_LOCK; |
329 | | | 329 | |
330 | LIST_FOREACH(f, &futex_list, f_list) { | | 330 | LIST_FOREACH(f, &futex_list, f_list) { |
331 | if (f->f_uaddr == uaddr) { | | 331 | if (f->f_uaddr == uaddr) { |
332 | f->f_refcount++; | | 332 | f->f_refcount++; |
333 | if (locked == FUTEX_UNLOCKED) | | 333 | if (locked == FUTEX_UNLOCKED) |
334 | FUTEX_UNLOCK; | | 334 | FUTEX_UNLOCK; |
335 | return f; | | 335 | return f; |
336 | } | | 336 | } |
337 | } | | 337 | } |
338 | | | 338 | |
339 | /* Not found, create it */ | | 339 | /* Not found, create it */ |
340 | f = kmem_zalloc(sizeof(*f), KM_SLEEP); | | 340 | f = kmem_zalloc(sizeof(*f), KM_SLEEP); |
341 | f->f_uaddr = uaddr; | | 341 | f->f_uaddr = uaddr; |
342 | f->f_refcount = 1; | | 342 | f->f_refcount = 1; |
343 | TAILQ_INIT(&f->f_waiting_proc); | | 343 | TAILQ_INIT(&f->f_waiting_proc); |
344 | LIST_INSERT_HEAD(&futex_list, f, f_list); | | 344 | LIST_INSERT_HEAD(&futex_list, f, f_list); |
345 | if (locked == FUTEX_UNLOCKED) | | 345 | if (locked == FUTEX_UNLOCKED) |
346 | FUTEX_UNLOCK; | | 346 | FUTEX_UNLOCK; |
347 | | | 347 | |
348 | return f; | | 348 | return f; |
349 | } | | 349 | } |
350 | | | 350 | |
351 | static void | | 351 | static void |
352 | futex_put(struct futex *f) | | 352 | futex_put(struct futex *f) |
353 | { | | 353 | { |
354 | | | 354 | |
355 | FUTEX_LOCK; | | 355 | FUTEX_LOCK; |
356 | f->f_refcount--; | | 356 | f->f_refcount--; |
357 | if (f->f_refcount == 0) { | | 357 | if (f->f_refcount == 0) { |
358 | KASSERT(TAILQ_EMPTY(&f->f_waiting_proc)); | | 358 | KASSERT(TAILQ_EMPTY(&f->f_waiting_proc)); |
359 | LIST_REMOVE(f, f_list); | | 359 | LIST_REMOVE(f, f_list); |
360 | kmem_free(f, sizeof(*f)); | | 360 | kmem_free(f, sizeof(*f)); |
361 | } | | 361 | } |
362 | FUTEX_UNLOCK; | | 362 | FUTEX_UNLOCK; |
363 | | | 363 | |
364 | return; | | 364 | return; |
365 | } | | 365 | } |
366 | | | 366 | |
367 | static int | | 367 | static int |
368 | futex_sleep(struct futex *f, lwp_t *l, unsigned long timeout) | | 368 | futex_sleep(struct futex *f, lwp_t *l, unsigned long timeout) |
369 | { | | 369 | { |
370 | struct waiting_proc *wp; | | 370 | struct waiting_proc *wp; |
371 | int ret; | | 371 | int ret; |
372 | | | 372 | |
373 | wp = kmem_zalloc(sizeof(*wp), KM_SLEEP); | | 373 | wp = kmem_zalloc(sizeof(*wp), KM_SLEEP); |
374 | wp->wp_l = l; | | 374 | wp->wp_l = l; |
375 | wp->wp_new_futex = NULL; | | 375 | wp->wp_new_futex = NULL; |
376 | cv_init(&wp->wp_futex_cv, "futex"); | | 376 | cv_init(&wp->wp_futex_cv, "futex"); |
377 | | | 377 | |
378 | FUTEX_LOCK; | | 378 | FUTEX_LOCK; |
379 | TAILQ_INSERT_TAIL(&f->f_waiting_proc, wp, wp_list); | | 379 | TAILQ_INSERT_TAIL(&f->f_waiting_proc, wp, wp_list); |
380 | ret = cv_timedwait_sig(&wp->wp_futex_cv, &futex_lock, timeout); | | 380 | ret = cv_timedwait_sig(&wp->wp_futex_cv, &futex_lock, timeout); |
381 | TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list); | | 381 | TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list); |
382 | FUTEX_UNLOCK; | | 382 | FUTEX_UNLOCK; |
383 | | | 383 | |
384 | /* if we got woken up in futex_wake */ | | 384 | /* if we got woken up in futex_wake */ |
385 | if ((ret == 0) && (wp->wp_new_futex != NULL)) { | | 385 | if ((ret == 0) && (wp->wp_new_futex != NULL)) { |
386 | /* suspend us on the new futex */ | | 386 | /* suspend us on the new futex */ |
387 | ret = futex_sleep(wp->wp_new_futex, l, timeout); | | 387 | ret = futex_sleep(wp->wp_new_futex, l, timeout); |
388 | /* and release the old one */ | | 388 | /* and release the old one */ |
389 | futex_put(wp->wp_new_futex); | | 389 | futex_put(wp->wp_new_futex); |
390 | } | | 390 | } |
391 | | | 391 | |
392 | cv_destroy(&wp->wp_futex_cv); | | 392 | cv_destroy(&wp->wp_futex_cv); |
393 | kmem_free(wp, sizeof(*wp)); | | 393 | kmem_free(wp, sizeof(*wp)); |
394 | return ret; | | 394 | return ret; |
395 | } | | 395 | } |
396 | | | 396 | |
397 | static int | | 397 | static int |
398 | futex_wake(struct futex *f, int n, struct futex *newf, int n2) | | 398 | futex_wake(struct futex *f, int n, struct futex *newf, int n2) |
399 | { | | 399 | { |
400 | struct waiting_proc *wp; | | 400 | struct waiting_proc *wp; |
401 | int count; | | 401 | int count; |
402 | | | 402 | |
403 | count = newf ? 0 : 1; | | 403 | count = newf ? 0 : 1; |
404 | | | 404 | |
405 | FUTEX_LOCK; | | 405 | FUTEX_LOCK; |
406 | TAILQ_FOREACH(wp, &f->f_waiting_proc, wp_list) { | | 406 | TAILQ_FOREACH(wp, &f->f_waiting_proc, wp_list) { |
407 | if (count <= n) { | | 407 | if (count <= n) { |
408 | cv_signal(&wp->wp_futex_cv); | | 408 | cv_signal(&wp->wp_futex_cv); |
409 | count++; | | 409 | count++; |
410 | } else { | | 410 | } else { |
411 | if (newf == NULL) | | 411 | if (newf == NULL) |
412 | continue; | | 412 | continue; |
413 | /* futex_put called after tsleep */ | | 413 | /* futex_put called after tsleep */ |
414 | wp->wp_new_futex = futex_get(newf->f_uaddr, | | 414 | wp->wp_new_futex = futex_get(newf->f_uaddr, |
415 | FUTEX_LOCKED); | | 415 | FUTEX_LOCKED); |
416 | cv_signal(&wp->wp_futex_cv); | | 416 | cv_signal(&wp->wp_futex_cv); |
417 | if (count - n >= n2) | | 417 | if (count - n >= n2) |
418 | break; | | 418 | break; |
419 | } | | 419 | } |
420 | } | | 420 | } |
421 | FUTEX_UNLOCK; | | 421 | FUTEX_UNLOCK; |
422 | | | 422 | |
423 | return count; | | 423 | return count; |
424 | } | | 424 | } |
425 | | | 425 | |
426 | static int | | 426 | static int |
427 | futex_atomic_op(lwp_t *l, int encoded_op, void *uaddr) | | 427 | futex_atomic_op(lwp_t *l, int encoded_op, void *uaddr) |
428 | { | | 428 | { |
429 | const int op = (encoded_op >> 28) & 7; | | 429 | const int op = (encoded_op >> 28) & 7; |
430 | const int cmp = (encoded_op >> 24) & 15; | | 430 | const int cmp = (encoded_op >> 24) & 15; |
431 | const int cmparg = (encoded_op << 20) >> 20; | | 431 | const int cmparg = (encoded_op << 20) >> 20; |
432 | int oparg = (encoded_op << 8) >> 20; | | 432 | int oparg = (encoded_op << 8) >> 20; |
433 | int error, oldval, cval; | | 433 | int error, oldval, cval; |
434 | | | 434 | |
435 | if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) | | 435 | if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) |
436 | oparg = 1 << oparg; | | 436 | oparg = 1 << oparg; |
437 | | | 437 | |
438 | /* XXX: linux verifies access here and returns EFAULT */ | | 438 | /* XXX: linux verifies access here and returns EFAULT */ |
439 | | | 439 | |
440 | if (copyin(uaddr, &cval, sizeof(int)) != 0) | | 440 | if (copyin(uaddr, &cval, sizeof(int)) != 0) |
441 | return -EFAULT; | | 441 | return -EFAULT; |
442 | | | 442 | |
443 | for (;;) { | | 443 | for (;;) { |
444 | int nval; | | 444 | int nval; |
445 | | | 445 | |
446 | switch (op) { | | 446 | switch (op) { |
447 | case FUTEX_OP_SET: | | 447 | case FUTEX_OP_SET: |
448 | nval = oparg; | | 448 | nval = oparg; |
449 | break; | | 449 | break; |
450 | case FUTEX_OP_ADD: | | 450 | case FUTEX_OP_ADD: |
451 | nval = cval + oparg; | | 451 | nval = cval + oparg; |
452 | break; | | 452 | break; |
453 | case FUTEX_OP_OR: | | 453 | case FUTEX_OP_OR: |
454 | nval = cval | oparg; | | 454 | nval = cval | oparg; |
455 | break; | | 455 | break; |
456 | case FUTEX_OP_ANDN: | | 456 | case FUTEX_OP_ANDN: |
457 | nval = cval & ~oparg; | | 457 | nval = cval & ~oparg; |
458 | break; | | 458 | break; |
459 | case FUTEX_OP_XOR: | | 459 | case FUTEX_OP_XOR: |
460 | nval = cval ^ oparg; | | 460 | nval = cval ^ oparg; |
461 | break; | | 461 | break; |
462 | default: | | 462 | default: |
463 | return -ENOSYS; | | 463 | return -ENOSYS; |
464 | } | | 464 | } |
465 | | | 465 | |
466 | error = ucas_int(uaddr, cval, nval, &oldval); | | 466 | error = ucas_int(uaddr, cval, nval, &oldval); |
467 | if (oldval == cval || error) { | | 467 | if (oldval == cval || error) { |
468 | break; | | 468 | break; |
469 | } | | 469 | } |
470 | cval = oldval; | | 470 | cval = oldval; |
471 | } | | 471 | } |
472 | | | 472 | |
473 | if (error) | | 473 | if (error) |
474 | return -EFAULT; | | 474 | return -EFAULT; |
475 | | | 475 | |
476 | switch (cmp) { | | 476 | switch (cmp) { |
477 | case FUTEX_OP_CMP_EQ: | | 477 | case FUTEX_OP_CMP_EQ: |
478 | return (oldval == cmparg); | | 478 | return (oldval == cmparg); |
479 | case FUTEX_OP_CMP_NE: | | 479 | case FUTEX_OP_CMP_NE: |
480 | return (oldval != cmparg); | | 480 | return (oldval != cmparg); |
481 | case FUTEX_OP_CMP_LT: | | 481 | case FUTEX_OP_CMP_LT: |
482 | return (oldval < cmparg); | | 482 | return (oldval < cmparg); |
483 | case FUTEX_OP_CMP_GE: | | 483 | case FUTEX_OP_CMP_GE: |
484 | return (oldval >= cmparg); | | 484 | return (oldval >= cmparg); |
485 | case FUTEX_OP_CMP_LE: | | 485 | case FUTEX_OP_CMP_LE: |
486 | return (oldval <= cmparg); | | 486 | return (oldval <= cmparg); |
487 | case FUTEX_OP_CMP_GT: | | 487 | case FUTEX_OP_CMP_GT: |
488 | return (oldval > cmparg); | | 488 | return (oldval > cmparg); |
489 | default: | | 489 | default: |
490 | return -ENOSYS; | | 490 | return -ENOSYS; |
491 | } | | 491 | } |
492 | } | | 492 | } |
493 | | | 493 | |
494 | int | | 494 | int |
495 | linux_sys_set_robust_list(struct lwp *l, | | 495 | linux_sys_set_robust_list(struct lwp *l, |
496 | const struct linux_sys_set_robust_list_args *uap, register_t *retval) | | 496 | const struct linux_sys_set_robust_list_args *uap, register_t *retval) |
497 | { | | 497 | { |
498 | struct proc *p = l->l_proc; | | 498 | struct proc *p = l->l_proc; |
499 | struct linux_emuldata *led = p->p_emuldata; | | 499 | struct linux_emuldata *led = p->p_emuldata; |
500 | | | 500 | |
| | | 501 | if (SCARG(uap, len) != sizeof(*(led->robust_futexes))) |
| | | 502 | return EINVAL; |
501 | led->robust_futexes = SCARG(uap, head); | | 503 | led->robust_futexes = SCARG(uap, head); |
502 | *retval = 0; | | 504 | *retval = 0; |
503 | return 0; | | 505 | return 0; |
504 | } | | 506 | } |
505 | | | 507 | |
506 | int | | 508 | int |
507 | linux_sys_get_robust_list(struct lwp *l, | | 509 | linux_sys_get_robust_list(struct lwp *l, |
508 | const struct linux_sys_get_robust_list_args *uap, register_t *retval) | | 510 | const struct linux_sys_get_robust_list_args *uap, register_t *retval) |
509 | { | | 511 | { |
510 | struct linux_emuldata *led; | | 512 | struct linux_emuldata *led; |
511 | struct linux_robust_list_head *head; | | 513 | struct linux_robust_list_head **head; |
512 | size_t len = sizeof(struct linux_robust_list_head); | | 514 | size_t len = sizeof(*led->robust_futexes); |
513 | int error = 0; | | 515 | int error = 0; |
514 | | | 516 | |
515 | if (!SCARG(uap, pid)) { | | 517 | if (!SCARG(uap, pid)) { |
516 | led = l->l_proc->p_emuldata; | | 518 | led = l->l_proc->p_emuldata; |
517 | head = led->robust_futexes; | | 519 | head = &led->robust_futexes; |
518 | } else { | | 520 | } else { |
519 | struct proc *p; | | 521 | struct proc *p; |
520 | | | 522 | |
521 | mutex_enter(proc_lock); | | 523 | mutex_enter(proc_lock); |
522 | if ((p = p_find(SCARG(uap, pid), PFIND_LOCKED)) == NULL || | | 524 | if ((p = p_find(SCARG(uap, pid), PFIND_LOCKED)) == NULL || |
523 | p->p_emul != &emul_linux) { | | 525 | p->p_emul != &emul_linux) { |
524 | mutex_exit(proc_lock); | | 526 | mutex_exit(proc_lock); |
525 | return ESRCH; | | 527 | return ESRCH; |
526 | } | | 528 | } |
527 | led = p->p_emuldata; | | 529 | led = p->p_emuldata; |
528 | head = led->robust_futexes; | | 530 | head = &led->robust_futexes; |
529 | mutex_exit(proc_lock); | | 531 | mutex_exit(proc_lock); |
530 | } | | 532 | } |
531 | | | 533 | |
532 | error = copyout(&len, SCARG(uap, len), sizeof(size_t)); | | 534 | error = copyout(&len, SCARG(uap, len), sizeof(len)); |
533 | if (error) | | 535 | if (error) |
534 | return error; | | 536 | return error; |
535 | return copyout(head, SCARG(uap, head), | | 537 | return copyout(head, SCARG(uap, head), sizeof(*head)); |
536 | sizeof(struct linux_robust_list_head)); | | | |
537 | } | | 538 | } |
538 | | | 539 | |
539 | static int | | 540 | static int |
540 | handle_futex_death(void *uaddr, pid_t pid, int pi) | | 541 | handle_futex_death(void *uaddr, pid_t pid, int pi) |
541 | { | | 542 | { |
542 | int uval, nval, mval; | | 543 | int uval, nval, mval; |
543 | struct futex *f; | | 544 | struct futex *f; |
544 | | | 545 | |
545 | retry: | | 546 | retry: |
546 | if (copyin(uaddr, &uval, 4)) | | 547 | if (copyin(uaddr, &uval, 4)) |
547 | return EFAULT; | | 548 | return EFAULT; |
548 | | | 549 | |
549 | if ((uval & FUTEX_TID_MASK) == pid) { | | 550 | if ((uval & FUTEX_TID_MASK) == pid) { |
550 | mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; | | 551 | mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; |
551 | nval = atomic_cas_32(uaddr, uval, mval); | | 552 | nval = atomic_cas_32(uaddr, uval, mval); |
552 | | | 553 | |
553 | if (nval == -1) | | 554 | if (nval == -1) |
554 | return EFAULT; | | 555 | return EFAULT; |
555 | | | 556 | |
556 | if (nval != uval) | | 557 | if (nval != uval) |
557 | goto retry; | | 558 | goto retry; |
558 | | | 559 | |
559 | if (!pi && (uval & FUTEX_WAITERS)) { | | 560 | if (!pi && (uval & FUTEX_WAITERS)) { |
560 | f = futex_get(uaddr, FUTEX_UNLOCKED); | | 561 | f = futex_get(uaddr, FUTEX_UNLOCKED); |
561 | futex_wake(f, 1, NULL, 0); | | 562 | futex_wake(f, 1, NULL, 0); |
562 | } | | 563 | } |
563 | } | | 564 | } |
564 | | | 565 | |
565 | return 0; | | 566 | return 0; |
566 | } | | 567 | } |
567 | | | 568 | |
568 | static int | | 569 | static int |
569 | fetch_robust_entry(struct linux_robust_list **entry, | | 570 | fetch_robust_entry(struct linux_robust_list **entry, |
570 | struct linux_robust_list **head, int *pi) | | 571 | struct linux_robust_list **head, int *pi) |
571 | { | | 572 | { |
572 | unsigned long uentry; | | 573 | unsigned long uentry; |
573 | | | 574 | |
574 | if (copyin((const void *)head, &uentry, sizeof(unsigned long))) | | 575 | if (copyin((const void *)head, &uentry, sizeof(unsigned long))) |
575 | return EFAULT; | | 576 | return EFAULT; |
576 | | | 577 | |
577 | *entry = (void *)(uentry & ~1UL); | | 578 | *entry = (void *)(uentry & ~1UL); |
578 | *pi = uentry & 1; | | 579 | *pi = uentry & 1; |
579 | | | 580 | |
580 | return 0; | | 581 | return 0; |
581 | } | | 582 | } |
582 | | | 583 | |
583 | /* This walks the list of robust futexes, releasing them. */ | | 584 | /* This walks the list of robust futexes, releasing them. */ |
584 | void | | 585 | void |
585 | release_futexes(struct proc *p) | | 586 | release_futexes(struct proc *p) |
586 | { | | 587 | { |
587 | struct linux_robust_list_head *head = NULL; | | 588 | struct linux_robust_list_head head; |
588 | struct linux_robust_list *entry, *next_entry, *pending; | | 589 | struct linux_robust_list *entry, *next_entry, *pending; |
589 | unsigned int limit = 2048, pi, next_pi, pip; | | 590 | unsigned int limit = 2048, pi, next_pi, pip; |
590 | struct linux_emuldata *led; | | 591 | struct linux_emuldata *led; |
591 | unsigned long futex_offset; | | 592 | unsigned long futex_offset; |
592 | int rc; | | 593 | int rc; |
593 | | | 594 | |
594 | led = p->p_emuldata; | | 595 | led = p->p_emuldata; |
595 | head = led->robust_futexes; | | 596 | if (led->robust_futexes == NULL) |
| | | 597 | return; |
596 | | | 598 | |
597 | if (head == NULL) | | 599 | if (copyin(led->robust_futexes, &head, sizeof(head))) |
598 | return; | | 600 | return; |
599 | | | 601 | |
600 | if (fetch_robust_entry(&entry, &head->list.next, &pi)) | | 602 | if (fetch_robust_entry(&entry, &head.list.next, &pi)) |
601 | return; | | 603 | return; |
602 | | | 604 | |
603 | if (copyin(&head->futex_offset, &futex_offset, sizeof(unsigned long))) | | 605 | if (copyin(&head.futex_offset, &futex_offset, sizeof(unsigned long))) |
604 | return; | | 606 | return; |
605 | | | 607 | |
606 | if (fetch_robust_entry(&pending, &head->pending_list, &pip)) | | 608 | if (fetch_robust_entry(&pending, &head.pending_list, &pip)) |
607 | return; | | 609 | return; |
608 | | | 610 | |
609 | while (entry != &head->list) { | | 611 | while (entry != &head.list) { |
610 | rc = fetch_robust_entry(&next_entry, &entry->next, &next_pi); | | 612 | rc = fetch_robust_entry(&next_entry, &entry->next, &next_pi); |
611 | | | 613 | |
612 | if (entry != pending) | | 614 | if (entry != pending) |
613 | if (handle_futex_death((char *)entry + futex_offset, | | 615 | if (handle_futex_death((char *)entry + futex_offset, |
614 | p->p_pid, pi)) | | 616 | p->p_pid, pi)) |
615 | return; | | 617 | return; |
616 | | | 618 | |
617 | if (rc) | | 619 | if (rc) |
618 | return; | | 620 | return; |
619 | | | 621 | |
620 | entry = next_entry; | | 622 | entry = next_entry; |
621 | pi = next_pi; | | 623 | pi = next_pi; |
622 | | | 624 | |
623 | if (!--limit) | | 625 | if (!--limit) |
624 | break; | | 626 | break; |
625 | | | 627 | |
626 | yield(); /* XXX why? */ | | 628 | yield(); /* XXX why? */ |
627 | } | | 629 | } |
628 | | | 630 | |
629 | if (pending) | | 631 | if (pending) |
630 | handle_futex_death((char *)pending + futex_offset, | | 632 | handle_futex_death((char *)pending + futex_offset, |
631 | p->p_pid, pip); | | 633 | p->p_pid, pip); |
632 | } | | 634 | } |