1 | /* Copyright (C) 2002-2019 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <sysdep.h> |
20 | #include <pthread-errnos.h> |
21 | #include <kernel-features.h> |
22 | #include <lowlevellock.h> |
23 | |
24 | #include <stap-probe.h> |
25 | |
26 | .text |
27 | |
28 | #define LOAD_PRIVATE_FUTEX_WAIT(reg) \ |
29 | movl $(FUTEX_WAIT | FUTEX_PRIVATE_FLAG), reg |
30 | #define LOAD_PRIVATE_FUTEX_WAKE(reg) \ |
31 | movl $(FUTEX_WAKE | FUTEX_PRIVATE_FLAG), reg |
32 | #define LOAD_FUTEX_WAIT(reg) \ |
33 | xorl $(FUTEX_WAIT | FUTEX_PRIVATE_FLAG), reg |
34 | #define LOAD_FUTEX_WAIT_ABS(reg) \ |
35 | xorl $(FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME), reg |
36 | #define LOAD_FUTEX_WAKE(reg) \ |
37 | xorl $(FUTEX_WAKE | FUTEX_PRIVATE_FLAG), reg |
38 | |
39 | |
40 | .globl __lll_lock_wait_private |
41 | .type __lll_lock_wait_private,@function |
42 | .hidden __lll_lock_wait_private |
43 | .align 16 |
44 | __lll_lock_wait_private: |
45 | cfi_startproc |
46 | pushq %r10 |
47 | cfi_adjust_cfa_offset(8) |
48 | pushq %rdx |
49 | cfi_adjust_cfa_offset(8) |
50 | cfi_offset(%r10, -16) |
51 | cfi_offset(%rdx, -24) |
52 | xorq %r10, %r10 /* No timeout. */ |
53 | movl $2, %edx |
54 | LOAD_PRIVATE_FUTEX_WAIT (%esi) |
55 | |
56 | cmpl %edx, %eax /* NB: %edx == 2 */ |
57 | jne 2f |
58 | |
59 | 1: LIBC_PROBE (lll_lock_wait_private, 1, %rdi) |
60 | movl $SYS_futex, %eax |
61 | syscall |
62 | |
63 | 2: movl %edx, %eax |
64 | xchgl %eax, (%rdi) /* NB: lock is implied */ |
65 | |
66 | testl %eax, %eax |
67 | jnz 1b |
68 | |
69 | popq %rdx |
70 | cfi_adjust_cfa_offset(-8) |
71 | cfi_restore(%rdx) |
72 | popq %r10 |
73 | cfi_adjust_cfa_offset(-8) |
74 | cfi_restore(%r10) |
75 | retq |
76 | cfi_endproc |
77 | .size __lll_lock_wait_private,.-__lll_lock_wait_private |
78 | |
79 | #if !IS_IN (libc) |
80 | .globl __lll_lock_wait |
81 | .type __lll_lock_wait,@function |
82 | .hidden __lll_lock_wait |
83 | .align 16 |
84 | __lll_lock_wait: |
85 | cfi_startproc |
86 | pushq %r10 |
87 | cfi_adjust_cfa_offset(8) |
88 | pushq %rdx |
89 | cfi_adjust_cfa_offset(8) |
90 | cfi_offset(%r10, -16) |
91 | cfi_offset(%rdx, -24) |
92 | xorq %r10, %r10 /* No timeout. */ |
93 | movl $2, %edx |
94 | LOAD_FUTEX_WAIT (%esi) |
95 | |
96 | cmpl %edx, %eax /* NB: %edx == 2 */ |
97 | jne 2f |
98 | |
99 | 1: LIBC_PROBE (lll_lock_wait, 2, %rdi, %rsi) |
100 | movl $SYS_futex, %eax |
101 | syscall |
102 | |
103 | 2: movl %edx, %eax |
104 | xchgl %eax, (%rdi) /* NB: lock is implied */ |
105 | |
106 | testl %eax, %eax |
107 | jnz 1b |
108 | |
109 | popq %rdx |
110 | cfi_adjust_cfa_offset(-8) |
111 | cfi_restore(%rdx) |
112 | popq %r10 |
113 | cfi_adjust_cfa_offset(-8) |
114 | cfi_restore(%r10) |
115 | retq |
116 | cfi_endproc |
117 | .size __lll_lock_wait,.-__lll_lock_wait |
118 | |
119 | /* %rdi: futex |
120 | %rsi: flags |
121 | %rdx: timeout |
122 | %eax: futex value |
123 | */ |
124 | .globl __lll_timedlock_wait |
125 | .type __lll_timedlock_wait,@function |
126 | .hidden __lll_timedlock_wait |
127 | .align 16 |
128 | __lll_timedlock_wait: |
129 | cfi_startproc |
130 | # ifndef __ASSUME_FUTEX_CLOCK_REALTIME |
131 | # ifdef PIC |
132 | cmpl $0, __have_futex_clock_realtime(%rip) |
133 | # else |
134 | cmpl $0, __have_futex_clock_realtime |
135 | # endif |
136 | je .Lreltmo |
137 | # endif |
138 | |
139 | cmpq $0, (%rdx) |
140 | js 5f |
141 | |
142 | pushq %r9 |
143 | cfi_adjust_cfa_offset(8) |
144 | cfi_rel_offset(%r9, 0) |
145 | |
146 | movq %rdx, %r10 |
147 | movl $0xffffffff, %r9d |
148 | LOAD_FUTEX_WAIT_ABS (%esi) |
149 | |
150 | movl $2, %edx |
151 | cmpl %edx, %eax |
152 | jne 2f |
153 | |
154 | 1: movl $SYS_futex, %eax |
155 | movl $2, %edx |
156 | syscall |
157 | |
158 | 2: xchgl %edx, (%rdi) /* NB: lock is implied */ |
159 | |
160 | testl %edx, %edx |
161 | jz 3f |
162 | |
163 | cmpl $-ETIMEDOUT, %eax |
164 | je 4f |
165 | cmpl $-EINVAL, %eax |
166 | jne 1b |
167 | 4: movl %eax, %edx |
168 | negl %edx |
169 | |
170 | 3: movl %edx, %eax |
171 | popq %r9 |
172 | cfi_adjust_cfa_offset(-8) |
173 | cfi_restore(%r9) |
174 | retq |
175 | |
176 | 5: movl $ETIMEDOUT, %eax |
177 | retq |
178 | |
179 | # ifndef __ASSUME_FUTEX_CLOCK_REALTIME |
180 | .Lreltmo: |
181 | /* Check for a valid timeout value. */ |
182 | cmpq $1000000000, 8(%rdx) |
183 | jae 3f |
184 | |
185 | pushq %r8 |
186 | cfi_adjust_cfa_offset(8) |
187 | pushq %r9 |
188 | cfi_adjust_cfa_offset(8) |
189 | pushq %r12 |
190 | cfi_adjust_cfa_offset(8) |
191 | pushq %r13 |
192 | cfi_adjust_cfa_offset(8) |
193 | pushq %r14 |
194 | cfi_adjust_cfa_offset(8) |
195 | cfi_offset(%r8, -16) |
196 | cfi_offset(%r9, -24) |
197 | cfi_offset(%r12, -32) |
198 | cfi_offset(%r13, -40) |
199 | cfi_offset(%r14, -48) |
200 | pushq %rsi |
201 | cfi_adjust_cfa_offset(8) |
202 | |
203 | /* Stack frame for the timespec and timeval structs. */ |
204 | subq $24, %rsp |
205 | cfi_adjust_cfa_offset(24) |
206 | |
207 | movq %rdi, %r12 |
208 | movq %rdx, %r13 |
209 | |
210 | movl $2, %edx |
211 | xchgl %edx, (%r12) |
212 | |
213 | testl %edx, %edx |
214 | je 6f |
215 | |
216 | 1: |
217 | /* Get current time. */ |
218 | movq %rsp, %rdi |
219 | xorl %esi, %esi |
220 | /* This call works because we directly jump to a system call entry |
221 | which preserves all the registers. */ |
222 | call JUMPTARGET(__gettimeofday) |
223 | |
224 | /* Compute relative timeout. */ |
225 | movq 8(%rsp), %rax |
226 | movl $1000, %edi |
227 | mul %rdi /* Milli seconds to nano seconds. */ |
228 | movq (%r13), %rdi |
229 | movq 8(%r13), %rsi |
230 | subq (%rsp), %rdi |
231 | subq %rax, %rsi |
232 | jns 4f |
233 | addq $1000000000, %rsi |
234 | decq %rdi |
235 | 4: testq %rdi, %rdi |
236 | js 2f /* Time is already up. */ |
237 | |
238 | /* Store relative timeout. */ |
239 | movq %rdi, (%rsp) |
240 | movq %rsi, 8(%rsp) |
241 | |
242 | /* Futex call. */ |
243 | movl $2, %edx |
244 | movl $1, %eax |
245 | movq %rsp, %r10 |
246 | movl 24(%rsp), %esi |
247 | LOAD_FUTEX_WAIT (%esi) |
248 | movq %r12, %rdi |
249 | movl $SYS_futex, %eax |
250 | syscall |
251 | |
252 | /* NB: %edx == 2 */ |
253 | xchgl %edx, (%r12) |
254 | |
255 | testl %edx, %edx |
256 | je 6f |
257 | |
258 | cmpl $-ETIMEDOUT, %eax |
259 | jne 1b |
260 | 2: movl $ETIMEDOUT, %edx |
261 | |
262 | 6: addq $32, %rsp |
263 | cfi_adjust_cfa_offset(-32) |
264 | popq %r14 |
265 | cfi_adjust_cfa_offset(-8) |
266 | cfi_restore(%r14) |
267 | popq %r13 |
268 | cfi_adjust_cfa_offset(-8) |
269 | cfi_restore(%r13) |
270 | popq %r12 |
271 | cfi_adjust_cfa_offset(-8) |
272 | cfi_restore(%r12) |
273 | popq %r9 |
274 | cfi_adjust_cfa_offset(-8) |
275 | cfi_restore(%r9) |
276 | popq %r8 |
277 | cfi_adjust_cfa_offset(-8) |
278 | cfi_restore(%r8) |
279 | movl %edx, %eax |
280 | retq |
281 | |
282 | 3: movl $EINVAL, %eax |
283 | retq |
284 | # endif |
285 | cfi_endproc |
286 | .size __lll_timedlock_wait,.-__lll_timedlock_wait |
287 | #endif |
288 | |
289 | |
290 | .globl __lll_unlock_wake_private |
291 | .type __lll_unlock_wake_private,@function |
292 | .hidden __lll_unlock_wake_private |
293 | .align 16 |
294 | __lll_unlock_wake_private: |
295 | cfi_startproc |
296 | pushq %rsi |
297 | cfi_adjust_cfa_offset(8) |
298 | pushq %rdx |
299 | cfi_adjust_cfa_offset(8) |
300 | cfi_offset(%rsi, -16) |
301 | cfi_offset(%rdx, -24) |
302 | |
303 | movl $0, (%rdi) |
304 | LOAD_PRIVATE_FUTEX_WAKE (%esi) |
305 | movl $1, %edx /* Wake one thread. */ |
306 | movl $SYS_futex, %eax |
307 | syscall |
308 | |
309 | popq %rdx |
310 | cfi_adjust_cfa_offset(-8) |
311 | cfi_restore(%rdx) |
312 | popq %rsi |
313 | cfi_adjust_cfa_offset(-8) |
314 | cfi_restore(%rsi) |
315 | retq |
316 | cfi_endproc |
317 | .size __lll_unlock_wake_private,.-__lll_unlock_wake_private |
318 | |
319 | #if !IS_IN (libc) |
320 | .globl __lll_unlock_wake |
321 | .type __lll_unlock_wake,@function |
322 | .hidden __lll_unlock_wake |
323 | .align 16 |
324 | __lll_unlock_wake: |
325 | cfi_startproc |
326 | pushq %rsi |
327 | cfi_adjust_cfa_offset(8) |
328 | pushq %rdx |
329 | cfi_adjust_cfa_offset(8) |
330 | cfi_offset(%rsi, -16) |
331 | cfi_offset(%rdx, -24) |
332 | |
333 | movl $0, (%rdi) |
334 | LOAD_FUTEX_WAKE (%esi) |
335 | movl $1, %edx /* Wake one thread. */ |
336 | movl $SYS_futex, %eax |
337 | syscall |
338 | |
339 | popq %rdx |
340 | cfi_adjust_cfa_offset(-8) |
341 | cfi_restore(%rdx) |
342 | popq %rsi |
343 | cfi_adjust_cfa_offset(-8) |
344 | cfi_restore(%rsi) |
345 | retq |
346 | cfi_endproc |
347 | .size __lll_unlock_wake,.-__lll_unlock_wake |
348 | |
349 | .globl __lll_timedwait_tid |
350 | .type __lll_timedwait_tid,@function |
351 | .hidden __lll_timedwait_tid |
352 | .align 16 |
353 | __lll_timedwait_tid: |
354 | cfi_startproc |
355 | pushq %r12 |
356 | cfi_adjust_cfa_offset(8) |
357 | pushq %r13 |
358 | cfi_adjust_cfa_offset(8) |
359 | cfi_offset(%r12, -16) |
360 | cfi_offset(%r13, -24) |
361 | |
362 | movq %rdi, %r12 |
363 | movq %rsi, %r13 |
364 | |
365 | /* Align stack to 16 bytes when calling __gettimeofday. */ |
366 | subq $24, %rsp |
367 | cfi_adjust_cfa_offset(24) |
368 | |
369 | /* Get current time. */ |
370 | 2: movq %rsp, %rdi |
371 | xorl %esi, %esi |
372 | /* This call works because we directly jump to a system call entry |
373 | which preserves all the registers. */ |
374 | call JUMPTARGET(__gettimeofday) |
375 | |
376 | /* Compute relative timeout. */ |
377 | movq 8(%rsp), %rax |
378 | movl $1000, %edi |
379 | mul %rdi /* Milli seconds to nano seconds. */ |
380 | movq (%r13), %rdi |
381 | movq 8(%r13), %rsi |
382 | subq (%rsp), %rdi |
383 | subq %rax, %rsi |
384 | jns 5f |
385 | addq $1000000000, %rsi |
386 | decq %rdi |
387 | 5: testq %rdi, %rdi |
388 | js 6f /* Time is already up. */ |
389 | |
390 | movq %rdi, (%rsp) /* Store relative timeout. */ |
391 | movq %rsi, 8(%rsp) |
392 | |
393 | movl (%r12), %edx |
394 | testl %edx, %edx |
395 | jz 4f |
396 | |
397 | movq %rsp, %r10 |
398 | /* XXX The kernel so far uses global futex for the wakeup at |
399 | all times. */ |
400 | #if FUTEX_WAIT == 0 |
401 | xorl %esi, %esi |
402 | #else |
403 | movl $FUTEX_WAIT, %esi |
404 | #endif |
405 | movq %r12, %rdi |
406 | movl $SYS_futex, %eax |
407 | syscall |
408 | |
409 | cmpl $0, (%rdi) |
410 | jne 1f |
411 | 4: xorl %eax, %eax |
412 | |
413 | 8: addq $24, %rsp |
414 | cfi_adjust_cfa_offset(-24) |
415 | popq %r13 |
416 | cfi_adjust_cfa_offset(-8) |
417 | cfi_restore(%r13) |
418 | popq %r12 |
419 | cfi_adjust_cfa_offset(-8) |
420 | cfi_restore(%r12) |
421 | retq |
422 | |
423 | cfi_adjust_cfa_offset(32) |
424 | 1: cmpq $-ETIMEDOUT, %rax |
425 | jne 2b |
426 | |
427 | 6: movl $ETIMEDOUT, %eax |
428 | jmp 8b |
429 | cfi_endproc |
430 | .size __lll_timedwait_tid,.-__lll_timedwait_tid |
431 | #endif |
432 | |